mirror of
https://github.com/github/codeql.git
synced 2026-02-12 05:01:06 +01:00
Merge branch 'main' into java-mad-test
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>maven-add-exports-module-flags</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>maven-add-exports-module-flags</name>
|
||||
<description>Test case: Project using --add-exports. Autobuilder should detect this and use --source/--target.</description>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>11</java.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
<compilerArgs>
|
||||
<arg>--add-exports</arg>
|
||||
<arg>jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports</arg>
|
||||
<arg>jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports</arg>
|
||||
<arg>jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports</arg>
|
||||
<arg>jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
|
||||
</compilerArgs>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,3 @@
|
||||
pom.xml
|
||||
src/main/java/com/example/CompilerUser.java
|
||||
target/maven-archiver/pom.properties
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.example;
|
||||
|
||||
import com.sun.tools.javac.api.JavacTool;
|
||||
|
||||
/**
|
||||
* Simple class that uses JDK compiler internals.
|
||||
* This requires --add-exports flags to compile.
|
||||
*/
|
||||
public class CompilerUser {
|
||||
public static void main(String[] args) {
|
||||
// Use JavacTool from jdk.compiler module
|
||||
JavacTool tool = JavacTool.create();
|
||||
System.out.println("Compiler tool: " + tool.getClass().getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
def test(codeql, java, actions_toolchains_file):
|
||||
codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>maven-execution-specific-java-version</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>maven-execution-specific-java-version</name>
|
||||
<description>Test case: Project with execution-specific Java versions (Java 11 for main, Java 17 for test). Maven.java should detect the highest version (17) and use it for compilation.</description>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<executions>
|
||||
<!-- Compilation for src/main/java -->
|
||||
<execution>
|
||||
<id>default-compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<release>11</release> <!-- Java 11 for main -->
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<!-- Compilation for src/test/java -->
|
||||
<execution>
|
||||
<id>default-testCompile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals>
|
||||
<goal>testCompile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<release>17</release> <!-- Java 17 for test -->
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,4 @@
|
||||
pom.xml
|
||||
src/main/java/com/example/App.java
|
||||
src/test/java/com/example/AppTest.java
|
||||
target/maven-archiver/pom.properties
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.example;
|
||||
|
||||
public class App {
|
||||
public static void main(String[] args) {
|
||||
var message = "Hello World! Running on Java " + System.getProperty("java.version");
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return "Hello from App";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.example;
|
||||
|
||||
public class AppTest {
|
||||
public static void main(String[] args) {
|
||||
var text = """
|
||||
Hello
|
||||
World
|
||||
""";
|
||||
System.out.println(text.strip());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
def test(codeql, java, actions_toolchains_file):
|
||||
codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>maven-java16-with-higher-jdk</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>maven-java16-with-higher-jdk</name>
|
||||
<description>Test case: Java 16 target when only Java 17+ is available.</description>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>16</java.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<release>${java.version}</release>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,3 @@
|
||||
pom.xml
|
||||
src/main/java/com/example/App.java
|
||||
target/maven-archiver/pom.properties
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.example;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Simple class using Java 16 features (e.g.,records).
|
||||
*/
|
||||
public class App {
|
||||
public static void main(String[] args) {
|
||||
Person person = new Person("Bob", 42);
|
||||
System.out.println(person);
|
||||
}
|
||||
}
|
||||
|
||||
record Person(String name, int age) {}
|
||||
@@ -0,0 +1,2 @@
|
||||
def test(codeql, java, actions_toolchains_file):
|
||||
codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>maven-java8-java11-dependency</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>maven-java8-java11-dependency</name>
|
||||
<description>Test case: Java 8 project with dependency requiring Java 11+</description>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- TestNG 7.7.0 is compiled with Java 11 (class file version 55.0) -->
|
||||
<dependency>
|
||||
<groupId>org.testng</groupId>
|
||||
<artifactId>testng</artifactId>
|
||||
<version>7.7.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.1</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,4 @@
|
||||
pom.xml
|
||||
src/main/java/com/example/Calculator.java
|
||||
src/test/java/com/example/CalculatorTest.java
|
||||
target/maven-archiver/pom.properties
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.example;
|
||||
|
||||
public class Calculator {
|
||||
public int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
public int multiply(int a, int b) {
|
||||
return a * b;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.example;
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
/**
|
||||
* Test class using TestNG 7.7.0 which requires Java 11+.
|
||||
*/
|
||||
public class CalculatorTest {
|
||||
@Test
|
||||
public void testAdd() {
|
||||
Calculator calc = new Calculator();
|
||||
Assert.assertEquals(calc.add(2, 3), 5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiply() {
|
||||
Calculator calc = new Calculator();
|
||||
Assert.assertEquals(calc.multiply(3, 4), 12);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
def test(codeql, java, actions_toolchains_file):
|
||||
codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>maven-multimodule-test-java-version</artifactId>
|
||||
<version>1.0</version>
|
||||
</parent>
|
||||
<artifactId>main-module</artifactId>
|
||||
</project>
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.example;
|
||||
|
||||
public class App {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello World!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>maven-multimodule-test-java-version</artifactId>
|
||||
<version>1.0</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.release>17</maven.compiler.release>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<module>main-module</module>
|
||||
<module>test-module</module>
|
||||
</modules>
|
||||
</project>
|
||||
@@ -0,0 +1,7 @@
|
||||
main-module/pom.xml
|
||||
main-module/src/main/java/com/example/App.java
|
||||
main-module/target/maven-archiver/pom.properties
|
||||
pom.xml
|
||||
test-module/pom.xml
|
||||
test-module/src/main/java/com/example/tests/TestUtils.java
|
||||
test-module/target/maven-archiver/pom.properties
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>maven-multimodule-test-java-version</artifactId>
|
||||
<version>1.0</version>
|
||||
</parent>
|
||||
<artifactId>test-module</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.release>21</maven.compiler.release>
|
||||
</properties>
|
||||
</project>
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.example.tests;
|
||||
|
||||
// Requires Java 21: switch with pattern matching and guards
|
||||
public class TestUtils {
|
||||
public static String analyze(Object obj) {
|
||||
return switch (obj) {
|
||||
case String s when s.length() > 5 -> "long";
|
||||
case String s -> "short";
|
||||
default -> "other";
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
def test(codeql, java, actions_toolchains_file):
|
||||
codeql.database.create(_env={"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file)})
|
||||
@@ -1,3 +1,21 @@
|
||||
## 7.8.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 7.8.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 7.8.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The SSA interface has been updated and all classes and several predicates have been renamed. See the qldoc for more specific migration information.
|
||||
|
||||
## 7.7.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 7.7.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* A sanitizer has been added to `java/ssrf` to remove alerts when a regular expression check is used to verify that the value is safe.
|
||||
3
java/ql/lib/change-notes/released/7.7.4.md
Normal file
3
java/ql/lib/change-notes/released/7.7.4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 7.7.4
|
||||
|
||||
No user-facing changes.
|
||||
5
java/ql/lib/change-notes/released/7.8.0.md
Normal file
5
java/ql/lib/change-notes/released/7.8.0.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 7.8.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The SSA interface has been updated and all classes and several predicates have been renamed. See the qldoc for more specific migration information.
|
||||
3
java/ql/lib/change-notes/released/7.8.1.md
Normal file
3
java/ql/lib/change-notes/released/7.8.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 7.8.1
|
||||
|
||||
No user-facing changes.
|
||||
3
java/ql/lib/change-notes/released/7.8.2.md
Normal file
3
java/ql/lib/change-notes/released/7.8.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 7.8.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 7.7.3
|
||||
lastReleaseVersion: 7.8.2
|
||||
|
||||
@@ -13,6 +13,14 @@ extensions:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
data: []
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierModel
|
||||
data: []
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierGuardModel
|
||||
data: []
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
|
||||
@@ -50,6 +50,12 @@ extensions:
|
||||
- ["hudson", "FilePath", False, "readToString", "", "", "ReturnValue", "file", "manual"]
|
||||
- ["hudson", "Plugin", True, "configure", "", "", "Parameter", "remote", "manual"]
|
||||
- ["hudson", "Plugin", True, "newInstance", "", "", "Parameter", "remote", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierModel
|
||||
data:
|
||||
- ["hudson", "Util", True, "escape", "(String)", "", "ReturnValue", "html-injection", "manual"]
|
||||
# Not including xmlEscape because it only accounts for >, <, and &. It does not account for ", or ', which makes it an incomplete XSS sanitizer.
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
|
||||
@@ -162,3 +162,8 @@ extensions:
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["java.io", "FileInputStream", True, "FileInputStream", "", "", "Argument[this]", "file", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierModel
|
||||
data:
|
||||
- ["java.io", "File", True, "getName", "()", "", "ReturnValue", "path-injection", "manual"]
|
||||
|
||||
@@ -34,6 +34,11 @@ extensions:
|
||||
- ["java.net", "URLClassLoader", False, "URLClassLoader", "(URL[],ClassLoader)", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- ["java.net", "URLClassLoader", False, "URLClassLoader", "(URL[])", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- ["java.net", "PasswordAuthentication", False, "PasswordAuthentication", "(String,char[])", "", "Argument[0]", "credentials-username", "hq-generated"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierGuardModel
|
||||
data:
|
||||
- ["java.net", "URI", True, "isAbsolute", "()", "", "Argument[this]", "false", "request-forgery", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
|
||||
@@ -12,6 +12,11 @@ extensions:
|
||||
- ["java.util.regex", "Pattern", False, "split", "(CharSequence)", "", "Argument[this]", "regex-use[0]", "manual"]
|
||||
- ["java.util.regex", "Pattern", False, "split", "(CharSequence,int)", "", "Argument[this]", "regex-use[0]", "manual"]
|
||||
- ["java.util.regex", "Pattern", False, "splitAsStream", "(CharSequence)", "", "Argument[this]", "regex-use[0]", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierModel
|
||||
data:
|
||||
- ["java.util.regex", "Pattern", False, "quote", "(String)", "", "ReturnValue", "regex-use", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
|
||||
@@ -1,6 +1,42 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierGuardModel
|
||||
data:
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidCreditCard", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidDate", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidDirectoryPath", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidDouble", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidFileContent", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidFileName", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidInput", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidInteger", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidListItem", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidNumber", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidPrintable", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidRedirectLocation", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidSafeHTML", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidURI", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierModel
|
||||
data:
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidCreditCard", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidDate", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidDirectoryPath", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidDouble", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidFileContent", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidFileName", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidInput", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidInteger", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidListItem", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidNumber", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidPrintable", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidRedirectLocation", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidSafeHTML", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidURI", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["org.owasp.esapi", "Encoder", true, "encodeForHTML", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["org.owasp.esapi", "Encoder", true, "encodeForHTML", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-all
|
||||
version: 7.7.4-dev
|
||||
version: 7.8.3-dev
|
||||
groups: java
|
||||
dbscheme: config/semmlecode.dbscheme
|
||||
extractor: java
|
||||
|
||||
@@ -1808,6 +1808,52 @@ class VariableAssign extends VariableUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TVariableWrite =
|
||||
TParamInit(Parameter p) or
|
||||
TVarWriteExpr(VariableUpdate u)
|
||||
|
||||
/**
|
||||
* A write to a variable. This is either a local variable declaration,
|
||||
* including parameter declarations, or an update to a variable.
|
||||
*/
|
||||
class VariableWrite extends TVariableWrite {
|
||||
/** Gets the expression representing this write, if any. */
|
||||
Expr asExpr() { this = TVarWriteExpr(result) }
|
||||
|
||||
/**
|
||||
* Gets the expression with the value being written, if any.
|
||||
*
|
||||
* This can be the same expression as returned by `asExpr()`, which is the
|
||||
* case for, for example, `++x` and `x += e`. For simple assignments like
|
||||
* `x = e`, `asExpr()` gets the whole assignment expression while
|
||||
* `getValue()` gets the right-hand side `e`. Post-crement operations like
|
||||
* `x++` do not have an expression with the value being written.
|
||||
*/
|
||||
Expr getValue() {
|
||||
this.asExpr().(VariableAssign).getSource() = result or
|
||||
this.asExpr().(AssignOp) = result or
|
||||
this.asExpr().(PreIncExpr) = result or
|
||||
this.asExpr().(PreDecExpr) = result
|
||||
}
|
||||
|
||||
/** Holds if this write is an initialization of parameter `p`. */
|
||||
predicate isParameterInit(Parameter p) { this = TParamInit(p) }
|
||||
|
||||
/** Gets a textual representation of this write. */
|
||||
string toString() {
|
||||
exists(Parameter p | this = TParamInit(p) and result = p.toString())
|
||||
or
|
||||
result = this.asExpr().toString()
|
||||
}
|
||||
|
||||
/** Gets the location of this write. */
|
||||
Location getLocation() {
|
||||
exists(Parameter p | this = TParamInit(p) and result = p.getLocation())
|
||||
or
|
||||
result = this.asExpr().getLocation()
|
||||
}
|
||||
}
|
||||
|
||||
/** A type literal. For example, `String.class`. */
|
||||
class TypeLiteral extends Expr, @typeliteral {
|
||||
/** Gets the access to the type whose class is accessed. */
|
||||
|
||||
@@ -321,12 +321,7 @@ class WriteObjectMethod extends Method {
|
||||
class ReadObjectMethod extends Method {
|
||||
ReadObjectMethod() {
|
||||
this.getDeclaringType() instanceof TypeObjectInputStream and
|
||||
(
|
||||
this.hasName("readObject") or
|
||||
this.hasName("readObjectOverride") or
|
||||
this.hasName("readUnshared") or
|
||||
this.hasName("resolveObject")
|
||||
)
|
||||
this.hasName(["readObject", "readObjectOverride", "readUnshared", "resolveObject"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,12 +46,7 @@ private class SpecialClassInstanceExpr extends ClassInstanceExpr {
|
||||
}
|
||||
|
||||
predicate throwsNfe() {
|
||||
this.isStringConstructor("Byte") or
|
||||
this.isStringConstructor("Short") or
|
||||
this.isStringConstructor("Integer") or
|
||||
this.isStringConstructor("Long") or
|
||||
this.isStringConstructor("Float") or
|
||||
this.isStringConstructor("Double")
|
||||
this.isStringConstructor(["Byte", "Short", "Integer", "Long", "Float", "Double"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
private import internal.OverlayXml
|
||||
|
||||
/**
|
||||
* A local predicate that always holds for the overlay variant and
|
||||
@@ -18,7 +19,7 @@ predicate isOverlay() { databaseMetadata("isOverlay", "true") }
|
||||
overlay[local]
|
||||
string getRawFile(@locatable el) {
|
||||
exists(@location loc, @file file |
|
||||
(hasLocation(el, loc) or xmllocations(el, loc)) and
|
||||
hasLocation(el, loc) and
|
||||
locations_default(loc, file, _, _, _, _) and
|
||||
files(file, result)
|
||||
)
|
||||
@@ -102,31 +103,3 @@ private predicate discardBaseConfigLocatable(@configLocatable el) {
|
||||
// property files than those included in overlayChangedFiles.
|
||||
overlayConfigExtracted(baseConfigLocatable(el))
|
||||
}
|
||||
|
||||
/**
|
||||
* An `@xmllocatable` that should be discarded in the base variant if its file is
|
||||
* extracted in the overlay variant.
|
||||
*/
|
||||
overlay[local]
|
||||
abstract class DiscardableXmlLocatable extends @xmllocatable {
|
||||
/** Gets the raw file for an xmllocatable in base. */
|
||||
string getRawFileInBase() { not isOverlay() and result = getRawFile(this) }
|
||||
|
||||
/** Gets a textual representation of this discardable xmllocatable. */
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
overlay[local]
|
||||
private predicate overlayXmlExtracted(string file) {
|
||||
isOverlay() and
|
||||
exists(@xmllocatable el | not files(el, _) and not xmlNs(el, _, _, _) and file = getRawFile(el))
|
||||
}
|
||||
|
||||
overlay[discard_entity]
|
||||
private predicate discardXmlLocatable(@xmllocatable el) {
|
||||
overlayChangedFiles(el.(DiscardableXmlLocatable).getRawFileInBase())
|
||||
or
|
||||
// The XML extractor is currently not incremental and may extract more
|
||||
// XML files than those included in overlayChangedFiles.
|
||||
overlayXmlExtracted(el.(DiscardableXmlLocatable).getRawFileInBase())
|
||||
}
|
||||
|
||||
@@ -6,11 +6,12 @@ module;
|
||||
|
||||
import java
|
||||
private import codeql.controlflow.ControlFlowReachability
|
||||
private import semmle.code.java.dataflow.SSA as SSA
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.controlflow.Guards as Guards
|
||||
|
||||
private module ControlFlowInput implements InputSig<Location, ControlFlowNode, BasicBlock> {
|
||||
private import java as J
|
||||
import Ssa
|
||||
|
||||
AstNode getEnclosingAstNode(ControlFlowNode node) { node.getAstNode() = result }
|
||||
|
||||
@@ -27,23 +28,6 @@ private module ControlFlowInput implements InputSig<Location, ControlFlowNode, B
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
class SourceVariable = SSA::SsaSourceVariable;
|
||||
|
||||
class SsaDefinition = SSA::SsaVariable;
|
||||
|
||||
class SsaExplicitWrite extends SsaDefinition instanceof SSA::SsaExplicitUpdate {
|
||||
Expr getValue() {
|
||||
super.getDefiningExpr().(VariableAssign).getSource() = result or
|
||||
super.getDefiningExpr().(AssignOp) = result
|
||||
}
|
||||
}
|
||||
|
||||
class SsaPhiDefinition = SSA::SsaPhiNode;
|
||||
|
||||
class SsaUncertainWrite extends SsaDefinition instanceof SSA::SsaUncertainImplicitUpdate {
|
||||
SsaDefinition getPriorDefinition() { result = super.getPriorDef() }
|
||||
}
|
||||
|
||||
class GuardValue = Guards::GuardValue;
|
||||
|
||||
predicate ssaControlsBranchEdge(SsaDefinition def, BasicBlock bb1, BasicBlock bb2, GuardValue v) {
|
||||
|
||||
@@ -141,7 +141,7 @@ private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pre
|
||||
|
||||
private module GuardsInput implements SharedGuards::InputSig<Location, ControlFlowNode, BasicBlock> {
|
||||
private import java as J
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.dataflow.NullGuards as NullGuards
|
||||
|
||||
class NormalExitNode = ControlFlow::NormalExitNode;
|
||||
@@ -211,10 +211,10 @@ private module GuardsInput implements SharedGuards::InputSig<Location, ControlFl
|
||||
f.getInitializer() = NullGuards::baseNotNullExpr()
|
||||
)
|
||||
or
|
||||
exists(CatchClause cc, LocalVariableDeclExpr decl, BaseSsaUpdate v |
|
||||
exists(CatchClause cc, LocalVariableDeclExpr decl, Base::SsaExplicitWrite v |
|
||||
decl = cc.getVariable() and
|
||||
decl = v.getDefiningExpr() and
|
||||
this = v.getAUse()
|
||||
this = v.getARead()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -407,30 +407,8 @@ private module LogicInputCommon {
|
||||
}
|
||||
|
||||
private module LogicInput_v1 implements GuardsImpl::LogicInputSig {
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
|
||||
final private class FinalBaseSsaVariable = BaseSsaVariable;
|
||||
|
||||
class SsaDefinition extends FinalBaseSsaVariable {
|
||||
GuardsInput::Expr getARead() { result = this.getAUse() }
|
||||
}
|
||||
|
||||
class SsaExplicitWrite extends SsaDefinition instanceof BaseSsaUpdate {
|
||||
GuardsInput::Expr getValue() {
|
||||
super.getDefiningExpr().(VariableAssign).getSource() = result or
|
||||
super.getDefiningExpr().(AssignOp) = result
|
||||
}
|
||||
}
|
||||
|
||||
class SsaPhiDefinition extends SsaDefinition instanceof BaseSsaPhiNode {
|
||||
predicate hasInputFromBlock(SsaDefinition inp, BasicBlock bb) {
|
||||
super.hasInputFromBlock(inp, bb)
|
||||
}
|
||||
}
|
||||
|
||||
class SsaParameterInit extends SsaDefinition instanceof BaseSsaImplicitInit {
|
||||
Parameter getParameter() { super.isParameterDefinition(result) }
|
||||
}
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
import Base::Ssa
|
||||
|
||||
predicate additionalNullCheck = LogicInputCommon::additionalNullCheck/4;
|
||||
|
||||
@@ -438,30 +416,8 @@ private module LogicInput_v1 implements GuardsImpl::LogicInputSig {
|
||||
}
|
||||
|
||||
private module LogicInput_v2 implements GuardsImpl::LogicInputSig {
|
||||
private import semmle.code.java.dataflow.SSA as SSA
|
||||
|
||||
final private class FinalSsaVariable = SSA::SsaVariable;
|
||||
|
||||
class SsaDefinition extends FinalSsaVariable {
|
||||
GuardsInput::Expr getARead() { result = this.getAUse() }
|
||||
}
|
||||
|
||||
class SsaExplicitWrite extends SsaDefinition instanceof SSA::SsaExplicitUpdate {
|
||||
GuardsInput::Expr getValue() {
|
||||
super.getDefiningExpr().(VariableAssign).getSource() = result or
|
||||
super.getDefiningExpr().(AssignOp) = result
|
||||
}
|
||||
}
|
||||
|
||||
class SsaPhiDefinition extends SsaDefinition instanceof SSA::SsaPhiNode {
|
||||
predicate hasInputFromBlock(SsaDefinition inp, BasicBlock bb) {
|
||||
super.hasInputFromBlock(inp, bb)
|
||||
}
|
||||
}
|
||||
|
||||
class SsaParameterInit extends SsaDefinition instanceof SSA::SsaImplicitInit {
|
||||
Parameter getParameter() { super.isParameterDefinition(result) }
|
||||
}
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
import Ssa
|
||||
|
||||
predicate additionalNullCheck = LogicInputCommon::additionalNullCheck/4;
|
||||
|
||||
|
||||
@@ -34,8 +34,12 @@ predicate useUsePair(VarRead use1, VarRead use2) { adjacentUseUse+(use1, use2) }
|
||||
* Other paths may also exist, so the SSA variables in `def` and `use` can be different.
|
||||
*/
|
||||
predicate defUsePair(VariableUpdate def, VarRead use) {
|
||||
exists(SsaVariable v |
|
||||
v.getAUse() = use and v.getAnUltimateDefinition().(SsaExplicitUpdate).getDefiningExpr() = def
|
||||
exists(SsaDefinition v, SsaExplicitWrite write |
|
||||
v.getARead() = use and write.getDefiningExpr() = def
|
||||
|
|
||||
v.getAnUltimateDefinition() = write
|
||||
or
|
||||
v.(SsaCapturedDefinition).getAnUltimateCapturedDefinition() = write
|
||||
)
|
||||
}
|
||||
|
||||
@@ -46,7 +50,9 @@ predicate defUsePair(VariableUpdate def, VarRead use) {
|
||||
* Other paths may also exist, so the SSA variables can be different.
|
||||
*/
|
||||
predicate parameterDefUsePair(Parameter p, VarRead use) {
|
||||
exists(SsaVariable v |
|
||||
v.getAUse() = use and v.getAnUltimateDefinition().(SsaImplicitInit).isParameterDefinition(p)
|
||||
exists(SsaDefinition v, SsaParameterInit init | v.getARead() = use and init.getParameter() = p |
|
||||
v.getAnUltimateDefinition() = init
|
||||
or
|
||||
v.(SsaCapturedDefinition).getAnUltimateCapturedDefinition() = init
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,14 +91,60 @@ module;
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow::DataFlow
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import FlowSummary as FlowSummary
|
||||
private import internal.DataFlowPrivate
|
||||
private import internal.FlowSummaryImpl
|
||||
private import internal.FlowSummaryImpl::Public
|
||||
private import internal.FlowSummaryImpl::Private
|
||||
private import internal.FlowSummaryImpl::Private::External
|
||||
private import internal.ExternalFlowExtensions as Extensions
|
||||
private import internal.ExternalFlowExtensions::Extensions as Extensions
|
||||
private import codeql.mad.ModelValidation as SharedModelVal
|
||||
private import codeql.mad.static.ModelsAsData as SharedMaD
|
||||
|
||||
private module MadInput implements SharedMaD::InputSig {
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate additionalSourceModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind, string provenance, string model
|
||||
) {
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance,
|
||||
madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
predicate additionalSinkModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind, string provenance, string model
|
||||
) {
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance, madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate additionalSummaryModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance, string model
|
||||
) {
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.summaryModel(package, type, subtypes, name, signature, ext, input, output, kind,
|
||||
provenance, madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module MaD = SharedMaD::ModelsAsData<Extensions, MadInput>;
|
||||
|
||||
import MaD
|
||||
|
||||
/**
|
||||
* A class for activating additional model rows.
|
||||
@@ -146,60 +192,18 @@ abstract private class ActiveExperimentalModelsInternal extends string {
|
||||
|
||||
deprecated class ActiveExperimentalModels = ActiveExperimentalModelsInternal;
|
||||
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
) {
|
||||
(
|
||||
Extensions::sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance,
|
||||
madId)
|
||||
or
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance, madId)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
predicate sinkModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
) {
|
||||
(
|
||||
Extensions::sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance,
|
||||
madId)
|
||||
or
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance, madId)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate summaryModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
) {
|
||||
(
|
||||
Extensions::summaryModel(package, type, subtypes, name, signature, ext, input, output, kind,
|
||||
provenance, madId)
|
||||
or
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.summaryModel(package, type, subtypes, name, signature, ext, input, output, kind,
|
||||
provenance, madId)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given extension tuple `madId` should pretty-print as `model`.
|
||||
*
|
||||
* This predicate should only be used in tests.
|
||||
*/
|
||||
predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
|
||||
MaD::interpretModelForTest(madId, model)
|
||||
or
|
||||
exists(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind, string provenance
|
||||
|
|
||||
sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance, madId) or
|
||||
Extensions::experimentalSourceModel(package, type, subtypes, name, signature, ext, output, kind,
|
||||
provenance, _, madId)
|
||||
|
|
||||
@@ -212,7 +216,6 @@ predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind, string provenance
|
||||
|
|
||||
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance, madId) or
|
||||
Extensions::experimentalSinkModel(package, type, subtypes, name, signature, ext, input, kind,
|
||||
provenance, _, madId)
|
||||
|
|
||||
@@ -225,8 +228,6 @@ predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance
|
||||
|
|
||||
summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance,
|
||||
madId) or
|
||||
Extensions::experimentalSummaryModel(package, type, subtypes, name, signature, ext, input,
|
||||
output, kind, provenance, _, madId)
|
||||
|
|
||||
@@ -236,65 +237,6 @@ predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a neutral model exists for the given parameters. */
|
||||
predicate neutralModel = Extensions::neutralModel/6;
|
||||
|
||||
private predicate relevantPackage(string package) {
|
||||
sourceModel(package, _, _, _, _, _, _, _, _, _) or
|
||||
sinkModel(package, _, _, _, _, _, _, _, _, _) or
|
||||
summaryModel(package, _, _, _, _, _, _, _, _, _, _)
|
||||
}
|
||||
|
||||
private predicate packageLink(string shortpkg, string longpkg) {
|
||||
relevantPackage(shortpkg) and
|
||||
relevantPackage(longpkg) and
|
||||
longpkg.prefix(longpkg.indexOf(".")) = shortpkg
|
||||
}
|
||||
|
||||
private predicate canonicalPackage(string package) {
|
||||
relevantPackage(package) and not packageLink(_, package)
|
||||
}
|
||||
|
||||
private predicate canonicalPkgLink(string package, string subpkg) {
|
||||
canonicalPackage(package) and
|
||||
(subpkg = package or packageLink(package, subpkg))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if MaD framework coverage of `package` is `n` api endpoints of the
|
||||
* kind `(kind, part)`, and `pkgs` is the number of subpackages of `package`
|
||||
* which have MaD framework coverage (including `package` itself).
|
||||
*/
|
||||
predicate modelCoverage(string package, int pkgs, string kind, string part, int n) {
|
||||
pkgs = strictcount(string subpkg | canonicalPkgLink(package, subpkg)) and
|
||||
(
|
||||
part = "source" and
|
||||
n =
|
||||
strictcount(string subpkg, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string output, string provenance |
|
||||
canonicalPkgLink(package, subpkg) and
|
||||
sourceModel(subpkg, type, subtypes, name, signature, ext, output, kind, provenance, _)
|
||||
)
|
||||
or
|
||||
part = "sink" and
|
||||
n =
|
||||
strictcount(string subpkg, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string provenance |
|
||||
canonicalPkgLink(package, subpkg) and
|
||||
sinkModel(subpkg, type, subtypes, name, signature, ext, input, kind, provenance, _)
|
||||
)
|
||||
or
|
||||
part = "summary" and
|
||||
n =
|
||||
strictcount(string subpkg, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string output, string provenance |
|
||||
canonicalPkgLink(package, subpkg) and
|
||||
summaryModel(subpkg, type, subtypes, name, signature, ext, input, output, kind, provenance,
|
||||
_)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides a query predicate to check the MaD models for validation errors. */
|
||||
module ModelValidation {
|
||||
private import codeql.dataflow.internal.AccessPathSyntax as AccessPathSyntax
|
||||
@@ -303,7 +245,9 @@ module ModelValidation {
|
||||
summaryModel(_, _, _, _, _, _, path, _, _, _, _) or
|
||||
summaryModel(_, _, _, _, _, _, _, path, _, _, _) or
|
||||
sinkModel(_, _, _, _, _, _, path, _, _, _) or
|
||||
sourceModel(_, _, _, _, _, _, path, _, _, _)
|
||||
sourceModel(_, _, _, _, _, _, path, _, _, _) or
|
||||
barrierModel(_, _, _, _, _, _, path, _, _, _) or
|
||||
barrierGuardModel(_, _, _, _, _, _, path, _, _, _, _)
|
||||
}
|
||||
|
||||
private module MkAccessPath = AccessPathSyntax::AccessPath<getRelevantAccessPath/1>;
|
||||
@@ -316,6 +260,8 @@ module ModelValidation {
|
||||
exists(string pred, AccessPath input, AccessPathToken part |
|
||||
sinkModel(_, _, _, _, _, _, input, _, _, _) and pred = "sink"
|
||||
or
|
||||
barrierGuardModel(_, _, _, _, _, _, input, _, _, _, _) and pred = "barrier guard"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, input, _, _, _, _) and pred = "summary"
|
||||
|
|
||||
(
|
||||
@@ -338,6 +284,8 @@ module ModelValidation {
|
||||
exists(string pred, AccessPath output, AccessPathToken part |
|
||||
sourceModel(_, _, _, _, _, _, output, _, _, _) and pred = "source"
|
||||
or
|
||||
barrierModel(_, _, _, _, _, _, output, _, _, _) and pred = "barrier"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, _, output, _, _, _) and pred = "summary"
|
||||
|
|
||||
(
|
||||
@@ -355,7 +303,13 @@ module ModelValidation {
|
||||
private module KindValConfig implements SharedModelVal::KindValidationConfigSig {
|
||||
predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _, _) }
|
||||
|
||||
predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _, _) }
|
||||
predicate sinkKind(string kind) {
|
||||
sinkModel(_, _, _, _, _, _, _, kind, _, _)
|
||||
or
|
||||
barrierModel(_, _, _, _, _, _, _, kind, _, _)
|
||||
or
|
||||
barrierGuardModel(_, _, _, _, _, _, _, _, kind, _, _)
|
||||
}
|
||||
|
||||
predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _, _) }
|
||||
|
||||
@@ -373,6 +327,11 @@ module ModelValidation {
|
||||
or
|
||||
sinkModel(package, type, _, name, signature, ext, _, _, provenance, _) and pred = "sink"
|
||||
or
|
||||
barrierModel(package, type, _, name, signature, ext, _, _, provenance, _) and pred = "barrier"
|
||||
or
|
||||
barrierGuardModel(package, type, _, name, signature, ext, _, _, _, provenance, _) and
|
||||
pred = "barrier guard"
|
||||
or
|
||||
summaryModel(package, type, _, name, signature, ext, _, _, _, provenance, _) and
|
||||
pred = "summary"
|
||||
or
|
||||
@@ -398,6 +357,14 @@ module ModelValidation {
|
||||
invalidProvenance(provenance) and
|
||||
result = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
exists(string acceptingvalue |
|
||||
barrierGuardModel(_, _, _, _, _, _, _, acceptingvalue, _, _, _) and
|
||||
invalidAcceptingValue(acceptingvalue) and
|
||||
result =
|
||||
"Unrecognized accepting value description \"" + acceptingvalue +
|
||||
"\" in barrier guard model."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a MaD flow model appears to contain typos. */
|
||||
@@ -418,6 +385,10 @@ private predicate elementSpec(
|
||||
or
|
||||
sinkModel(package, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
or
|
||||
barrierModel(package, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
or
|
||||
barrierGuardModel(package, type, subtypes, name, signature, ext, _, _, _, _, _)
|
||||
or
|
||||
summaryModel(package, type, subtypes, name, signature, ext, _, _, _, _, _)
|
||||
or
|
||||
neutralModel(package, type, name, signature, _, _) and ext = "" and subtypes = true
|
||||
@@ -578,6 +549,53 @@ private module Cached {
|
||||
isSinkNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TKindModelPair =
|
||||
TMkPair(string kind, string model) { isBarrierGuardNode(_, _, kind, model) }
|
||||
|
||||
private GuardValue convertAcceptingValue(AcceptingValue av) {
|
||||
av.isTrue() and result.asBooleanValue() = true
|
||||
or
|
||||
av.isFalse() and result.asBooleanValue() = false
|
||||
or
|
||||
av.isNoException() and result.getDualValue().isThrowsException()
|
||||
or
|
||||
av.isZero() and result.asIntValue() = 0
|
||||
or
|
||||
av.isNotZero() and result.getDualValue().asIntValue() = 0
|
||||
or
|
||||
av.isNull() and result.isNullValue()
|
||||
or
|
||||
av.isNotNull() and result.isNonNullValue()
|
||||
}
|
||||
|
||||
private predicate barrierGuardChecks(Guard g, Expr e, GuardValue gv, TKindModelPair kmp) {
|
||||
exists(
|
||||
SourceSinkInterpretationInput::InterpretNode n, AcceptingValue acceptingvalue, string kind,
|
||||
string model
|
||||
|
|
||||
isBarrierGuardNode(n, acceptingvalue, kind, model) and
|
||||
n.asNode().asExpr() = e and
|
||||
kmp = TMkPair(kind, model) and
|
||||
gv = convertAcceptingValue(acceptingvalue)
|
||||
|
|
||||
g.(Call).getAnArgument() = e or g.(MethodCall).getQualifier() = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate barrierNode(Node node, string kind, string model) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isBarrierNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
or
|
||||
ParameterizedBarrierGuard<TKindModelPair, barrierGuardChecks/4>::getABarrierNode(TMkPair(kind,
|
||||
model)) = node
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -594,6 +612,12 @@ predicate sourceNode(Node node, string kind) { sourceNode(node, kind, _) }
|
||||
*/
|
||||
predicate sinkNode(Node node, string kind) { sinkNode(node, kind, _) }
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
predicate barrierNode(Node node, string kind) { barrierNode(node, kind, _) }
|
||||
|
||||
// adapter class for converting Mad summaries to `SummarizedCallable`s
|
||||
private class SummarizedCallableAdapter extends SummarizedCallable {
|
||||
SummarizedCallableAdapter() { summaryElement(this, _, _, _, _, _, _) }
|
||||
|
||||
@@ -26,9 +26,9 @@ Expr enumConstEquality(Expr e, boolean polarity, EnumConstant c) {
|
||||
}
|
||||
|
||||
/** Gets an instanceof expression of `v` with type `type` */
|
||||
InstanceOfExpr instanceofExpr(SsaVariable v, RefType type) {
|
||||
InstanceOfExpr instanceofExpr(SsaDefinition v, RefType type) {
|
||||
result.getCheckedType() = type and
|
||||
result.getExpr() = v.getAUse()
|
||||
result.getExpr() = v.getARead()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,8 +37,8 @@ InstanceOfExpr instanceofExpr(SsaVariable v, RefType type) {
|
||||
*
|
||||
* Note this includes Kotlin's `==` and `!=` operators, which are value-equality tests.
|
||||
*/
|
||||
EqualityTest varEqualityTestExpr(SsaVariable v1, SsaVariable v2, boolean isEqualExpr) {
|
||||
result.hasOperands(v1.getAUse(), v2.getAUse()) and
|
||||
EqualityTest varEqualityTestExpr(SsaDefinition v1, SsaDefinition v2, boolean isEqualExpr) {
|
||||
result.hasOperands(v1.getARead(), v2.getARead()) and
|
||||
isEqualExpr = result.polarity()
|
||||
}
|
||||
|
||||
@@ -91,37 +91,44 @@ Expr clearlyNotNullExpr(Expr reason) {
|
||||
(reason = r1 or reason = r2)
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, boolean branch, VarRead rval, Guard guard |
|
||||
exists(SsaDefinition v, boolean branch, VarRead rval, Guard guard |
|
||||
guard = directNullGuard(v, branch, false) and
|
||||
guard.controls(rval.getBasicBlock(), branch) and
|
||||
reason = guard and
|
||||
rval = v.getAUse() and
|
||||
rval = v.getARead() and
|
||||
result = rval and
|
||||
not result = baseNotNullExpr()
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v |
|
||||
exists(SsaDefinition v |
|
||||
clearlyNotNull(v, reason) and
|
||||
result = v.getAUse() and
|
||||
result = v.getARead() and
|
||||
not result = baseNotNullExpr()
|
||||
)
|
||||
or
|
||||
exists(Field f |
|
||||
result = f.getAnAccess() and
|
||||
f.isFinal() and
|
||||
f.getInitializer() = clearlyNotNullExpr(reason) and
|
||||
not result = baseNotNullExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `v` is an SSA variable that is provably not `null`. */
|
||||
predicate clearlyNotNull(SsaVariable v, Expr reason) {
|
||||
predicate clearlyNotNull(SsaDefinition v, Expr reason) {
|
||||
exists(Expr src |
|
||||
src = v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() and
|
||||
src = v.(SsaExplicitWrite).getValue() and
|
||||
src = clearlyNotNullExpr(reason)
|
||||
)
|
||||
or
|
||||
exists(CatchClause cc, LocalVariableDeclExpr decl |
|
||||
decl = cc.getVariable() and
|
||||
decl = v.(SsaExplicitUpdate).getDefiningExpr() and
|
||||
decl = v.(SsaExplicitWrite).getDefiningExpr() and
|
||||
reason = decl
|
||||
)
|
||||
or
|
||||
exists(SsaVariable captured |
|
||||
v.(SsaImplicitInit).captures(captured) and
|
||||
exists(SsaDefinition captured |
|
||||
v.(SsaCapturedDefinition).captures(captured) and
|
||||
clearlyNotNull(captured, reason)
|
||||
)
|
||||
or
|
||||
@@ -136,7 +143,7 @@ predicate clearlyNotNull(SsaVariable v, Expr reason) {
|
||||
Expr clearlyNotNullExpr() { result = clearlyNotNullExpr(_) }
|
||||
|
||||
/** Holds if `v` is an SSA variable that is provably not `null`. */
|
||||
predicate clearlyNotNull(SsaVariable v) { clearlyNotNull(v, _) }
|
||||
predicate clearlyNotNull(SsaDefinition v) { clearlyNotNull(v, _) }
|
||||
|
||||
/**
|
||||
* Holds if the evaluation of a call to `m` resulting in the value `branch`
|
||||
@@ -207,7 +214,7 @@ deprecated Expr basicOrCustomNullGuard(Expr e, boolean branch, boolean isnull) {
|
||||
* If `result` evaluates to `branch`, then `v` is guaranteed to be null if `isnull`
|
||||
* is true, and non-null if `isnull` is false.
|
||||
*/
|
||||
Expr directNullGuard(SsaVariable v, boolean branch, boolean isnull) {
|
||||
Expr directNullGuard(SsaDefinition v, boolean branch, boolean isnull) {
|
||||
result = basicNullGuard(sameValue(v, _), branch, isnull)
|
||||
}
|
||||
|
||||
@@ -219,7 +226,7 @@ Expr directNullGuard(SsaVariable v, boolean branch, boolean isnull) {
|
||||
* If `result` evaluates to `branch`, then `v` is guaranteed to be null if `isnull`
|
||||
* is true, and non-null if `isnull` is false.
|
||||
*/
|
||||
deprecated Guard nullGuard(SsaVariable v, boolean branch, boolean isnull) {
|
||||
deprecated Guard nullGuard(SsaDefinition v, boolean branch, boolean isnull) {
|
||||
result = directNullGuard(v, branch, isnull)
|
||||
}
|
||||
|
||||
@@ -228,7 +235,9 @@ deprecated Guard nullGuard(SsaVariable v, boolean branch, boolean isnull) {
|
||||
* from `bb1` to `bb2` implies that `v` is guaranteed to be null if `isnull` is
|
||||
* true, and non-null if `isnull` is false.
|
||||
*/
|
||||
predicate nullGuardControlsBranchEdge(SsaVariable v, boolean isnull, BasicBlock bb1, BasicBlock bb2) {
|
||||
predicate nullGuardControlsBranchEdge(
|
||||
SsaDefinition v, boolean isnull, BasicBlock bb1, BasicBlock bb2
|
||||
) {
|
||||
exists(GuardValue gv |
|
||||
Guards_v3::ssaControlsBranchEdge(v, bb1, bb2, gv) and
|
||||
gv.isNullness(isnull)
|
||||
@@ -240,7 +249,7 @@ predicate nullGuardControlsBranchEdge(SsaVariable v, boolean isnull, BasicBlock
|
||||
* `bb` `v` is guaranteed to be null if `isnull` is true, and non-null if
|
||||
* `isnull` is false.
|
||||
*/
|
||||
predicate nullGuardControls(SsaVariable v, boolean isnull, BasicBlock bb) {
|
||||
predicate nullGuardControls(SsaDefinition v, boolean isnull, BasicBlock bb) {
|
||||
exists(GuardValue gv |
|
||||
Guards_v3::ssaControls(v, bb, gv) and
|
||||
gv.isNullness(isnull)
|
||||
@@ -263,6 +272,6 @@ predicate guardSuggestsExprMaybeNull(Expr guard, Expr e) {
|
||||
/**
|
||||
* Holds if `guard` is a guard expression that suggests that `v` might be null.
|
||||
*/
|
||||
predicate guardSuggestsVarMaybeNull(Expr guard, SsaVariable v) {
|
||||
predicate guardSuggestsVarMaybeNull(Expr guard, SsaDefinition v) {
|
||||
guardSuggestsExprMaybeNull(guard, sameValue(v, _))
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ predicate dereference(Expr e) {
|
||||
*
|
||||
* The `VarAccess` is included for nicer error reporting.
|
||||
*/
|
||||
private ControlFlowNode varDereference(SsaVariable v, VarAccess va) {
|
||||
private ControlFlowNode varDereference(SsaDefinition v, VarAccess va) {
|
||||
dereference(result.asExpr()) and
|
||||
result.asExpr() = sameValue(v, va)
|
||||
}
|
||||
@@ -121,7 +121,7 @@ private ControlFlowNode varDereference(SsaVariable v, VarAccess va) {
|
||||
/**
|
||||
* The first dereference of a variable in a given `BasicBlock`.
|
||||
*/
|
||||
private predicate firstVarDereferenceInBlock(BasicBlock bb, SsaVariable v, VarAccess va) {
|
||||
private predicate firstVarDereferenceInBlock(BasicBlock bb, SsaDefinition v, VarAccess va) {
|
||||
exists(ControlFlowNode n |
|
||||
varDereference(v, va) = n and
|
||||
n.getBasicBlock() = bb and
|
||||
@@ -135,14 +135,14 @@ private predicate firstVarDereferenceInBlock(BasicBlock bb, SsaVariable v, VarAc
|
||||
}
|
||||
|
||||
/** A variable suspected of being `null`. */
|
||||
private predicate varMaybeNull(SsaVariable v, ControlFlowNode node, string msg, Expr reason) {
|
||||
private predicate varMaybeNull(SsaDefinition v, ControlFlowNode node, string msg, Expr reason) {
|
||||
// A variable compared to null might be null.
|
||||
exists(Expr e |
|
||||
reason = e and
|
||||
msg = "as suggested by $@ null guard" and
|
||||
guardSuggestsVarMaybeNull(e, v) and
|
||||
node = v.getCfgNode() and
|
||||
not v instanceof SsaPhiNode and
|
||||
node = v.getControlFlowNode() and
|
||||
not v instanceof SsaPhiDefinition and
|
||||
not clearlyNotNull(v) and
|
||||
// Comparisons in finally blocks are excluded since missing exception edges in the CFG could otherwise yield FPs.
|
||||
not exists(TryStmt try | try.getFinally() = e.getEnclosingStmt().getEnclosingStmt*()) and
|
||||
@@ -151,13 +151,13 @@ private predicate varMaybeNull(SsaVariable v, ControlFlowNode node, string msg,
|
||||
not exists(MethodCall ma | ma.getAnArgument().getAChildExpr*() = e)
|
||||
) and
|
||||
// Don't use a guard as reason if there is a null assignment.
|
||||
not v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = nullExpr()
|
||||
not v.(SsaExplicitWrite).getDefiningExpr().(VariableAssign).getSource() = nullExpr()
|
||||
)
|
||||
or
|
||||
// A parameter might be null if there is a null argument somewhere.
|
||||
exists(Parameter p, Expr arg |
|
||||
v.(SsaImplicitInit).isParameterDefinition(p) and
|
||||
node = v.getCfgNode() and
|
||||
v.(SsaParameterInit).getParameter() = p and
|
||||
node = v.getControlFlowNode() and
|
||||
p.getAnArgument() = arg and
|
||||
reason = arg and
|
||||
msg = "because of $@ null argument" and
|
||||
@@ -167,7 +167,7 @@ private predicate varMaybeNull(SsaVariable v, ControlFlowNode node, string msg,
|
||||
or
|
||||
// If the source of a variable is null then the variable may be null.
|
||||
exists(VariableAssign def |
|
||||
v.(SsaExplicitUpdate).getDefiningExpr() = def and
|
||||
v.(SsaExplicitWrite).getDefiningExpr() = def and
|
||||
def.getSource() = nullExpr(node.asExpr()) and
|
||||
reason = def and
|
||||
msg = "because of $@ assignment"
|
||||
@@ -179,26 +179,26 @@ private Expr nonEmptyExpr() {
|
||||
// An array creation with a known positive size is trivially non-empty.
|
||||
result.(ArrayCreationExpr).getFirstDimensionSize() > 0
|
||||
or
|
||||
exists(SsaVariable v |
|
||||
exists(SsaDefinition v |
|
||||
// A use of an array variable is non-empty if...
|
||||
result = v.getAUse() and
|
||||
result = v.getARead() and
|
||||
v.getSourceVariable().getType() instanceof Array
|
||||
|
|
||||
// ...its definition is non-empty...
|
||||
v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = nonEmptyExpr()
|
||||
v.(SsaExplicitWrite).getValue() = nonEmptyExpr()
|
||||
or
|
||||
// ...or it is guarded by a condition proving its length to be non-zero.
|
||||
exists(ConditionBlock cond, boolean branch, FieldAccess length |
|
||||
cond.controls(result.getBasicBlock(), branch) and
|
||||
cond.getCondition() = nonZeroGuard(length, branch) and
|
||||
length.getField().hasName("length") and
|
||||
length.getQualifier() = v.getAUse()
|
||||
length.getQualifier() = v.getARead()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v |
|
||||
exists(SsaDefinition v |
|
||||
// A use of a Collection variable is non-empty if...
|
||||
result = v.getAUse() and
|
||||
result = v.getARead() and
|
||||
v.getSourceVariable().getType() instanceof CollectionType and
|
||||
exists(ConditionBlock cond, boolean branch, Expr c |
|
||||
// ...it is guarded by a condition...
|
||||
@@ -216,13 +216,13 @@ private Expr nonEmptyExpr() {
|
||||
// ...and the condition proves that it is non-empty, either by using the `isEmpty` method...
|
||||
c.(MethodCall).getMethod().hasName("isEmpty") and
|
||||
branch = false and
|
||||
c.(MethodCall).getQualifier() = v.getAUse()
|
||||
c.(MethodCall).getQualifier() = v.getARead()
|
||||
or
|
||||
// ...or a check on its `size`.
|
||||
exists(MethodCall size |
|
||||
c = nonZeroGuard(size, branch) and
|
||||
size.getMethod().hasName("size") and
|
||||
size.getQualifier() = v.getAUse()
|
||||
size.getQualifier() = v.getARead()
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -249,9 +249,9 @@ private predicate impossibleEdge(BasicBlock bb1, BasicBlock bb2) {
|
||||
}
|
||||
|
||||
private module NullnessConfig implements ControlFlowReachability::ConfigSig {
|
||||
predicate source(ControlFlowNode node, SsaVariable def) { varMaybeNull(def, node, _, _) }
|
||||
predicate source(ControlFlowNode node, SsaDefinition def) { varMaybeNull(def, node, _, _) }
|
||||
|
||||
predicate sink(ControlFlowNode node, SsaVariable def) { varDereference(def, _) = node }
|
||||
predicate sink(ControlFlowNode node, SsaDefinition def) { varDereference(def, _) = node }
|
||||
|
||||
predicate barrierValue(GuardValue gv) { gv.isNullness(false) }
|
||||
|
||||
@@ -266,7 +266,7 @@ private module NullnessFlow = ControlFlowReachability::Flow<NullnessConfig>;
|
||||
* Holds if the dereference of `v` at `va` might be `null`.
|
||||
*/
|
||||
predicate nullDeref(SsaSourceVariable v, VarAccess va, string msg, Expr reason) {
|
||||
exists(SsaVariable origin, SsaVariable ssa, ControlFlowNode src, ControlFlowNode sink |
|
||||
exists(SsaDefinition origin, SsaDefinition ssa, ControlFlowNode src, ControlFlowNode sink |
|
||||
varMaybeNull(origin, src, msg, reason) and
|
||||
NullnessFlow::flow(src, origin, sink, ssa) and
|
||||
ssa.getSourceVariable() = v and
|
||||
@@ -278,9 +278,9 @@ predicate nullDeref(SsaSourceVariable v, VarAccess va, string msg, Expr reason)
|
||||
* A dereference of a variable that is always `null`.
|
||||
*/
|
||||
predicate alwaysNullDeref(SsaSourceVariable v, VarAccess va) {
|
||||
exists(BasicBlock bb, SsaVariable ssa |
|
||||
forall(SsaVariable def | def = ssa.getAnUltimateDefinition() |
|
||||
def.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = alwaysNullExpr()
|
||||
exists(BasicBlock bb, SsaDefinition ssa |
|
||||
forall(SsaDefinition def | def = ssa.getAnUltimateDefinition() |
|
||||
def.(SsaExplicitWrite).getValue() = alwaysNullExpr()
|
||||
)
|
||||
or
|
||||
nullGuardControls(ssa, true, bb) and
|
||||
|
||||
@@ -242,17 +242,17 @@ module Sem implements Semantic<Location> {
|
||||
|
||||
Type getSsaType(SsaVariable var) { result = var.getSourceVariable().getType() }
|
||||
|
||||
final private class FinalSsaVariable = SSA::SsaVariable;
|
||||
final private class FinalSsaVariable = SSA::SsaDefinition;
|
||||
|
||||
class SsaVariable extends FinalSsaVariable {
|
||||
Expr getAUse() { result = super.getAUse() }
|
||||
Expr getAUse() { result = super.getARead() }
|
||||
}
|
||||
|
||||
class SsaPhiNode extends SsaVariable instanceof SSA::SsaPhiNode {
|
||||
class SsaPhiNode extends SsaVariable instanceof SSA::SsaPhiDefinition {
|
||||
predicate hasInputFromBlock(SsaVariable inp, BasicBlock bb) { super.hasInputFromBlock(inp, bb) }
|
||||
}
|
||||
|
||||
class SsaExplicitUpdate extends SsaVariable instanceof SSA::SsaExplicitUpdate {
|
||||
class SsaExplicitUpdate extends SsaVariable instanceof SSA::SsaExplicitWrite {
|
||||
Expr getDefiningExpr() { result = super.getDefiningExpr() }
|
||||
}
|
||||
|
||||
|
||||
@@ -30,17 +30,17 @@ predicate eqFlowCond = U::eqFlowCond/5;
|
||||
* only other input to `phi` is a `null` value.
|
||||
*
|
||||
* Note that the declared type of `phi` is `SsaVariable` instead of
|
||||
* `SsaPhiNode` in order for the reflexive case of `nonNullSsaFwdStep*(..)` to
|
||||
* have non-`SsaPhiNode` results.
|
||||
* `SsaPhiDefinition` in order for the reflexive case of `nonNullSsaFwdStep*(..)` to
|
||||
* have non-`SsaPhiDefinition` results.
|
||||
*/
|
||||
private predicate nonNullSsaFwdStep(SsaVariable v, SsaVariable phi) {
|
||||
exists(SsaExplicitUpdate vnull, SsaPhiNode phi0 | phi0 = phi |
|
||||
2 = strictcount(phi0.getAPhiInput()) and
|
||||
vnull = phi0.getAPhiInput() and
|
||||
v = phi0.getAPhiInput() and
|
||||
private predicate nonNullSsaFwdStep(SsaDefinition v, SsaDefinition phi) {
|
||||
exists(SsaExplicitWrite vnull, SsaPhiDefinition phi0 | phi0 = phi |
|
||||
2 = strictcount(phi0.getAnInput()) and
|
||||
vnull = phi0.getAnInput() and
|
||||
v = phi0.getAnInput() and
|
||||
not backEdge(phi0, v, _) and
|
||||
vnull != v and
|
||||
vnull.getDefiningExpr().(VariableAssign).getSource() instanceof NullLiteral
|
||||
vnull.getValue() instanceof NullLiteral
|
||||
)
|
||||
}
|
||||
|
||||
@@ -56,13 +56,13 @@ private predicate nonNullDefStep(Expr e1, Expr e2) {
|
||||
* explicit `ArrayCreationExpr` definition and that the definition does not go
|
||||
* through a back edge.
|
||||
*/
|
||||
ArrayCreationExpr getArrayDef(SsaVariable v) {
|
||||
ArrayCreationExpr getArrayDef(SsaDefinition v) {
|
||||
exists(Expr src |
|
||||
v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() = src and
|
||||
v.(SsaExplicitWrite).getValue() = src and
|
||||
nonNullDefStep*(result, src)
|
||||
)
|
||||
or
|
||||
exists(SsaVariable mid |
|
||||
exists(SsaDefinition mid |
|
||||
result = getArrayDef(mid) and
|
||||
nonNullSsaFwdStep(mid, v)
|
||||
)
|
||||
@@ -74,9 +74,9 @@ ArrayCreationExpr getArrayDef(SsaVariable v) {
|
||||
* `arrlen` without going through a back edge.
|
||||
*/
|
||||
private predicate arrayLengthDef(FieldRead arrlen, ArrayCreationExpr def) {
|
||||
exists(SsaVariable arr |
|
||||
exists(SsaDefinition arr |
|
||||
arrlen.getField() instanceof ArrayLengthField and
|
||||
arrlen.getQualifier() = arr.getAUse() and
|
||||
arrlen.getQualifier() = arr.getARead() and
|
||||
def = getArrayDef(arr)
|
||||
)
|
||||
}
|
||||
@@ -86,9 +86,11 @@ pragma[nomagic]
|
||||
private predicate constantIntegerExpr(Expr e, int val) {
|
||||
e.(CompileTimeConstantExpr).getIntValue() = val
|
||||
or
|
||||
exists(SsaExplicitUpdate v, Expr src |
|
||||
e = v.getAUse() and
|
||||
src = v.getDefiningExpr().(VariableAssign).getSource() and
|
||||
e.(LongLiteral).getValue().toInt() = val
|
||||
or
|
||||
exists(SsaExplicitWrite v, Expr src |
|
||||
e = v.getARead() and
|
||||
src = v.getValue() and
|
||||
constantIntegerExpr(src, val)
|
||||
)
|
||||
or
|
||||
@@ -112,9 +114,9 @@ pragma[nomagic]
|
||||
private predicate constantBooleanExpr(Expr e, boolean val) {
|
||||
e.(CompileTimeConstantExpr).getBooleanValue() = val
|
||||
or
|
||||
exists(SsaExplicitUpdate v, Expr src |
|
||||
e = v.getAUse() and
|
||||
src = v.getDefiningExpr().(VariableAssign).getSource() and
|
||||
exists(SsaExplicitWrite v, Expr src |
|
||||
e = v.getARead() and
|
||||
src = v.getValue() and
|
||||
constantBooleanExpr(src, val)
|
||||
)
|
||||
or
|
||||
@@ -125,9 +127,9 @@ pragma[nomagic]
|
||||
private predicate constantStringExpr(Expr e, string val) {
|
||||
e.(CompileTimeConstantExpr).getStringValue() = val
|
||||
or
|
||||
exists(SsaExplicitUpdate v, Expr src |
|
||||
e = v.getAUse() and
|
||||
src = v.getDefiningExpr().(VariableAssign).getSource() and
|
||||
exists(SsaExplicitWrite v, Expr src |
|
||||
e = v.getARead() and
|
||||
src = v.getValue() and
|
||||
constantStringExpr(src, val)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,16 +5,16 @@
|
||||
* `ControlFlowNode` at which it is defined. Each SSA variable is defined
|
||||
* either by a phi node, an implicit initial value (for parameters and fields),
|
||||
* an explicit update, or an implicit update (for fields).
|
||||
* An implicit update occurs either at a `Call` that might modify a field, at
|
||||
* another update that can update the qualifier of a field, or at a `FieldRead`
|
||||
* of the field in case the field is not amenable to a non-trivial SSA
|
||||
* representation.
|
||||
* An implicit update occurs either at a `Call` that might modify a field, or
|
||||
* at another update that can update the qualifier of a field.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
private import internal.SsaImpl
|
||||
import internal.SsaImpl::Ssa as Ssa
|
||||
import Ssa
|
||||
|
||||
/**
|
||||
* A fully qualified variable in the context of a `Callable` in which it is
|
||||
@@ -105,7 +105,7 @@ class SsaSourceVariable extends TSsaSourceVariable {
|
||||
SsaSourceVariable getQualifier() { this = TQualifiedField(_, result, _) }
|
||||
|
||||
/** Gets an SSA variable that has this variable as its underlying source variable. */
|
||||
SsaVariable getAnSsaVariable() { result.getSourceVariable() = this }
|
||||
SsaDefinition getAnSsaVariable() { result.getSourceVariable() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,22 +138,76 @@ class SsaSourceField extends SsaSourceVariable {
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA definition in a closure that captures a variable. */
|
||||
class SsaCapturedDefinition extends SsaImplicitEntryDefinition {
|
||||
SsaCapturedDefinition() { captures(this, _) }
|
||||
|
||||
override string toString() { result = "SSA capture def(" + this.getSourceVariable() + ")" }
|
||||
|
||||
/** Holds if this definition captures the value of `capturedvar`. */
|
||||
predicate captures(SsaDefinition capturedvar) { captures(this, capturedvar) }
|
||||
|
||||
/**
|
||||
* Gets a definition that ultimately defines the captured variable and is not itself a phi node.
|
||||
*/
|
||||
SsaDefinition getAnUltimateCapturedDefinition() {
|
||||
exists(SsaDefinition capturedvar |
|
||||
captures(this, capturedvar) and result = capturedvar.getAnUltimateDefinition()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An SSA definition representing the potential definition of a variable
|
||||
* via a call.
|
||||
*/
|
||||
class SsaImplicitCallDefinition extends SsaImplicitWrite {
|
||||
SsaImplicitCallDefinition() { isNonLocal(this) and not hasQualifierUpdate(this) }
|
||||
|
||||
override string toString() { result = "SSA call def(" + this.getSourceVariable() + ")" }
|
||||
|
||||
/**
|
||||
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
|
||||
*/
|
||||
overlay[global]
|
||||
FieldWrite getANonLocalUpdate() { result = getANonLocalUpdate(this) }
|
||||
}
|
||||
|
||||
/** An SSA definition due to an update of the qualifier. */
|
||||
class SsaImplicitQualifierDefinition extends SsaImplicitWrite {
|
||||
SsaImplicitQualifierDefinition() { hasQualifierUpdate(this) }
|
||||
|
||||
override string toString() { result = "SSA qualifier def(" + this.getSourceVariable() + ")" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access of the SSA source variable underlying this SSA variable
|
||||
* that can be reached from this SSA variable without passing through any
|
||||
* other uses, but potentially through phi nodes and uncertain implicit
|
||||
* updates.
|
||||
*/
|
||||
VarRead ssaGetAFirstUse(SsaDefinition def) { firstUse(def, result) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `SsaDefinition` instead.
|
||||
*
|
||||
* An SSA variable.
|
||||
*/
|
||||
class SsaVariable extends Definition {
|
||||
deprecated class SsaVariable extends Definition {
|
||||
/** Gets the SSA source variable underlying this SSA variable. */
|
||||
SsaSourceVariable getSourceVariable() { result = super.getSourceVariable() }
|
||||
|
||||
/** Gets the `ControlFlowNode` at which this SSA variable is defined. */
|
||||
/**
|
||||
* DEPRECATED: Use `getControlFlowNode()` instead.
|
||||
*
|
||||
* Gets the `ControlFlowNode` at which this SSA variable is defined.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
ControlFlowNode getCfgNode() {
|
||||
exists(BasicBlock bb, int i, int j |
|
||||
deprecated ControlFlowNode getCfgNode() {
|
||||
exists(BasicBlock bb, int i |
|
||||
this.definesAt(_, bb, i) and
|
||||
// untracked definitions are inserted just before reads
|
||||
(if this instanceof UntrackedDef then j = i + 1 else j = i) and
|
||||
// phi nodes are inserted at position `-1`
|
||||
result = bb.getNode(0.maximum(j))
|
||||
result = bb.getNode(0.maximum(i))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -166,10 +220,16 @@ class SsaVariable extends Definition {
|
||||
/** Gets the `BasicBlock` in which this SSA variable is defined. */
|
||||
BasicBlock getBasicBlock() { result = super.getBasicBlock() }
|
||||
|
||||
/** Gets an access of this SSA variable. */
|
||||
VarRead getAUse() { result = getAUse(this) }
|
||||
/**
|
||||
* DEPRECATED: Use `getARead()` instead.
|
||||
*
|
||||
* Gets an access of this SSA variable.
|
||||
*/
|
||||
deprecated VarRead getAUse() { result = getAUse(this) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ssaGetAFirstUse(SsaDefinition)` instead.
|
||||
*
|
||||
* Gets an access of the SSA source variable underlying this SSA variable
|
||||
* that can be reached from this SSA variable without passing through any
|
||||
* other uses, but potentially through phi nodes and uncertain implicit
|
||||
@@ -178,35 +238,50 @@ class SsaVariable extends Definition {
|
||||
* Subsequent uses can be found by following the steps defined by
|
||||
* `adjacentUseUse`.
|
||||
*/
|
||||
VarRead getAFirstUse() { firstUse(this, result) }
|
||||
deprecated VarRead getAFirstUse() { firstUse(this, result) }
|
||||
|
||||
/** Holds if this SSA variable is live at the end of `b`. */
|
||||
predicate isLiveAtEndOfBlock(BasicBlock b) { ssaDefReachesEndOfBlock(b, this) }
|
||||
|
||||
/**
|
||||
* DEPRECATED.
|
||||
*
|
||||
* Gets an SSA variable whose value can flow to this one in one step. This
|
||||
* includes inputs to phi nodes, the prior definition of uncertain updates,
|
||||
* and the captured ssa variable for a closure variable.
|
||||
*/
|
||||
SsaVariable getAPhiInputOrPriorDef() {
|
||||
deprecated SsaVariable getAPhiInputOrPriorDef() {
|
||||
result = this.(SsaPhiNode).getAPhiInput() or
|
||||
result = this.(SsaUncertainImplicitUpdate).getPriorDef() or
|
||||
this.(SsaImplicitInit).captures(result)
|
||||
}
|
||||
|
||||
/** Gets a definition that ultimately defines this variable and is not itself a phi node. */
|
||||
SsaVariable getAnUltimateDefinition() {
|
||||
/**
|
||||
* DEPRECATED: Use `SsaCapturedDefinition::getAnUltimateCapturedDefinition()`
|
||||
* and/or `SsaDefinition::getAnUltimateDefinition()` instead.
|
||||
*
|
||||
* Gets a definition that ultimately defines this variable and is not itself a phi node.
|
||||
*/
|
||||
deprecated SsaVariable getAnUltimateDefinition() {
|
||||
result = this.getAPhiInputOrPriorDef*() and not result instanceof SsaPhiNode
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA variable that either explicitly or implicitly updates the variable. */
|
||||
class SsaUpdate extends SsaVariable instanceof WriteDefinition {
|
||||
/**
|
||||
* DEPRECATED: use `SsaWriteDefinition` instead.
|
||||
*
|
||||
* An SSA variable that either explicitly or implicitly updates the variable.
|
||||
*/
|
||||
deprecated class SsaUpdate extends SsaVariable instanceof WriteDefinition {
|
||||
SsaUpdate() { not this instanceof SsaImplicitInit }
|
||||
}
|
||||
|
||||
/** An SSA variable that is defined by a `VariableUpdate`. */
|
||||
class SsaExplicitUpdate extends SsaUpdate {
|
||||
/**
|
||||
* DEPRECATED: Use `SsaExplicitWrite` instead.
|
||||
*
|
||||
* An SSA variable that is defined by a `VariableUpdate`.
|
||||
*/
|
||||
deprecated class SsaExplicitUpdate extends SsaUpdate {
|
||||
private VariableUpdate upd;
|
||||
|
||||
SsaExplicitUpdate() { ssaExplicitUpdate(this, upd) }
|
||||
@@ -218,12 +293,14 @@ class SsaExplicitUpdate extends SsaUpdate {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaImplicitWrite` instead.
|
||||
*
|
||||
* An SSA variable that represents any sort of implicit update. This can be a
|
||||
* `Call` that might reach a non-local update of the field, an explicit or
|
||||
* implicit update of the qualifier of the field, or the implicit update that
|
||||
* occurs just prior to a `FieldRead` of an untracked field.
|
||||
*/
|
||||
class SsaImplicitUpdate extends SsaUpdate {
|
||||
deprecated class SsaImplicitUpdate extends SsaUpdate {
|
||||
SsaImplicitUpdate() { not this instanceof SsaExplicitUpdate }
|
||||
|
||||
override string toString() {
|
||||
@@ -246,73 +323,93 @@ class SsaImplicitUpdate extends SsaUpdate {
|
||||
}
|
||||
|
||||
private string getKind() {
|
||||
this instanceof UntrackedDef and result = "untracked"
|
||||
or
|
||||
this.hasExplicitQualifierUpdate() and
|
||||
result = "explicit qualifier"
|
||||
result = "explicit qualifier" // -> SSA qualifier def
|
||||
or
|
||||
if this.hasImplicitQualifierUpdate()
|
||||
then
|
||||
if isNonLocal(this)
|
||||
then result = "nonlocal + nonlocal qualifier"
|
||||
else result = "nonlocal qualifier"
|
||||
then result = "nonlocal + nonlocal qualifier" // -> SSA qualifier def
|
||||
else result = "nonlocal qualifier" // -> SSA qualifier def
|
||||
else (
|
||||
isNonLocal(this) and result = "nonlocal"
|
||||
isNonLocal(this) and result = "nonlocal" // -> SSA call def
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaImplicitCallDefinition.getANonLocalUpdate()` instead.
|
||||
*
|
||||
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
|
||||
*/
|
||||
overlay[global]
|
||||
FieldWrite getANonLocalUpdate() {
|
||||
exists(SsaSourceField f, Callable setter |
|
||||
relevantFieldUpdate(setter, f.getField(), result) and
|
||||
defUpdatesNamedField(this, f, setter)
|
||||
)
|
||||
}
|
||||
deprecated FieldWrite getANonLocalUpdate() { result = getANonLocalUpdate(this) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaImplicitQualifierDefinition` instead.
|
||||
*
|
||||
* Holds if this ssa variable might change the value to something unknown.
|
||||
*
|
||||
* Examples include updates that might change the value of the qualifier, or
|
||||
* reads from untracked variables, for example those where the field or one
|
||||
* of its qualifiers is volatile.
|
||||
*/
|
||||
predicate assignsUnknownValue() {
|
||||
this instanceof UntrackedDef
|
||||
or
|
||||
deprecated predicate assignsUnknownValue() {
|
||||
this.hasExplicitQualifierUpdate()
|
||||
or
|
||||
this.hasImplicitQualifierUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private predicate isNonLocalImpl(SsaImplicitUpdate su) { exists(su.getANonLocalUpdate()) }
|
||||
|
||||
private predicate isNonLocal(SsaImplicitUpdate su) = forceLocal(isNonLocalImpl/1)(su)
|
||||
private predicate hasQualifierUpdate(SsaImplicitWrite def) {
|
||||
exists(SsaWriteDefinition qdef, BasicBlock bb, int i |
|
||||
qdef.definesAt(def.getSourceVariable().getQualifier(), bb, i) and
|
||||
def.definesAt(_, bb, i) and
|
||||
not qdef instanceof SsaImplicitEntryDefinition
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
|
||||
*/
|
||||
overlay[global]
|
||||
private FieldWrite getANonLocalUpdate(SsaImplicitWrite calldef) {
|
||||
exists(SsaSourceField f, Callable setter |
|
||||
relevantFieldUpdate(setter, f.getField(), result) and
|
||||
defUpdatesNamedField(calldef, f, setter)
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private predicate isNonLocalImpl(SsaImplicitWrite calldef) { exists(getANonLocalUpdate(calldef)) }
|
||||
|
||||
private predicate isNonLocal(SsaImplicitWrite calldef) = forceLocal(isNonLocalImpl/1)(calldef)
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaUncertainWrite` instead.
|
||||
*
|
||||
* An SSA variable that represents an uncertain implicit update of the value.
|
||||
* This is a `Call` that might reach a non-local update of the field or one of
|
||||
* its qualifiers.
|
||||
*/
|
||||
class SsaUncertainImplicitUpdate extends SsaImplicitUpdate {
|
||||
deprecated class SsaUncertainImplicitUpdate extends SsaImplicitUpdate {
|
||||
SsaUncertainImplicitUpdate() { ssaUncertainImplicitUpdate(this) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getPriorDefinition()` instead.
|
||||
*
|
||||
* Gets the immediately preceding definition. Since this update is uncertain
|
||||
* the value from the preceding definition might still be valid.
|
||||
*/
|
||||
SsaVariable getPriorDef() { ssaDefReachesUncertainDef(result, this) }
|
||||
deprecated SsaVariable getPriorDef() { ssaDefReachesUncertainDef(result, this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaParameterInit`, `SsaImplicitEntryDefinition`, or `SsaCapturedDefinition` instead.
|
||||
*
|
||||
* An SSA variable that is defined by its initial value in the callable. This
|
||||
* includes initial values of parameters, fields, and closure variables.
|
||||
*/
|
||||
class SsaImplicitInit extends SsaVariable instanceof WriteDefinition {
|
||||
deprecated class SsaImplicitInit extends SsaVariable instanceof WriteDefinition {
|
||||
SsaImplicitInit() { ssaImplicitInit(this) }
|
||||
|
||||
override string toString() { result = "SSA init(" + this.getSourceVariable() + ")" }
|
||||
@@ -321,20 +418,30 @@ class SsaImplicitInit extends SsaVariable instanceof WriteDefinition {
|
||||
predicate captures(SsaVariable capturedvar) { captures(this, capturedvar) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaParameterInit::getParameter()` instead.
|
||||
*
|
||||
* Holds if the SSA variable is a parameter defined by its initial value in the callable.
|
||||
*/
|
||||
predicate isParameterDefinition(Parameter p) {
|
||||
deprecated predicate isParameterDefinition(Parameter p) {
|
||||
this.getSourceVariable() = TLocalVar(p.getCallable(), p) and
|
||||
p.getCallable().getBody().getControlFlowNode() = this.getCfgNode()
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA phi node. */
|
||||
class SsaPhiNode extends SsaVariable instanceof PhiNode {
|
||||
/**
|
||||
* DEPRECATED: Use `SsaPhiDefinition` instead.
|
||||
*
|
||||
* An SSA phi node.
|
||||
*/
|
||||
deprecated class SsaPhiNode extends SsaVariable instanceof PhiNode {
|
||||
override string toString() { result = "SSA phi(" + this.getSourceVariable() + ")" }
|
||||
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
SsaVariable getAPhiInput() { this.hasInputFromBlock(result, _) }
|
||||
/**
|
||||
* DEPRECATED: Use `getAnInput()` instead.
|
||||
*
|
||||
* Gets an input to the phi node defining the SSA variable.
|
||||
*/
|
||||
deprecated SsaVariable getAPhiInput() { this.hasInputFromBlock(result, _) }
|
||||
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
SsaVariable getAnInput() { this.hasInputFromBlock(result, _) }
|
||||
@@ -357,10 +464,10 @@ private class RefTypeCastingExpr extends CastingExpr {
|
||||
*
|
||||
* The `VarAccess` represents the access to `v` that `result` has the same value as.
|
||||
*/
|
||||
Expr sameValue(SsaVariable v, VarAccess va) {
|
||||
result = v.getAUse() and result = va
|
||||
Expr sameValue(SsaDefinition v, VarAccess va) {
|
||||
result = v.getARead() and result = va
|
||||
or
|
||||
result.(AssignExpr).getDest() = va and result = v.(SsaExplicitUpdate).getDefiningExpr()
|
||||
result.(AssignExpr).getDest() = va and result = v.(SsaExplicitWrite).getDefiningExpr()
|
||||
or
|
||||
result.(AssignExpr).getSource() = sameValue(v, va)
|
||||
or
|
||||
|
||||
@@ -12,7 +12,7 @@ module;
|
||||
|
||||
import java as J
|
||||
private import semmle.code.java.dispatch.VirtualDispatch
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import codeql.typeflow.TypeFlow
|
||||
private import codeql.typeflow.UniversalFlow as UniversalFlow
|
||||
@@ -27,7 +27,7 @@ private RefType boxIfNeeded(J::Type t) {
|
||||
module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
private newtype TFlowNode =
|
||||
TField(Field f) { not f.getType() instanceof PrimitiveType } or
|
||||
TSsa(BaseSsaVariable ssa) { not ssa.getSourceVariable().getType() instanceof PrimitiveType } or
|
||||
TSsa(Base::SsaDefinition ssa) { not ssa.getSourceVariable().getType() instanceof PrimitiveType } or
|
||||
TExpr(Expr e) or
|
||||
TMethod(Method m) { not m.getReturnType() instanceof PrimitiveType }
|
||||
|
||||
@@ -55,7 +55,7 @@ module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
Field asField() { this = TField(result) }
|
||||
|
||||
/** Gets the SSA variable corresponding to this node, if any. */
|
||||
BaseSsaVariable asSsa() { this = TSsa(result) }
|
||||
Base::SsaDefinition asSsa() { this = TSsa(result) }
|
||||
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { this = TExpr(result) }
|
||||
@@ -107,7 +107,7 @@ module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
not e.(FieldAccess).getField() = f
|
||||
)
|
||||
or
|
||||
n2.asSsa().(BaseSsaPhiNode).getAnUltimateLocalDefinition() = n1.asSsa()
|
||||
n2.asSsa().(Base::SsaPhiDefinition).getAnUltimateDefinition() = n1.asSsa()
|
||||
or
|
||||
exists(ReturnStmt ret |
|
||||
n2.asMethod() = ret.getEnclosingCallable() and ret.getResult() = n1.asExpr()
|
||||
@@ -118,14 +118,14 @@ module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
exists(Argument arg, Parameter p |
|
||||
privateParamArg(p, arg) and
|
||||
n1.asExpr() = arg and
|
||||
n2.asSsa().(BaseSsaImplicitInit).isParameterDefinition(p) and
|
||||
n2.asSsa().(Base::SsaParameterInit).getParameter() = p and
|
||||
// skip trivial recursion
|
||||
not arg = n2.asSsa().getAUse()
|
||||
not arg = n2.asSsa().getARead()
|
||||
)
|
||||
or
|
||||
n2.asExpr() = n1.asField().getAnAccess()
|
||||
or
|
||||
n2.asExpr() = n1.asSsa().getAUse()
|
||||
n2.asExpr() = n1.asSsa().getARead()
|
||||
or
|
||||
n2.asExpr().(CastingExpr).getExpr() = n1.asExpr() and
|
||||
not n2.asExpr().getType() instanceof PrimitiveType
|
||||
@@ -133,9 +133,9 @@ module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
n2.asExpr().(AssignExpr).getSource() = n1.asExpr() and
|
||||
not n2.asExpr().getType() instanceof PrimitiveType
|
||||
or
|
||||
n2.asSsa().(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
n2.asSsa().(Base::SsaExplicitWrite).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
or
|
||||
n2.asSsa().(BaseSsaImplicitInit).captures(n1.asSsa())
|
||||
n2.asSsa().(Base::SsaCapturedDefinition).captures(n1.asSsa())
|
||||
or
|
||||
n2.asExpr().(NotNullExpr).getExpr() = n1.asExpr()
|
||||
}
|
||||
@@ -147,7 +147,7 @@ module FlowStepsInput implements UniversalFlow::UniversalFlowInput<Location> {
|
||||
n.asExpr() instanceof NullLiteral
|
||||
or
|
||||
exists(LocalVariableDeclExpr decl |
|
||||
n.asSsa().(BaseSsaUpdate).getDefiningExpr() = decl and
|
||||
n.asSsa().(Base::SsaExplicitWrite).getDefiningExpr() = decl and
|
||||
not decl.hasImplicitInit() and
|
||||
not exists(decl.getInitOrPatternSource())
|
||||
)
|
||||
@@ -216,7 +216,9 @@ private module Input implements TypeFlowInput<Location> {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate upcastEnhancedForStmtAux(BaseSsaUpdate v, RefType t, RefType t1, RefType t2) {
|
||||
private predicate upcastEnhancedForStmtAux(
|
||||
Base::SsaExplicitWrite v, RefType t, RefType t1, RefType t2
|
||||
) {
|
||||
exists(EnhancedForStmt for |
|
||||
for.getVariable() = v.getDefiningExpr() and
|
||||
v.getSourceVariable().getType().getErasure() = t2 and
|
||||
@@ -230,7 +232,7 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* the type of the elements being iterated over, and this type is more precise
|
||||
* than the type of `v`.
|
||||
*/
|
||||
private predicate upcastEnhancedForStmt(BaseSsaUpdate v, RefType t) {
|
||||
private predicate upcastEnhancedForStmt(Base::SsaExplicitWrite v, RefType t) {
|
||||
exists(RefType t1, RefType t2 |
|
||||
upcastEnhancedForStmtAux(v, t, t1, t2) and
|
||||
t1.getASourceSupertype+() = t2
|
||||
@@ -238,9 +240,9 @@ private module Input implements TypeFlowInput<Location> {
|
||||
}
|
||||
|
||||
private predicate downcastSuccessorAux(
|
||||
CastingExpr cast, BaseSsaVariable v, RefType t, RefType t1, RefType t2
|
||||
CastingExpr cast, Base::SsaDefinition v, RefType t, RefType t1, RefType t2
|
||||
) {
|
||||
cast.getExpr() = v.getAUse() and
|
||||
cast.getExpr() = v.getARead() and
|
||||
t = cast.getType() and
|
||||
t1 = t.getErasure() and
|
||||
t2 = v.getSourceVariable().getType().getErasure()
|
||||
@@ -250,10 +252,10 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* Holds if `va` is an access to a value that has previously been downcast to `t`.
|
||||
*/
|
||||
private predicate downcastSuccessor(VarAccess va, RefType t) {
|
||||
exists(CastingExpr cast, BaseSsaVariable v, RefType t1, RefType t2 |
|
||||
exists(CastingExpr cast, Base::SsaDefinition v, RefType t1, RefType t2 |
|
||||
downcastSuccessorAux(pragma[only_bind_into](cast), v, t, t1, t2) and
|
||||
t1.getASourceSupertype+() = t2 and
|
||||
va = v.getAUse() and
|
||||
va = v.getARead() and
|
||||
dominates(cast.getControlFlowNode(), va.getControlFlowNode()) and
|
||||
dominates(cast.getControlFlowNode().getANormalSuccessor(), va.getControlFlowNode())
|
||||
)
|
||||
@@ -263,9 +265,9 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* Holds if `va` is an access to a value that is guarded by `instanceof t` or `case e t`.
|
||||
*/
|
||||
private predicate typeTestGuarded(VarAccess va, RefType t) {
|
||||
exists(Guard typeTest, BaseSsaVariable v |
|
||||
typeTest.appliesTypeTest(v.getAUse(), t, _) and
|
||||
va = v.getAUse() and
|
||||
exists(Guard typeTest, Base::SsaDefinition v |
|
||||
typeTest.appliesTypeTest(v.getARead(), t, _) and
|
||||
va = v.getARead() and
|
||||
guardControls_v1(typeTest, va.getBasicBlock(), true)
|
||||
)
|
||||
}
|
||||
@@ -274,12 +276,12 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* Holds if `aa` is an access to a value that is guarded by `instanceof t` or `case e t`.
|
||||
*/
|
||||
private predicate arrayTypeTestGuarded(ArrayAccess aa, RefType t) {
|
||||
exists(Guard typeTest, BaseSsaVariable v1, BaseSsaVariable v2, ArrayAccess aa1 |
|
||||
exists(Guard typeTest, Base::SsaDefinition v1, Base::SsaDefinition v2, ArrayAccess aa1 |
|
||||
typeTest.appliesTypeTest(aa1, t, _) and
|
||||
aa1.getArray() = v1.getAUse() and
|
||||
aa1.getIndexExpr() = v2.getAUse() and
|
||||
aa.getArray() = v1.getAUse() and
|
||||
aa.getIndexExpr() = v2.getAUse() and
|
||||
aa1.getArray() = v1.getARead() and
|
||||
aa1.getIndexExpr() = v2.getARead() and
|
||||
aa.getArray() = v1.getARead() and
|
||||
aa.getIndexExpr() = v2.getARead() and
|
||||
guardControls_v1(typeTest, aa.getBasicBlock(), true)
|
||||
)
|
||||
}
|
||||
@@ -321,14 +323,14 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* Holds if `ioe` checks `v`, its true-successor is `bb`, and `bb` has multiple
|
||||
* predecessors.
|
||||
*/
|
||||
private predicate instanceofDisjunct(InstanceOfExpr ioe, BasicBlock bb, BaseSsaVariable v) {
|
||||
ioe.getExpr() = v.getAUse() and
|
||||
private predicate instanceofDisjunct(InstanceOfExpr ioe, BasicBlock bb, Base::SsaDefinition v) {
|
||||
ioe.getExpr() = v.getARead() and
|
||||
strictcount(bb.getAPredecessor()) > 1 and
|
||||
exists(ConditionBlock cb | cb.getCondition() = ioe and cb.getTestSuccessor(true) = bb)
|
||||
}
|
||||
|
||||
/** Holds if `bb` is disjunctively guarded by multiple `instanceof` tests on `v`. */
|
||||
private predicate instanceofDisjunction(BasicBlock bb, BaseSsaVariable v) {
|
||||
private predicate instanceofDisjunction(BasicBlock bb, Base::SsaDefinition v) {
|
||||
strictcount(InstanceOfExpr ioe | instanceofDisjunct(ioe, bb, v)) =
|
||||
strictcount(bb.getAPredecessor())
|
||||
}
|
||||
@@ -338,10 +340,10 @@ private module Input implements TypeFlowInput<Location> {
|
||||
* `instanceof t_i` where `t` is one of those `t_i`.
|
||||
*/
|
||||
predicate instanceofDisjunctionGuarded(TypeFlowNode n, RefType t) {
|
||||
exists(BasicBlock bb, InstanceOfExpr ioe, BaseSsaVariable v, VarAccess va |
|
||||
exists(BasicBlock bb, InstanceOfExpr ioe, Base::SsaDefinition v, VarAccess va |
|
||||
instanceofDisjunction(bb, v) and
|
||||
bb.dominates(va.getBasicBlock()) and
|
||||
va = v.getAUse() and
|
||||
va = v.getARead() and
|
||||
instanceofDisjunct(ioe, bb, v) and
|
||||
t = ioe.getSyntacticCheckedType() and
|
||||
n.asExpr() = va
|
||||
|
||||
@@ -25,7 +25,8 @@ private module BaseSsaStage {
|
||||
predicate backref() {
|
||||
(exists(TLocalVar(_, _)) implies any()) and
|
||||
(exists(any(BaseSsaSourceVariable v).getAnAccess()) implies any()) and
|
||||
(exists(getAUse(_)) implies any())
|
||||
(exists(any(SsaDefinition def).getARead()) implies any()) and
|
||||
(captures(_, _) implies any())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +158,7 @@ private module BaseSsaImpl {
|
||||
|
||||
private import BaseSsaImpl
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock> {
|
||||
private module SsaImplInput implements SsaImplCommon::InputSig<Location, BasicBlock> {
|
||||
class SourceVariable = BaseSsaSourceVariable;
|
||||
|
||||
/**
|
||||
@@ -169,7 +170,7 @@ private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock>
|
||||
certain = true
|
||||
or
|
||||
hasEntryDef(v, bb) and
|
||||
i = 0 and
|
||||
i = -1 and
|
||||
certain = true
|
||||
}
|
||||
|
||||
@@ -189,67 +190,46 @@ private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock>
|
||||
}
|
||||
}
|
||||
|
||||
private module Impl = SsaImplCommon::Make<Location, Cfg, SsaInput>;
|
||||
private module Impl = SsaImplCommon::Make<Location, Cfg, SsaImplInput>;
|
||||
|
||||
private module SsaInput implements Impl::SsaInputSig {
|
||||
private import java as J
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
class Parameter = J::Parameter;
|
||||
|
||||
class VariableWrite = J::VariableWrite;
|
||||
|
||||
predicate explicitWrite(VariableWrite w, BasicBlock bb, int i, BaseSsaSourceVariable v) {
|
||||
variableUpdate(v, w.asExpr().getControlFlowNode(), bb, i)
|
||||
or
|
||||
exists(Parameter p, Callable c |
|
||||
c = p.getCallable() and
|
||||
v = TLocalVar(c, p) and
|
||||
w.isParameterInit(p) and
|
||||
c.getBody().getBasicBlock() = bb and
|
||||
i = -1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Ssa = Impl::MakeSsa<SsaInput>;
|
||||
|
||||
import Ssa
|
||||
private import Cached
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
VarRead getAUse(Impl::Definition def) {
|
||||
BaseSsaStage::ref() and
|
||||
exists(BaseSsaSourceVariable v, BasicBlock bb, int i |
|
||||
Impl::ssaDefReachesRead(v, def, bb, i) and
|
||||
result.getControlFlowNode() = bb.getNode(i) and
|
||||
result = v.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Impl::Definition def) {
|
||||
Impl::ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate firstUse(Impl::Definition def, VarRead use) {
|
||||
exists(BasicBlock bb, int i |
|
||||
Impl::firstUse(def, bb, i, _) and
|
||||
use.getControlFlowNode() = bb.getNode(i)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaUpdate(Impl::Definition def, VariableUpdate upd) {
|
||||
exists(BaseSsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
variableUpdate(v, upd.getControlFlowNode(), bb, i) and
|
||||
getDestVar(upd) = v
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaImplicitInit(Impl::WriteDefinition def) {
|
||||
exists(BaseSsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
hasEntryDef(v, bb) and
|
||||
i = 0
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `init` is a closure variable that captures the value of `capturedvar`. */
|
||||
cached
|
||||
predicate captures(BaseSsaImplicitInit init, BaseSsaVariable capturedvar) {
|
||||
predicate captures(SsaImplicitEntryDefinition init, SsaDefinition capturedvar) {
|
||||
exists(BasicBlock bb, int i |
|
||||
Impl::ssaDefReachesRead(_, capturedvar, bb, i) and
|
||||
Ssa::ssaDefReachesUncertainRead(_, capturedvar, bb, i) and
|
||||
variableCapture(capturedvar.getSourceVariable(), init.getSourceVariable(), bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate phiHasInputFromBlock(Impl::PhiNode phi, Impl::Definition inp, BasicBlock bb) {
|
||||
Impl::phiHasInputFromBlock(phi, inp, bb)
|
||||
}
|
||||
|
||||
cached
|
||||
module SsaPublic {
|
||||
/**
|
||||
@@ -285,36 +265,73 @@ private module Cached {
|
||||
|
||||
import SsaPublic
|
||||
|
||||
/**
|
||||
* An SSA variable.
|
||||
*/
|
||||
class BaseSsaVariable extends Impl::Definition {
|
||||
/** Gets the `ControlFlowNode` at which this SSA variable is defined. */
|
||||
ControlFlowNode getCfgNode() {
|
||||
exists(BasicBlock bb, int i | this.definesAt(_, bb, i) and result = bb.getNode(0.maximum(i)))
|
||||
}
|
||||
/** An SSA definition in a closure that captures a variable. */
|
||||
class SsaCapturedDefinition extends SsaImplicitEntryDefinition {
|
||||
SsaCapturedDefinition() { captures(this, _) }
|
||||
|
||||
/** Gets an access of this SSA variable. */
|
||||
VarRead getAUse() { result = getAUse(this) }
|
||||
override string toString() { result = "SSA capture def(" + this.getSourceVariable() + ")" }
|
||||
|
||||
/** Holds if this definition captures the value of `capturedvar`. */
|
||||
predicate captures(SsaDefinition capturedvar) { captures(this, capturedvar) }
|
||||
|
||||
/**
|
||||
* Gets an access of the SSA source variable underlying this SSA variable
|
||||
* that can be reached from this SSA variable without passing through any
|
||||
* other uses, but potentially through phi nodes.
|
||||
*
|
||||
* Subsequent uses can be found by following the steps defined by
|
||||
* `baseSsaAdjacentUseUse`.
|
||||
* Gets a definition that ultimately defines the captured variable and is not itself a phi node.
|
||||
*/
|
||||
VarRead getAFirstUse() { firstUse(this, result) }
|
||||
SsaDefinition getAnUltimateCapturedDefinition() {
|
||||
exists(SsaDefinition capturedvar |
|
||||
captures(this, capturedvar) and result = capturedvar.getAnUltimateDefinition()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private predicate ssaUpdate(Impl::Definition def, VariableUpdate upd) {
|
||||
exists(BaseSsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
variableUpdate(v, upd.getControlFlowNode(), bb, i) and
|
||||
getDestVar(upd) = v
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate ssaImplicitInit(Impl::WriteDefinition def) {
|
||||
exists(BaseSsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
hasEntryDef(v, bb) and
|
||||
i = -1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaDefinition` instead.
|
||||
*
|
||||
* An SSA variable.
|
||||
*/
|
||||
deprecated class BaseSsaVariable extends Impl::Definition {
|
||||
/**
|
||||
* DEPRECATED: Use `getControlFlowNode()` instead.
|
||||
*
|
||||
* Gets the `ControlFlowNode` at which this SSA variable is defined.
|
||||
*/
|
||||
deprecated ControlFlowNode getCfgNode() { result = this.(SsaDefinition).getControlFlowNode() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getARead()` instead.
|
||||
*
|
||||
* Gets an access of this SSA variable.
|
||||
*/
|
||||
deprecated VarRead getAUse() { result = this.(SsaDefinition).getARead() }
|
||||
|
||||
/** Holds if this SSA variable is live at the end of `b`. */
|
||||
predicate isLiveAtEndOfBlock(BasicBlock b) { ssaDefReachesEndOfBlock(b, this) }
|
||||
predicate isLiveAtEndOfBlock(BasicBlock b) { this.(SsaDefinition).isLiveAtEndOfBlock(b) }
|
||||
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
private BaseSsaVariable getAPhiInput() { result = this.(BaseSsaPhiNode).getAPhiInput() }
|
||||
private BaseSsaVariable getAPhiInput() { result = this.(BaseSsaPhiNode).getAnInput() }
|
||||
|
||||
/** Gets a definition in the same callable that ultimately defines this variable and is not itself a phi node. */
|
||||
BaseSsaVariable getAnUltimateLocalDefinition() {
|
||||
/**
|
||||
* DEPRECATED: Use `SsaDefinition::getAnUltimateDefinition()` instead.
|
||||
*
|
||||
* Gets a definition in the same callable that ultimately defines this variable and is not itself a phi node.
|
||||
*/
|
||||
deprecated BaseSsaVariable getAnUltimateLocalDefinition() {
|
||||
result = this.getAPhiInput*() and not result instanceof BaseSsaPhiNode
|
||||
}
|
||||
|
||||
@@ -324,18 +341,27 @@ class BaseSsaVariable extends Impl::Definition {
|
||||
* variable.
|
||||
*/
|
||||
private BaseSsaVariable getAPhiInputOrCapturedVar() {
|
||||
result = this.(BaseSsaPhiNode).getAPhiInput() or
|
||||
result = this.(BaseSsaPhiNode).getAnInput() or
|
||||
this.(BaseSsaImplicitInit).captures(result)
|
||||
}
|
||||
|
||||
/** Gets a definition that ultimately defines this variable and is not itself a phi node. */
|
||||
BaseSsaVariable getAnUltimateDefinition() {
|
||||
/**
|
||||
* DEPRECATED: Use `SsaCapturedDefinition::getAnUltimateCapturedDefinition()`
|
||||
* and/or `SsaDefinition::getAnUltimateDefinition()` instead.
|
||||
*
|
||||
* Gets a definition that ultimately defines this variable and is not itself a phi node.
|
||||
*/
|
||||
deprecated BaseSsaVariable getAnUltimateDefinition() {
|
||||
result = this.getAPhiInputOrCapturedVar*() and not result instanceof BaseSsaPhiNode
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA variable that is defined by a `VariableUpdate`. */
|
||||
class BaseSsaUpdate extends BaseSsaVariable instanceof Impl::WriteDefinition {
|
||||
/**
|
||||
* DEPRECATED: Use `SsaExplicitWrite` instead.
|
||||
*
|
||||
* An SSA variable that is defined by a `VariableUpdate`.
|
||||
*/
|
||||
deprecated class BaseSsaUpdate extends BaseSsaVariable instanceof Impl::WriteDefinition {
|
||||
BaseSsaUpdate() { ssaUpdate(this, _) }
|
||||
|
||||
/** Gets the `VariableUpdate` defining the SSA variable. */
|
||||
@@ -343,34 +369,46 @@ class BaseSsaUpdate extends BaseSsaVariable instanceof Impl::WriteDefinition {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaParameterInit` or `SsaCapturedDefinition` instead.
|
||||
*
|
||||
* An SSA variable that is defined by its initial value in the callable. This
|
||||
* includes initial values of parameters, fields, and closure variables.
|
||||
*/
|
||||
class BaseSsaImplicitInit extends BaseSsaVariable instanceof Impl::WriteDefinition {
|
||||
deprecated class BaseSsaImplicitInit extends BaseSsaVariable instanceof Impl::WriteDefinition {
|
||||
BaseSsaImplicitInit() { ssaImplicitInit(this) }
|
||||
|
||||
/** Holds if this is a closure variable that captures the value of `capturedvar`. */
|
||||
predicate captures(BaseSsaVariable capturedvar) { captures(this, capturedvar) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SsaParameterInit::getParameter()` instead.
|
||||
*
|
||||
* Holds if the SSA variable is a parameter defined by its initial value in the callable.
|
||||
*/
|
||||
predicate isParameterDefinition(Parameter p) {
|
||||
deprecated predicate isParameterDefinition(Parameter p) {
|
||||
this.getSourceVariable() = TLocalVar(p.getCallable(), p) and
|
||||
p.getCallable().getBody().getControlFlowNode() = this.getCfgNode()
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA phi node. */
|
||||
class BaseSsaPhiNode extends BaseSsaVariable instanceof Impl::PhiNode {
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
BaseSsaVariable getAPhiInput() { this.hasInputFromBlock(result, _) }
|
||||
/**
|
||||
* DEPRECATED: Use `SsaPhiDefinition` instead.
|
||||
*
|
||||
* An SSA phi node.
|
||||
*/
|
||||
deprecated class BaseSsaPhiNode extends BaseSsaVariable instanceof Impl::PhiNode {
|
||||
/**
|
||||
* DEPRECATED: Use `getAnInput()` instead.
|
||||
*
|
||||
* Gets an input to the phi node defining the SSA variable.
|
||||
*/
|
||||
deprecated BaseSsaVariable getAPhiInput() { this.hasInputFromBlock(result, _) }
|
||||
|
||||
/** Gets an input to the phi node defining the SSA variable. */
|
||||
BaseSsaVariable getAnInput() { this.hasInputFromBlock(result, _) }
|
||||
|
||||
/** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */
|
||||
predicate hasInputFromBlock(BaseSsaVariable inp, BasicBlock bb) {
|
||||
phiHasInputFromBlock(this, inp, bb)
|
||||
this.(SsaPhiDefinition).hasInputFromBlock(inp, bb)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,12 +460,12 @@ predicate arrayStoreStep(Node node1, Node node2) {
|
||||
}
|
||||
|
||||
private predicate enhancedForStmtStep(Node node1, Node node2, Type containerType) {
|
||||
exists(EnhancedForStmt for, Expr e, SsaExplicitUpdate v |
|
||||
exists(EnhancedForStmt for, Expr e, SsaExplicitWrite v |
|
||||
for.getExpr() = e and
|
||||
node1.asExpr() = e and
|
||||
containerType = e.getType() and
|
||||
v.getDefiningExpr() = for.getVariable() and
|
||||
v.getAFirstUse() = node2.asExpr()
|
||||
ssaGetAFirstUse(v) = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ private module DispatchImpl {
|
||||
not (
|
||||
// Only use summarized callables with generated summaries in case
|
||||
// the static call target is not in the source code.
|
||||
// Note that if applyGeneratedModel holds it implies that there doesn't
|
||||
// Note that if `applyGeneratedModel` holds it implies that there doesn't
|
||||
// exist a manual model.
|
||||
exists(Callable staticTarget | staticTarget = call.getCallee().getSourceDeclaration() |
|
||||
staticTarget.fromSource() and not staticTarget.isStub()
|
||||
|
||||
@@ -29,7 +29,7 @@ private predicate deadcode(Expr e) {
|
||||
module SsaFlow {
|
||||
module Impl = SsaImpl::DataFlowIntegration;
|
||||
|
||||
private predicate ssaDefAssigns(SsaExplicitUpdate def, Expr value) {
|
||||
private predicate ssaDefAssigns(SsaExplicitWrite def, Expr value) {
|
||||
exists(VariableUpdate upd | upd = def.getDefiningExpr() |
|
||||
value = upd.(VariableAssign).getSource() or
|
||||
value = upd.(AssignOp) or
|
||||
@@ -46,7 +46,7 @@ module SsaFlow {
|
||||
or
|
||||
exists(Parameter p |
|
||||
n = TExplicitParameterNode(p) and
|
||||
result.(Impl::WriteDefSourceNode).getDefinition().(SsaImplicitInit).isParameterDefinition(p)
|
||||
result.(Impl::WriteDefSourceNode).getDefinition().(SsaParameterInit).getParameter() = p
|
||||
)
|
||||
or
|
||||
ssaDefAssigns(result.(Impl::WriteDefSourceNode).getDefinition(), n.asExpr())
|
||||
|
||||
@@ -62,10 +62,10 @@ private predicate fieldStep(Node node1, Node node2) {
|
||||
private predicate closureFlowStep(Expr e1, Expr e2) {
|
||||
simpleAstFlowStep(e1, e2)
|
||||
or
|
||||
exists(SsaVariable v |
|
||||
v.getAUse() = e2 and
|
||||
v.getAnUltimateDefinition().(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() =
|
||||
e1
|
||||
exists(SsaDefinition v, SsaExplicitWrite def | v.getARead() = e2 and def.getValue() = e1 |
|
||||
v.getAnUltimateDefinition() = def
|
||||
or
|
||||
v.(SsaCapturedDefinition).getAnUltimateCapturedDefinition() = def
|
||||
)
|
||||
}
|
||||
|
||||
@@ -395,13 +395,13 @@ class CastNode extends ExprNode {
|
||||
CastNode() {
|
||||
this.getExpr() instanceof CastingExpr
|
||||
or
|
||||
exists(SsaExplicitUpdate upd |
|
||||
exists(SsaExplicitWrite upd |
|
||||
upd.getDefiningExpr().(VariableAssign).getSource() =
|
||||
[
|
||||
any(SwitchStmt ss).getExpr(), any(SwitchExpr se).getExpr(),
|
||||
any(InstanceOfExpr ioe).getExpr()
|
||||
] and
|
||||
this.asExpr() = upd.getAFirstUse()
|
||||
this.asExpr() = ssaGetAFirstUse(upd)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -531,9 +531,9 @@ class NodeRegion instanceof BasicBlock {
|
||||
private predicate constantBooleanExpr(Expr e, boolean val) {
|
||||
e.(CompileTimeConstantExpr).getBooleanValue() = val
|
||||
or
|
||||
exists(SsaExplicitUpdate v, Expr src |
|
||||
e = v.getAUse() and
|
||||
src = v.getDefiningExpr().(VariableAssign).getSource() and
|
||||
exists(SsaExplicitWrite v, Expr src |
|
||||
e = v.getARead() and
|
||||
src = v.getValue() and
|
||||
constantBooleanExpr(src, val)
|
||||
)
|
||||
}
|
||||
@@ -551,15 +551,15 @@ private class ConstantBooleanArgumentNode extends ArgumentNode, ExprNode {
|
||||
*/
|
||||
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) {
|
||||
exists(
|
||||
ExplicitParameterNode paramNode, ConstantBooleanArgumentNode arg, SsaImplicitInit param,
|
||||
ExplicitParameterNode paramNode, ConstantBooleanArgumentNode arg, SsaParameterInit param,
|
||||
Guard guard
|
||||
|
|
||||
// get constant bool argument and parameter for this call
|
||||
viableParamArg(call, pragma[only_bind_into](paramNode), arg) and
|
||||
// get the ssa variable definition for this parameter
|
||||
param.isParameterDefinition(paramNode.getParameter()) and
|
||||
param.getParameter() = paramNode.getParameter() and
|
||||
// which is used in a guard
|
||||
param.getAUse() = guard and
|
||||
param.getARead() = guard and
|
||||
// which controls `n` with the opposite value of `arg`
|
||||
guard
|
||||
.controls(nr,
|
||||
|
||||
@@ -99,11 +99,12 @@ predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)
|
||||
* updates.
|
||||
*/
|
||||
predicate hasNonlocalValue(FieldRead fr) {
|
||||
not exists(SsaVariable v | v.getAUse() = fr)
|
||||
not exists(SsaDefinition v | v.getARead() = fr)
|
||||
or
|
||||
exists(SsaVariable v, SsaVariable def | v.getAUse() = fr and def = v.getAnUltimateDefinition() |
|
||||
def instanceof SsaImplicitInit or
|
||||
def instanceof SsaImplicitUpdate
|
||||
exists(SsaDefinition v, SsaDefinition def |
|
||||
v.getARead() = fr and
|
||||
def = v.getAnUltimateDefinition() and
|
||||
def instanceof SsaImplicitWrite
|
||||
)
|
||||
}
|
||||
|
||||
@@ -373,6 +374,29 @@ class ContentSet instanceof Content {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the guard `g` validates the expression `e` upon evaluating to `gv`.
|
||||
*
|
||||
* The expression `e` is expected to be a syntactic part of the guard `g`.
|
||||
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
|
||||
* the argument `x`.
|
||||
*/
|
||||
signature predicate valueGuardChecksSig(Guard g, Expr e, GuardValue gv);
|
||||
|
||||
/**
|
||||
* Provides a set of barrier nodes for a guard that validates an expression.
|
||||
*
|
||||
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
|
||||
* in data flow and taint tracking.
|
||||
*/
|
||||
module BarrierGuardValue<valueGuardChecksSig/3 guardChecks> {
|
||||
/** Gets a node that is safely guarded by the given guard check. */
|
||||
Node getABarrierNode() {
|
||||
SsaFlow::asNode(result) =
|
||||
SsaImpl::DataFlowIntegration::BarrierGuard<guardChecks/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
|
||||
*
|
||||
@@ -389,9 +413,38 @@ signature predicate guardChecksSig(Guard g, Expr e, boolean branch);
|
||||
* in data flow and taint tracking.
|
||||
*/
|
||||
module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
private predicate guardChecks0(Guard g, Expr e, GuardValue gv) {
|
||||
guardChecks(g, e, gv.asBooleanValue())
|
||||
}
|
||||
|
||||
/** Gets a node that is safely guarded by the given guard check. */
|
||||
Node getABarrierNode() {
|
||||
Node getABarrierNode() { result = BarrierGuardValue<guardChecks0/3>::getABarrierNode() }
|
||||
}
|
||||
|
||||
bindingset[this]
|
||||
private signature class ParamSig;
|
||||
|
||||
private module WithParam<ParamSig P> {
|
||||
/**
|
||||
* Holds if the guard `g` validates the expression `e` upon evaluating to `gv`.
|
||||
*
|
||||
* The expression `e` is expected to be a syntactic part of the guard `g`.
|
||||
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
|
||||
* the argument `x`.
|
||||
*/
|
||||
signature predicate guardChecksSig(Guard g, Expr e, GuardValue gv, P param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a set of barrier nodes for a guard that validates an expression.
|
||||
*
|
||||
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
|
||||
* in data flow and taint tracking.
|
||||
*/
|
||||
module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guardChecks> {
|
||||
/** Gets a node that is safely guarded by the given guard check. */
|
||||
Node getABarrierNode(P param) {
|
||||
SsaFlow::asNode(result) =
|
||||
SsaImpl::DataFlowIntegration::BarrierGuard<guardChecks/3>::getABarrierNode()
|
||||
SsaImpl::DataFlowIntegration::ParameterizedBarrierGuard<P, guardChecks/4>::getABarrierNode(param)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
private import codeql.mad.static.ModelsAsData as SharedMaD
|
||||
|
||||
/**
|
||||
* Holds if a source model exists for the given parameters.
|
||||
*/
|
||||
@@ -20,6 +22,22 @@ extensible predicate sinkModel(
|
||||
string input, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if a barrier model exists for the given parameters.
|
||||
*/
|
||||
extensible predicate barrierModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if a barrier guard model exists for the given parameters.
|
||||
*/
|
||||
extensible predicate barrierGuardModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string acceptingvalue, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if a summary model exists for the given parameters.
|
||||
*/
|
||||
@@ -77,3 +95,9 @@ extensible predicate experimentalSummaryModel(
|
||||
string input, string output, string kind, string provenance, string filter,
|
||||
QlBuiltins::ExtensionId madId
|
||||
);
|
||||
|
||||
module Extensions implements SharedMaD::ExtensionsSig {
|
||||
import ExternalFlowExtensions
|
||||
|
||||
predicate namespaceGrouping(string group, string namespace) { none() }
|
||||
}
|
||||
|
||||
@@ -158,7 +158,9 @@ private predicate relatedArgSpec(Callable c, string spec) {
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _, _) or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, spec, _, _, _) or
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _) or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _)
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _) or
|
||||
barrierModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _) or
|
||||
barrierGuardModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _, _)
|
||||
|
|
||||
c = interpretElement(namespace, type, subtypes, name, signature, ext, _)
|
||||
)
|
||||
@@ -226,11 +228,10 @@ module SourceSinkInterpretationInput implements
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
SourceOrSinkElement baseSource, string originalOutput, QlBuiltins::ExtensionId madId
|
||||
SourceOrSinkElement baseSource, string originalOutput
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, originalOutput, kind, provenance,
|
||||
madId) and
|
||||
model = "MaD:" + madId.toString() and
|
||||
model) and
|
||||
baseSource = interpretElement(namespace, type, subtypes, name, signature, ext, _) and
|
||||
(
|
||||
e = baseSource and output = originalOutput
|
||||
@@ -245,11 +246,10 @@ module SourceSinkInterpretationInput implements
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
SourceOrSinkElement baseSink, string originalInput, QlBuiltins::ExtensionId madId
|
||||
SourceOrSinkElement baseSink, string originalInput
|
||||
|
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, originalInput, kind, provenance,
|
||||
madId) and
|
||||
model = "MaD:" + madId.toString() and
|
||||
model) and
|
||||
baseSink = interpretElement(namespace, type, subtypes, name, signature, ext, _) and
|
||||
(
|
||||
e = baseSink and originalInput = input
|
||||
@@ -259,6 +259,43 @@ module SourceSinkInterpretationInput implements
|
||||
)
|
||||
}
|
||||
|
||||
predicate barrierElement(
|
||||
Element e, string output, string kind, Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
SourceOrSinkElement baseBarrier, string originalOutput
|
||||
|
|
||||
barrierModel(namespace, type, subtypes, name, signature, ext, originalOutput, kind,
|
||||
provenance, model) and
|
||||
baseBarrier = interpretElement(namespace, type, subtypes, name, signature, ext, _) and
|
||||
(
|
||||
e = baseBarrier and output = originalOutput
|
||||
or
|
||||
correspondingKotlinParameterDefaultsArgSpec(baseBarrier, e, originalOutput, output)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate barrierGuardElement(
|
||||
Element e, string input, Public::AcceptingValue acceptingvalue, string kind,
|
||||
Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
SourceOrSinkElement baseBarrier, string originalInput
|
||||
|
|
||||
barrierGuardModel(namespace, type, subtypes, name, signature, ext, originalInput,
|
||||
acceptingvalue, kind, provenance, model) and
|
||||
baseBarrier = interpretElement(namespace, type, subtypes, name, signature, ext, _) and
|
||||
(
|
||||
e = baseBarrier and input = originalInput
|
||||
or
|
||||
correspondingKotlinParameterDefaultsArgSpec(baseBarrier, e, originalInput, input)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
class SourceOrSinkElement = Element;
|
||||
|
||||
private newtype TInterpretNode =
|
||||
@@ -343,12 +380,10 @@ module Private {
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string originalInput, string originalOutput, Callable baseCallable,
|
||||
QlBuiltins::ExtensionId madId
|
||||
string originalInput, string originalOutput, Callable baseCallable
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, originalInput, originalOutput,
|
||||
kind, provenance, madId) and
|
||||
model = "MaD:" + madId.toString() and
|
||||
kind, provenance, model) and
|
||||
baseCallable = interpretElement(namespace, type, subtypes, name, signature, ext, isExact) and
|
||||
(
|
||||
c.asCallable() = baseCallable and input = originalInput and output = originalOutput
|
||||
|
||||
@@ -82,13 +82,6 @@ private module TrackedVariablesImpl {
|
||||
|
||||
private import TrackedVariablesImpl
|
||||
|
||||
private predicate untrackedFieldWrite(BasicBlock bb, int i, SsaSourceVariable v) {
|
||||
v =
|
||||
any(SsaSourceField nf |
|
||||
bb.getNode(i + 1) = nf.getAnAccess().(FieldRead).getControlFlowNode() and not trackField(nf)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the definition point of a nested class in the parent scope. */
|
||||
private ControlFlowNode parentDef(NestedClass nc) {
|
||||
nc.(AnonymousClass).getClassInstanceExpr().getControlFlowNode() = result or
|
||||
@@ -171,7 +164,7 @@ private predicate uncertainVariableUpdateImpl(TrackedVar v, ControlFlowNode n, B
|
||||
predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) =
|
||||
forceLocal(uncertainVariableUpdateImpl/4)(v, n, b, i)
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock> {
|
||||
private module SsaImplInput implements SsaImplCommon::InputSig<Location, BasicBlock> {
|
||||
class SourceVariable = SsaSourceVariable;
|
||||
|
||||
/**
|
||||
@@ -184,11 +177,8 @@ private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock>
|
||||
certainVariableUpdate(v, _, bb, i) and
|
||||
certain = true
|
||||
or
|
||||
untrackedFieldWrite(bb, i, v) and
|
||||
certain = true
|
||||
or
|
||||
hasEntryDef(v, bb) and
|
||||
i = 0 and
|
||||
i = -1 and
|
||||
certain = true
|
||||
or
|
||||
uncertainVariableUpdate(v, _, bb, i) and
|
||||
@@ -204,7 +194,10 @@ private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock>
|
||||
hasDominanceInformation(bb) and
|
||||
(
|
||||
exists(VarRead use |
|
||||
v.getAnAccess() = use and bb.getNode(i) = use.getControlFlowNode() and certain = true
|
||||
v instanceof TrackedVar and
|
||||
v.getAnAccess() = use and
|
||||
bb.getNode(i) = use.getControlFlowNode() and
|
||||
certain = true
|
||||
)
|
||||
or
|
||||
variableCapture(v, _, bb, i) and
|
||||
@@ -213,7 +206,35 @@ private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock>
|
||||
}
|
||||
}
|
||||
|
||||
import SsaImplCommon::Make<Location, Cfg, SsaInput> as Impl
|
||||
import SsaImplCommon::Make<Location, Cfg, SsaImplInput> as Impl
|
||||
|
||||
private module SsaInput implements Impl::SsaInputSig {
|
||||
private import java as J
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
class Parameter = J::Parameter;
|
||||
|
||||
class VariableWrite = J::VariableWrite;
|
||||
|
||||
predicate explicitWrite(VariableWrite w, BasicBlock bb, int i, SsaSourceVariable v) {
|
||||
exists(VariableUpdate upd |
|
||||
upd = w.asExpr() and
|
||||
certainVariableUpdate(v, upd.getControlFlowNode(), bb, i) and
|
||||
getDestVar(upd) = v
|
||||
)
|
||||
or
|
||||
exists(Parameter p, Callable c |
|
||||
c = p.getCallable() and
|
||||
v = TLocalVar(c, p) and
|
||||
w.isParameterInit(p) and
|
||||
c.getBody().getBasicBlock() = bb and
|
||||
i = -1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Ssa = Impl::MakeSsa<SsaInput>;
|
||||
|
||||
final class Definition = Impl::Definition;
|
||||
|
||||
@@ -223,14 +244,51 @@ final class UncertainWriteDefinition = Impl::UncertainWriteDefinition;
|
||||
|
||||
final class PhiNode = Impl::PhiNode;
|
||||
|
||||
class UntrackedDef extends Definition {
|
||||
private VarRead read;
|
||||
deprecated predicate ssaExplicitUpdate(SsaUpdate def, VariableUpdate upd) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
certainVariableUpdate(v, upd.getControlFlowNode(), bb, i) and
|
||||
getDestVar(upd) = def.getSourceVariable()
|
||||
)
|
||||
}
|
||||
|
||||
UntrackedDef() { ssaUntrackedDef(this, read) }
|
||||
deprecated predicate ssaUncertainImplicitUpdate(SsaImplicitUpdate def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
uncertainVariableUpdate(v, _, bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
string toString() { result = read.toString() }
|
||||
deprecated predicate ssaImplicitInit(WriteDefinition def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
hasEntryDef(v, bb) and
|
||||
i = -1
|
||||
)
|
||||
}
|
||||
|
||||
Location getLocation() { result = read.getLocation() }
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches `redef` without crossing another
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
deprecated predicate ssaDefReachesUncertainDef(TrackedSsaDef def, SsaUncertainImplicitUpdate redef) {
|
||||
Impl::uncertainWriteDefinitionInput(redef, def)
|
||||
}
|
||||
|
||||
deprecated VarRead getAUse(Definition def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
Impl::ssaDefReachesRead(v, def, bb, i) and
|
||||
result.getControlFlowNode() = bb.getNode(i) and
|
||||
result = v.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def) {
|
||||
Impl::ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
deprecated predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
|
||||
Impl::phiHasInputFromBlock(phi, inp, bb)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -247,24 +305,6 @@ private module Cached {
|
||||
result.getAnAccess() = upd.(UnaryAssignExpr).getExpr()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaExplicitUpdate(SsaUpdate def, VariableUpdate upd) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
certainVariableUpdate(v, upd.getControlFlowNode(), bb, i) and
|
||||
getDestVar(upd) = def.getSourceVariable()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaUntrackedDef(Definition def, VarRead read) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
untrackedFieldWrite(bb, i, v) and
|
||||
read.getControlFlowNode() = bb.getNode(i + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* The SSA construction for a field `f` relies on implicit update nodes at
|
||||
* every call site that conceivably could reach an update of the field.
|
||||
@@ -484,46 +524,20 @@ private module Cached {
|
||||
|
||||
overlay[global]
|
||||
cached
|
||||
predicate defUpdatesNamedField(SsaImplicitUpdate def, TrackedField f, Callable setter) {
|
||||
f = def.getSourceVariable() and
|
||||
updatesNamedField0(def.getCfgNode().asCall(), f, setter)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaUncertainImplicitUpdate(SsaImplicitUpdate def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
uncertainVariableUpdate(v, _, bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaImplicitInit(WriteDefinition def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
def.definesAt(v, bb, i) and
|
||||
hasEntryDef(v, bb) and
|
||||
i = 0
|
||||
)
|
||||
predicate defUpdatesNamedField(SsaImplicitWrite calldef, TrackedField f, Callable setter) {
|
||||
f = calldef.getSourceVariable() and
|
||||
updatesNamedField0(calldef.getControlFlowNode().asCall(), f, setter)
|
||||
}
|
||||
|
||||
/** Holds if `init` is a closure variable that captures the value of `capturedvar`. */
|
||||
cached
|
||||
predicate captures(SsaImplicitInit init, SsaVariable capturedvar) {
|
||||
predicate captures(SsaImplicitEntryDefinition init, SsaDefinition capturedvar) {
|
||||
exists(BasicBlock bb, int i |
|
||||
Impl::ssaDefReachesRead(_, capturedvar, bb, i) and
|
||||
Ssa::ssaDefReachesUncertainRead(_, capturedvar, bb, i) and
|
||||
variableCapture(capturedvar.getSourceVariable(), init.getSourceVariable(), bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches `redef` without crossing another
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
cached
|
||||
predicate ssaDefReachesUncertainDef(TrackedSsaDef def, SsaUncertainImplicitUpdate redef) {
|
||||
Impl::uncertainWriteDefinitionInput(redef, def)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value defined at `def` can reach `use` without passing through
|
||||
* any other uses, but possibly through phi nodes and uncertain implicit updates.
|
||||
@@ -536,25 +550,6 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
VarRead getAUse(Definition def) {
|
||||
exists(SsaSourceVariable v, BasicBlock bb, int i |
|
||||
Impl::ssaDefReachesRead(v, def, bb, i) and
|
||||
result.getControlFlowNode() = bb.getNode(i) and
|
||||
result = v.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def) {
|
||||
Impl::ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
|
||||
Impl::phiHasInputFromBlock(phi, inp, bb)
|
||||
}
|
||||
|
||||
cached
|
||||
module DataFlowIntegration {
|
||||
import DataFlowIntegrationImpl
|
||||
@@ -569,12 +564,14 @@ private module Cached {
|
||||
DataFlowIntegrationImpl::localMustFlowStep(v, nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
signature predicate guardChecksSig(Guards::Guard g, Expr e, boolean branch);
|
||||
signature predicate guardChecksSig(Guards::Guard g, Expr e, Guards::GuardValue gv);
|
||||
|
||||
cached // nothing is actually cached
|
||||
module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
private predicate guardChecksAdjTypes(Guards::Guards_v3::Guard g, Expr e, boolean branch) {
|
||||
guardChecks(g, e, branch)
|
||||
private predicate guardChecksAdjTypes(
|
||||
Guards::Guards_v3::Guard g, Expr e, Guards::GuardValue gv
|
||||
) {
|
||||
guardChecks(g, e, gv)
|
||||
}
|
||||
|
||||
private predicate guardChecksWithWrappers(
|
||||
@@ -591,6 +588,36 @@ private module Cached {
|
||||
|
||||
predicate getABarrierNode = getABarrierNodeImpl/0;
|
||||
}
|
||||
|
||||
bindingset[this]
|
||||
private signature class ParamSig;
|
||||
|
||||
private module WithParam<ParamSig P> {
|
||||
signature predicate guardChecksSig(Guards::Guard g, Expr e, Guards::GuardValue gv, P param);
|
||||
}
|
||||
|
||||
cached // nothing is actually cached
|
||||
module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guardChecks> {
|
||||
private predicate guardChecksAdjTypes(
|
||||
Guards::Guards_v3::Guard g, Expr e, Guards::GuardValue gv, P param
|
||||
) {
|
||||
guardChecks(g, e, gv, param)
|
||||
}
|
||||
|
||||
private predicate guardChecksWithWrappers(
|
||||
DataFlowIntegrationInput::Guard g, Definition def, Guards::GuardValue val, P param
|
||||
) {
|
||||
Guards::Guards_v3::ParameterizedValidationWrapper<P, guardChecksAdjTypes/4>::guardChecksDef(g,
|
||||
def, val, param)
|
||||
}
|
||||
|
||||
private Node getABarrierNodeImpl(P param) {
|
||||
result =
|
||||
DataFlowIntegrationImpl::BarrierGuardDefWithState<P, guardChecksWithWrappers/4>::getABarrierNode(param)
|
||||
}
|
||||
|
||||
predicate getABarrierNode = getABarrierNodeImpl/1;
|
||||
}
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -664,14 +691,12 @@ private module DataFlowIntegrationInput implements Impl::DataFlowIntegrationInpu
|
||||
}
|
||||
}
|
||||
|
||||
Expr getARead(Definition def) { result = getAUse(def) }
|
||||
Expr getARead(Definition def) { result = def.(SsaDefinition).getARead() }
|
||||
|
||||
predicate ssaDefHasSource(WriteDefinition def) {
|
||||
def instanceof SsaExplicitUpdate or def.(SsaImplicitInit).isParameterDefinition(_)
|
||||
}
|
||||
predicate ssaDefHasSource(WriteDefinition def) { def instanceof SsaExplicitWrite }
|
||||
|
||||
predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) {
|
||||
def instanceof SsaUncertainImplicitUpdate
|
||||
def instanceof SsaUncertainWrite
|
||||
}
|
||||
|
||||
class GuardValue = Guards::GuardValue;
|
||||
|
||||
@@ -8,7 +8,10 @@ private import java as J
|
||||
private import semmle.code.java.dataflow.SSA as Ssa
|
||||
private import semmle.code.java.dataflow.RangeUtils as RU
|
||||
|
||||
class SsaVariable = Ssa::SsaVariable;
|
||||
class SsaVariable extends Ssa::SsaDefinition {
|
||||
/** Gets a use of this variable. */
|
||||
Expr getAUse() { result = super.getARead() }
|
||||
}
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
|
||||
@@ -11,9 +11,11 @@ module Private {
|
||||
|
||||
class BasicBlock = BB::BasicBlock;
|
||||
|
||||
class SsaVariable = Ssa::SsaVariable;
|
||||
class SsaVariable extends Ssa::SsaDefinition {
|
||||
Expr getAUse() { result = super.getARead() }
|
||||
}
|
||||
|
||||
class SsaPhiNode = Ssa::SsaPhiNode;
|
||||
class SsaPhiNode = Ssa::SsaPhiDefinition;
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ module Private {
|
||||
|
||||
class Guard = G::Guards_v2::Guard;
|
||||
|
||||
class SsaVariable = Ssa::SsaVariable;
|
||||
class SsaVariable = Ssa::SsaDefinition;
|
||||
|
||||
class SsaPhiNode = Ssa::SsaPhiNode;
|
||||
class SsaPhiNode = Ssa::SsaPhiDefinition;
|
||||
|
||||
class VarAccess = J::VarAccess;
|
||||
|
||||
@@ -240,8 +240,8 @@ private module Impl {
|
||||
}
|
||||
|
||||
/** Returns the underlying variable update of the explicit SSA variable `v`. */
|
||||
VariableUpdate getExplicitSsaAssignment(SsaVariable v) {
|
||||
result = v.(SsaExplicitUpdate).getDefiningExpr()
|
||||
VariableUpdate getExplicitSsaAssignment(SsaDefinition v) {
|
||||
result = v.(SsaExplicitWrite).getDefiningExpr()
|
||||
}
|
||||
|
||||
/** Returns the assignment of the variable update `def`. */
|
||||
@@ -267,13 +267,12 @@ private module Impl {
|
||||
}
|
||||
|
||||
/** Gets the variable underlying the implicit SSA variable `v`. */
|
||||
Variable getImplicitSsaDeclaration(SsaVariable v) {
|
||||
result = v.(SsaImplicitUpdate).getSourceVariable().getVariable() or
|
||||
result = v.(SsaImplicitInit).getSourceVariable().getVariable()
|
||||
Variable getImplicitSsaDeclaration(SsaDefinition v) {
|
||||
result = v.(SsaImplicitWrite).getSourceVariable().getVariable()
|
||||
}
|
||||
|
||||
/** Holds if the variable underlying the implicit SSA variable `v` is not a field. */
|
||||
predicate nonFieldImplicitSsaDefinition(SsaImplicitInit v) { v.isParameterDefinition(_) }
|
||||
predicate nonFieldImplicitSsaDefinition(SsaParameterInit v) { any() }
|
||||
|
||||
/** Returned an expression that is assigned to `f`. */
|
||||
Expr getAssignedValueToField(Field f) {
|
||||
@@ -324,7 +323,7 @@ private module Impl {
|
||||
result = e.(CastingExpr).getExpr()
|
||||
}
|
||||
|
||||
Expr getARead(SsaVariable v) { result = v.getAUse() }
|
||||
Expr getARead(SsaDefinition v) { result = v.getARead() }
|
||||
|
||||
Field getField(FieldAccess fa) { result = fa.getField() }
|
||||
|
||||
|
||||
@@ -8,14 +8,14 @@ private import semmle.code.java.dataflow.SSA as Ssa
|
||||
private import semmle.code.java.controlflow.BasicBlocks as BB
|
||||
private import SsaReadPositionCommon
|
||||
|
||||
class SsaVariable = Ssa::SsaVariable;
|
||||
class SsaVariable = Ssa::SsaDefinition;
|
||||
|
||||
class SsaPhiNode = Ssa::SsaPhiNode;
|
||||
class SsaPhiNode = Ssa::SsaPhiDefinition;
|
||||
|
||||
class BasicBlock = BB::BasicBlock;
|
||||
|
||||
/** Gets a basic block in which SSA variable `v` is read. */
|
||||
BasicBlock getAReadBasicBlock(SsaVariable v) { result = v.getAUse().getBasicBlock() }
|
||||
BasicBlock getAReadBasicBlock(SsaVariable v) { result = v.getARead().getBasicBlock() }
|
||||
|
||||
private predicate id(BB::ExprParent x, BB::ExprParent y) { x = y }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import java
|
||||
private import VirtualDispatch
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.dataflow.internal.DataFlowUtil as DataFlow
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.InstanceAccess
|
||||
@@ -162,14 +162,28 @@ private module TypeTrackingSteps {
|
||||
storeContents = loadContents
|
||||
}
|
||||
|
||||
predicate simpleLocalSmallStep(Node n1, Node n2) {
|
||||
exists(BaseSsaVariable v, BaseSsaVariable def |
|
||||
def.(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
or
|
||||
def.(BaseSsaImplicitInit).isParameterDefinition(n1.asParameter())
|
||||
/**
|
||||
* Holds if `n` is a read of an SSA variable that is ultimately defined by `def`.
|
||||
*
|
||||
* This includes reads of captured variables even though they are not technically
|
||||
* local steps, but treating them as local is useful for type tracking purposes.
|
||||
*/
|
||||
private predicate readsSsa(Node n, Base::SsaDefinition def) {
|
||||
exists(Base::SsaDefinition v |
|
||||
v.getAnUltimateDefinition() = def or
|
||||
v.(Base::SsaCapturedDefinition).getAnUltimateCapturedDefinition() = def
|
||||
|
|
||||
v.getAnUltimateDefinition() = def and
|
||||
v.getAUse() = n2.asExpr()
|
||||
v.getARead() = n.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate simpleLocalSmallStep(Node n1, Node n2) {
|
||||
exists(Base::SsaDefinition def |
|
||||
def.(Base::SsaExplicitWrite).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
or
|
||||
def.(Base::SsaParameterInit).getParameter() = n1.asParameter()
|
||||
|
|
||||
readsSsa(n2, def)
|
||||
)
|
||||
or
|
||||
exists(Callable c | n1.(DataFlow::InstanceParameterNode).getCallable() = c |
|
||||
@@ -220,11 +234,10 @@ private module TypeTrackingSteps {
|
||||
n2.asExpr() = get
|
||||
)
|
||||
or
|
||||
exists(EnhancedForStmt for, BaseSsaVariable ssa, BaseSsaVariable def |
|
||||
for.getVariable() = def.(BaseSsaUpdate).getDefiningExpr() and
|
||||
exists(EnhancedForStmt for, Base::SsaDefinition def |
|
||||
for.getVariable() = def.(Base::SsaExplicitWrite).getDefiningExpr() and
|
||||
for.getExpr() = v.getAnAccess() and
|
||||
ssa.getAnUltimateDefinition() = def and
|
||||
ssa.getAUse() = n2.asExpr()
|
||||
readsSsa(n2, def)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -259,16 +272,15 @@ private module TypeTrackingSteps {
|
||||
}
|
||||
|
||||
predicate loadStep(Node n1, LocalSourceNode n2, Content f) {
|
||||
exists(BaseSsaVariable v, BaseSsaVariable def |
|
||||
exists(Base::SsaDefinition def |
|
||||
exists(EnhancedForStmt for |
|
||||
for.getVariable() = def.(BaseSsaUpdate).getDefiningExpr() and
|
||||
for.getVariable() = def.(Base::SsaExplicitWrite).getDefiningExpr() and
|
||||
for.getExpr() = n1.asExpr() and
|
||||
n1.getType() instanceof Array and
|
||||
f = ContentArray()
|
||||
)
|
||||
|
|
||||
v.getAnUltimateDefinition() = def and
|
||||
v.getAUse() = n2.asExpr()
|
||||
readsSsa(n2, def)
|
||||
)
|
||||
or
|
||||
n2.asExpr().(ArrayAccess).getArray() = n1.asExpr()
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import java
|
||||
private import VirtualDispatch
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.java.dataflow.internal.ContainerFlow
|
||||
@@ -71,21 +71,24 @@ private predicate callFlowStep(Node n1, Node n2) {
|
||||
* flow, calls, returns, fields, array reads or writes, or container taint steps.
|
||||
*/
|
||||
private predicate step(Node n1, Node n2) {
|
||||
exists(BaseSsaVariable v, BaseSsaVariable def |
|
||||
def.(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
exists(Base::SsaDefinition v, Base::SsaDefinition def |
|
||||
def.(Base::SsaExplicitWrite).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr()
|
||||
or
|
||||
def.(BaseSsaImplicitInit).isParameterDefinition(n1.asParameter())
|
||||
def.(Base::SsaParameterInit).getParameter() = n1.asParameter()
|
||||
or
|
||||
exists(EnhancedForStmt for |
|
||||
for.getVariable() = def.(BaseSsaUpdate).getDefiningExpr() and
|
||||
for.getVariable() = def.(Base::SsaExplicitWrite).getDefiningExpr() and
|
||||
for.getExpr() = n1.asExpr()
|
||||
)
|
||||
|
|
||||
v.getAnUltimateDefinition() = def and
|
||||
v.getAUse() = n2.asExpr()
|
||||
(
|
||||
v.(Base::SsaCapturedDefinition).getAnUltimateCapturedDefinition() = def or
|
||||
v.getAnUltimateDefinition() = def
|
||||
) and
|
||||
v.getARead() = n2.asExpr()
|
||||
)
|
||||
or
|
||||
baseSsaAdjacentUseUse(n1.asExpr(), n2.asExpr())
|
||||
Base::baseSsaAdjacentUseUse(n1.asExpr(), n2.asExpr())
|
||||
or
|
||||
exists(Callable c | n1.(InstanceParameterNode).getCallable() = c |
|
||||
exists(InstanceAccess ia |
|
||||
|
||||
@@ -7,7 +7,7 @@ import java
|
||||
import semmle.code.java.dataflow.TypeFlow
|
||||
private import DispatchFlow as DispatchFlow
|
||||
private import ObjFlow as ObjFlow
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dispatch.internal.Unification
|
||||
|
||||
@@ -194,10 +194,10 @@ private module Dispatch {
|
||||
*/
|
||||
private predicate impossibleDispatchTarget(MethodCall source, Method tgt) {
|
||||
tgt = viableImpl_v1_cand(source) and
|
||||
exists(Guard typeTest, BaseSsaVariable v, Expr q, RefType t |
|
||||
exists(Guard typeTest, Base::SsaDefinition v, Expr q, RefType t |
|
||||
source.getQualifier() = q and
|
||||
v.getAUse() = q and
|
||||
typeTest.appliesTypeTest(v.getAUse(), t, false) and
|
||||
v.getARead() = q and
|
||||
typeTest.appliesTypeTest(v.getARead(), t, false) and
|
||||
guardControls_v1(typeTest, q.getBasicBlock(), false) and
|
||||
tgt.getDeclaringType().getSourceDeclaration().getASourceSupertype*() = t.getErasure()
|
||||
)
|
||||
|
||||
@@ -41,11 +41,13 @@ private class InputStreamWrapperCapturedJumpStep extends AdditionalTaintStep {
|
||||
*/
|
||||
private class InputStreamWrapperCapturedLocalStep extends AdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(InputStreamRead m, NestedClass wrapper, SsaVariable captured, SsaImplicitInit capturer |
|
||||
exists(
|
||||
InputStreamRead m, NestedClass wrapper, SsaDefinition captured, SsaCapturedDefinition capturer
|
||||
|
|
||||
wrapper.getASourceSupertype+() instanceof TypeInputStream and
|
||||
m.getDeclaringType() = wrapper and
|
||||
capturer.captures(captured) and
|
||||
TaintTracking::localTaint(DataFlow::exprNode(capturer.getAFirstUse()),
|
||||
TaintTracking::localTaint(DataFlow::exprNode(ssaGetAFirstUse(capturer)),
|
||||
any(DataFlow::PostUpdateNode pun |
|
||||
pun.getPreUpdateNode().asExpr() = m.getParameter(0).getAnAccess()
|
||||
)) and
|
||||
@@ -55,9 +57,9 @@ private class InputStreamWrapperCapturedLocalStep extends AdditionalTaintStep {
|
||||
.getASourceSupertype*()
|
||||
.getSourceDeclaration() = wrapper
|
||||
|
|
||||
n1.asExpr() = captured.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource()
|
||||
n1.asExpr() = captured.(SsaExplicitWrite).getDefiningExpr().(VariableAssign).getSource()
|
||||
or
|
||||
captured.(SsaImplicitInit).isParameterDefinition(n1.asParameter())
|
||||
captured.(SsaParameterInit).getParameter() = n1.asParameter()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,10 +107,7 @@ class XmlAccessType extends EnumConstant {
|
||||
*/
|
||||
class JaxbMemberAnnotation extends JaxbAnnotationType {
|
||||
JaxbMemberAnnotation() {
|
||||
this.hasName("XmlElement") or
|
||||
this.hasName("XmlAttribute") or
|
||||
this.hasName("XmlElementRefs") or
|
||||
this.hasName("XmlElements")
|
||||
this.hasName(["XmlElement", "XmlAttribute", "XmlElementRefs", "XmlElements"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,46 @@ module;
|
||||
|
||||
import java
|
||||
|
||||
/** The class `java.util.regex.Matcher`. */
|
||||
class TypeRegexMatcher extends Class {
|
||||
TypeRegexMatcher() { this.hasQualifiedName("java.util.regex", "Matcher") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `matches` method of `java.util.regex.Matcher`.
|
||||
*/
|
||||
class MatcherMatchesMethod extends Method {
|
||||
MatcherMatchesMethod() {
|
||||
this.getDeclaringType() instanceof TypeRegexMatcher and
|
||||
this.hasName("matches")
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `java.util.regex.Pattern`. */
|
||||
class TypeRegexPattern extends Class {
|
||||
TypeRegexPattern() { this.hasQualifiedName("java.util.regex", "Pattern") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `matches` method of `java.util.regex.Pattern`.
|
||||
*/
|
||||
class PatternMatchesMethod extends Method {
|
||||
PatternMatchesMethod() {
|
||||
this.getDeclaringType() instanceof TypeRegexPattern and
|
||||
this.hasName("matches")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `matcher` method of `java.util.regex.Pattern`.
|
||||
*/
|
||||
class PatternMatcherMethod extends Method {
|
||||
PatternMatcherMethod() {
|
||||
this.getDeclaringType() instanceof TypeRegexPattern and
|
||||
this.hasName("matcher")
|
||||
}
|
||||
}
|
||||
|
||||
/** The `quote` method of the `java.util.regex.Pattern` class. */
|
||||
class PatternQuoteMethod extends Method {
|
||||
PatternQuoteMethod() {
|
||||
|
||||
@@ -262,10 +262,10 @@ private predicate reaches(Expr src, Argument arg) {
|
||||
any(StartComponentMethodCall ma).getIntentArg() = arg and
|
||||
src = arg
|
||||
or
|
||||
exists(Expr mid, BaseSsa::BaseSsaVariable ssa, BaseSsa::BaseSsaUpdate upd |
|
||||
exists(Expr mid, BaseSsa::SsaDefinition ssa, BaseSsa::SsaExplicitWrite upd |
|
||||
reaches(mid, arg) and
|
||||
mid = ssa.getAUse() and
|
||||
upd = ssa.getAnUltimateLocalDefinition() and
|
||||
mid = ssa.getARead() and
|
||||
upd = ssa.getAnUltimateDefinition() and
|
||||
src = upd.getDefiningExpr().(VariableAssign).getSource()
|
||||
)
|
||||
or
|
||||
|
||||
@@ -26,9 +26,7 @@ private module TypeLiteralToParseAsFlowConfig implements DataFlow::ConfigSig {
|
||||
|
||||
private module TypeLiteralToParseAsFlow = DataFlow::Global<TypeLiteralToParseAsFlowConfig>;
|
||||
|
||||
private TypeLiteral getSourceWithFlowToParseAs() {
|
||||
TypeLiteralToParseAsFlow::flow(DataFlow::exprNode(result), _)
|
||||
}
|
||||
private TypeLiteral getSourceWithFlowToParseAs() { TypeLiteralToParseAsFlow::flowFromExpr(result) }
|
||||
|
||||
/** A field that is deserialized by `HttpResponse.parseAs`. */
|
||||
class HttpResponseParseAsDeserializableField extends DeserializableField {
|
||||
|
||||
@@ -14,14 +14,3 @@ class HudsonWebMethod extends Method {
|
||||
this.getDeclaringType().getASourceSupertype*().hasQualifiedName("hudson.model", "Descriptor")
|
||||
}
|
||||
}
|
||||
|
||||
private class HudsonUtilXssSanitizer extends XssSanitizer {
|
||||
HudsonUtilXssSanitizer() {
|
||||
this.asExpr()
|
||||
.(MethodCall)
|
||||
.getMethod()
|
||||
// Not including xmlEscape because it only accounts for >, <, and &.
|
||||
// It does not account for ", or ', which makes it an incomplete XSS sanitizer.
|
||||
.hasQualifiedName("hudson", "Util", "escape")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ private module TypeLiteralToJacksonDatabindFlow =
|
||||
DataFlow::Global<TypeLiteralToJacksonDatabindFlowConfig>;
|
||||
|
||||
private TypeLiteral getSourceWithFlowToJacksonDatabind() {
|
||||
TypeLiteralToJacksonDatabindFlow::flow(DataFlow::exprNode(result), _)
|
||||
TypeLiteralToJacksonDatabindFlow::flowFromExpr(result)
|
||||
}
|
||||
|
||||
/** A type whose values are explicitly deserialized in a call to a Jackson method. */
|
||||
|
||||
@@ -677,7 +677,7 @@ Type inheritsMatchingMethodExceptThrows(SessionEjb ejb, Method m) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ejb` inherits an `ejbCreate` or `@Init` method matching `create` method `m`.
|
||||
* Holds if `ejb` inherits an `ejbCreate` or `@Init` method matching `create` method `icm`.
|
||||
* (Ignores `throws` clauses.)
|
||||
*/
|
||||
predicate inheritsMatchingCreateMethodIgnoreThrows(
|
||||
@@ -704,7 +704,7 @@ predicate inheritsMatchingCreateMethodIgnoreThrows(
|
||||
}
|
||||
|
||||
/**
|
||||
* If `ejb` inherits an `ejbCreate` or `@Init` method matching `create` method `m` except for the `throws` clause,
|
||||
* If `ejb` inherits an `ejbCreate` or `@Init` method matching `create` method `icm` except for the `throws` clause,
|
||||
* then return any type in the `throws` clause that does not match.
|
||||
*/
|
||||
Type inheritsMatchingCreateMethodExceptThrows(StatefulSessionEjb ejb, EjbInterfaceCreateMethod icm) {
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/** Classes and predicates for reasoning about the `owasp.easpi` package. */
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
* The `org.owasp.esapi.Validator` interface.
|
||||
*/
|
||||
class EsapiValidator extends RefType {
|
||||
EsapiValidator() { this.hasQualifiedName("org.owasp.esapi", "Validator") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods of `org.owasp.esapi.Validator` which validate data.
|
||||
*/
|
||||
class EsapiIsValidMethod extends Method {
|
||||
EsapiIsValidMethod() {
|
||||
this.getDeclaringType() instanceof EsapiValidator and
|
||||
this.hasName([
|
||||
"isValidCreditCard", "isValidDate", "isValidDirectoryPath", "isValidDouble",
|
||||
"isValidFileContent", "isValidFileName", "isValidInput", "isValidInteger",
|
||||
"isValidListItem", "isValidNumber", "isValidPrintable", "isValidRedirectLocation",
|
||||
"isValidSafeHTML", "isValidURI"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods of `org.owasp.esapi.Validator` which return validated data.
|
||||
*/
|
||||
class EsapiGetValidMethod extends Method {
|
||||
EsapiGetValidMethod() {
|
||||
this.getDeclaringType() instanceof EsapiValidator and
|
||||
this.hasName([
|
||||
"getValidCreditCard", "getValidDate", "getValidDirectoryPath", "getValidDouble",
|
||||
"getValidFileContent", "getValidFileName", "getValidInput", "getValidInteger",
|
||||
"getValidListItem", "getValidNumber", "getValidPrintable", "getValidRedirectLocation",
|
||||
"getValidSafeHTML", "getValidURI"
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -187,13 +187,10 @@ class SpringServletInputAnnotation extends Annotation {
|
||||
a = this.getType() and
|
||||
a.getPackage().getName() = "org.springframework.web.bind.annotation"
|
||||
|
|
||||
a.hasName("MatrixVariable") or
|
||||
a.hasName("RequestParam") or
|
||||
a.hasName("RequestHeader") or
|
||||
a.hasName("CookieValue") or
|
||||
a.hasName("RequestPart") or
|
||||
a.hasName("PathVariable") or
|
||||
a.hasName("RequestBody")
|
||||
a.hasName([
|
||||
"MatrixVariable", "RequestParam", "RequestHeader", "CookieValue", "RequestPart",
|
||||
"PathVariable", "RequestBody"
|
||||
])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,12 +40,7 @@ class Struts2ActionClass extends Class {
|
||||
getStrutsMapperClass(this) = "org.apache.struts2.dispatcher.mapper.RestfulActionMapper"
|
||||
then
|
||||
// The "Restful" action mapper maps rest APIs to specific methods
|
||||
result.hasName("index") or
|
||||
result.hasName("create") or
|
||||
result.hasName("editNew") or
|
||||
result.hasName("view") or
|
||||
result.hasName("remove") or
|
||||
result.hasName("update")
|
||||
result.hasName(["index", "create", "editNew", "view", "remove", "update"])
|
||||
else
|
||||
if
|
||||
getStrutsMapperClass(this) = "org.apache.struts2.rest.RestActionMapper" or
|
||||
@@ -53,13 +48,7 @@ class Struts2ActionClass extends Class {
|
||||
then
|
||||
// The "Rest" action mapper is provided with the rest plugin, and maps rest APIs to specific
|
||||
// methods based on a "ruby-on-rails" style.
|
||||
result.hasName("index") or
|
||||
result.hasName("show") or
|
||||
result.hasName("edit") or
|
||||
result.hasName("editNew") or
|
||||
result.hasName("create") or
|
||||
result.hasName("update") or
|
||||
result.hasName("destroy")
|
||||
result.hasName(["index", "show", "edit", "editNew", "create", "update", "destroy"])
|
||||
else
|
||||
if exists(getStrutsMapperClass(this))
|
||||
then
|
||||
|
||||
46
java/ql/lib/semmle/code/java/internal/OverlayXml.qll
Normal file
46
java/ql/lib/semmle/code/java/internal/OverlayXml.qll
Normal file
@@ -0,0 +1,46 @@
|
||||
overlay[local]
|
||||
module;
|
||||
|
||||
/**
|
||||
* A local predicate that always holds for the overlay variant and never holds for the base variant.
|
||||
* This is used to define local predicates that behave differently for the base and overlay variant.
|
||||
*/
|
||||
private predicate isOverlay() { databaseMetadata("isOverlay", "true") }
|
||||
|
||||
private string getXmlFile(@xmllocatable locatable) {
|
||||
exists(@location_default location, @file file | xmllocations(locatable, location) |
|
||||
locations_default(location, file, _, _, _, _) and
|
||||
files(file, result)
|
||||
)
|
||||
}
|
||||
|
||||
private string getXmlFileInBase(@xmllocatable locatable) {
|
||||
not isOverlay() and
|
||||
result = getXmlFile(locatable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `file` was extracted as part of the overlay and was extracted by the HTML/XML
|
||||
* extractor.
|
||||
*/
|
||||
private predicate overlayXmlExtracted(string file) {
|
||||
isOverlay() and
|
||||
exists(@xmllocatable locatable |
|
||||
not files(locatable, _) and not xmlNs(locatable, _, _, _) and file = getXmlFile(locatable)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given XML `locatable` should be discarded, because it is part of the overlay base
|
||||
* and is in a file that was also extracted as part of the overlay database.
|
||||
*/
|
||||
overlay[discard_entity]
|
||||
private predicate discardXmlLocatable(@xmllocatable locatable) {
|
||||
exists(string file | file = getXmlFileInBase(locatable) |
|
||||
overlayChangedFiles(file)
|
||||
or
|
||||
// The HTML/XML extractor is currently not incremental and may extract more files than those
|
||||
// included in overlayChangedFiles.
|
||||
overlayXmlExtracted(file)
|
||||
)
|
||||
}
|
||||
@@ -164,7 +164,7 @@ private module RegexFlowConfig implements DataFlow::ConfigSig {
|
||||
private module RegexFlow = DataFlow::Global<RegexFlowConfig>;
|
||||
|
||||
private predicate usedAsRegexImpl(StringLiteral regex, string mode, boolean match_full_string) {
|
||||
RegexFlow::flow(DataFlow::exprNode(regex), _) and
|
||||
RegexFlow::flowFromExpr(regex) and
|
||||
mode = "None" and // TODO: proper mode detection
|
||||
(if matchesFullString(regex) then match_full_string = true else match_full_string = false)
|
||||
}
|
||||
|
||||
@@ -38,9 +38,13 @@ private predicate isShell(Expr ex) {
|
||||
cmd.regexpMatch(".*(sh|javac?|python[23]?|osascript|cmd)(\\.exe)?$")
|
||||
)
|
||||
or
|
||||
exists(SsaVariable ssa |
|
||||
ex = ssa.getAUse() and
|
||||
isShell(ssa.getAnUltimateDefinition().(SsaExplicitUpdate).getDefiningExpr())
|
||||
exists(SsaDefinition ssa, SsaExplicitWrite def |
|
||||
ex = ssa.getARead() and
|
||||
isShell(def.getDefiningExpr())
|
||||
|
|
||||
ssa.getAnUltimateDefinition() = def
|
||||
or
|
||||
ssa.(SsaCapturedDefinition).getAnUltimateCapturedDefinition() = def
|
||||
)
|
||||
or
|
||||
isShell(ex.(Assignment).getRhs())
|
||||
@@ -61,17 +65,17 @@ private class ListOfStringType extends CollectionType {
|
||||
/**
|
||||
* A variable that could be used as a list of arguments to a command.
|
||||
*/
|
||||
private class CommandArgumentList extends SsaExplicitUpdate {
|
||||
private class CommandArgumentList extends SsaExplicitWrite {
|
||||
CommandArgumentList() {
|
||||
this.getSourceVariable().getType() instanceof ListOfStringType and
|
||||
forex(CollectionMutation ma | ma.getQualifier() = this.getAUse() |
|
||||
forex(CollectionMutation ma | ma.getQualifier() = this.getARead() |
|
||||
ma.getMethod().getName().matches("add%")
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a use of the variable for which the list could be empty. */
|
||||
private VarRead getAUseBeforeFirstAdd() {
|
||||
result = this.getAFirstUse()
|
||||
result = ssaGetAFirstUse(this)
|
||||
or
|
||||
exists(VarRead mid |
|
||||
mid = this.getAUseBeforeFirstAdd() and
|
||||
@@ -87,7 +91,7 @@ private class CommandArgumentList extends SsaExplicitUpdate {
|
||||
* Gets an addition to this list, i.e. a call to an `add` or `addAll` method.
|
||||
*/
|
||||
MethodCall getAnAdd() {
|
||||
result.getQualifier() = this.getAUse() and
|
||||
result.getQualifier() = this.getARead() and
|
||||
result.getMethod().getName().matches("add%")
|
||||
}
|
||||
|
||||
@@ -121,10 +125,10 @@ private predicate arrayVarWrite(ArrayAccess acc) { exists(Assignment a | a.getDe
|
||||
/**
|
||||
* A variable that could be an array of arguments to a command.
|
||||
*/
|
||||
private class CommandArgumentArray extends SsaExplicitUpdate {
|
||||
private class CommandArgumentArray extends SsaExplicitWrite {
|
||||
CommandArgumentArray() {
|
||||
this.getSourceVariable().getType() instanceof ArrayOfStringType and
|
||||
forall(ArrayAccess a | a.getArray() = this.getAUse() and arrayVarWrite(a) |
|
||||
forall(ArrayAccess a | a.getArray() = this.getARead() and arrayVarWrite(a) |
|
||||
a.getIndexExpr() instanceof CompileTimeConstantExpr
|
||||
)
|
||||
}
|
||||
@@ -133,7 +137,7 @@ private class CommandArgumentArray extends SsaExplicitUpdate {
|
||||
Expr getAWrite(int index, VarRead use) {
|
||||
exists(Assignment a, ArrayAccess acc |
|
||||
acc.getArray() = use and
|
||||
use = this.getAUse() and
|
||||
use = this.getARead() and
|
||||
index = acc.getIndexExpr().(CompileTimeConstantExpr).getIntValue() and
|
||||
acc = a.getDest() and
|
||||
result = a.getRhs()
|
||||
@@ -150,7 +154,7 @@ private class CommandArgumentArray extends SsaExplicitUpdate {
|
||||
private class CommandArgArrayImmutableFirst extends CommandArgumentArray {
|
||||
CommandArgArrayImmutableFirst() {
|
||||
(exists(this.getAWrite(0)) or exists(firstElementOf(this.getDefiningExpr()))) and
|
||||
forall(VarRead use | exists(this.getAWrite(0, use)) | use = this.getAFirstUse())
|
||||
forall(VarRead use | exists(this.getAWrite(0, use)) | use = ssaGetAFirstUse(this))
|
||||
}
|
||||
|
||||
/** Gets the first element of this array. */
|
||||
@@ -173,7 +177,9 @@ private Expr firstElementOf(Expr arr) {
|
||||
or
|
||||
result = firstElementOf(arr.(LocalVariableDeclExpr).getInit())
|
||||
or
|
||||
exists(CommandArgArrayImmutableFirst caa | arr = caa.getAUse() | result = caa.getFirstElement())
|
||||
exists(CommandArgArrayImmutableFirst caa | arr = caa.getARead() |
|
||||
result = caa.getFirstElement()
|
||||
)
|
||||
or
|
||||
exists(MethodCall ma, Method m |
|
||||
arr = ma and
|
||||
|
||||
@@ -51,7 +51,7 @@ private module VerifiedIntentFlow = DataFlow::Global<VerifiedIntentConfig>;
|
||||
/** An `onReceive` method that doesn't verify the action of the intent it receives. */
|
||||
private class UnverifiedOnReceiveMethod extends OnReceiveMethod {
|
||||
UnverifiedOnReceiveMethod() {
|
||||
not VerifiedIntentFlow::flow(DataFlow::parameterNode(this.getIntentParameter()), _) and
|
||||
not VerifiedIntentFlow::flowFrom(DataFlow::parameterNode(this.getIntentParameter())) and
|
||||
// Empty methods do not need to be verified since they do not perform any actions.
|
||||
this.getBody().getNumStmt() > 0
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import java
|
||||
private import codeql.typeflow.UniversalFlow as UniversalFlow
|
||||
private import semmle.code.java.Collections
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA as Base
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.TypeFlow
|
||||
private import semmle.code.java.dispatch.VirtualDispatch
|
||||
@@ -115,7 +115,7 @@ private predicate nodeWithAddition(FlowNode n, Variable v) {
|
||||
n.asField() = v
|
||||
or
|
||||
n.asSsa().getSourceVariable().getVariable() = v and
|
||||
(n.asSsa() instanceof BaseSsaUpdate or n.asSsa().(BaseSsaImplicitInit).isParameterDefinition(_))
|
||||
n.asSsa() instanceof Base::SsaExplicitWrite
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -46,14 +46,14 @@ class RightShiftOp extends Expr {
|
||||
}
|
||||
|
||||
private predicate boundedRead(VarRead read) {
|
||||
exists(SsaVariable v, ConditionBlock cb, ComparisonExpr comp, boolean testIsTrue |
|
||||
read = v.getAUse() and
|
||||
exists(SsaDefinition v, ConditionBlock cb, ComparisonExpr comp, boolean testIsTrue |
|
||||
read = v.getARead() and
|
||||
cb.controls(read.getBasicBlock(), testIsTrue) and
|
||||
cb.getCondition() = comp
|
||||
|
|
||||
comp.getLesserOperand() = v.getAUse() and testIsTrue = true
|
||||
comp.getLesserOperand() = v.getARead() and testIsTrue = true
|
||||
or
|
||||
comp.getGreaterOperand() = v.getAUse() and testIsTrue = false
|
||||
comp.getGreaterOperand() = v.getARead() and testIsTrue = false
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ module;
|
||||
|
||||
import java
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.frameworks.kotlin.IO
|
||||
@@ -288,19 +289,8 @@ private Method getSourceMethod(Method m) {
|
||||
result = m
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer that protects against path injection vulnerabilities
|
||||
* by extracting the final component of the user provided path.
|
||||
*
|
||||
* TODO: convert this class to models-as-data if sanitizer support is added
|
||||
*/
|
||||
private class FileGetNameSanitizer extends PathInjectionSanitizer {
|
||||
FileGetNameSanitizer() {
|
||||
exists(MethodCall mc |
|
||||
mc.getMethod().hasQualifiedName("java.io", "File", "getName") and
|
||||
this.asExpr() = mc
|
||||
)
|
||||
}
|
||||
private class DefaultPathInjectionSanitizer extends PathInjectionSanitizer {
|
||||
DefaultPathInjectionSanitizer() { barrierNode(this, "path-injection") }
|
||||
}
|
||||
|
||||
/** Holds if `g` is a guard that checks for `..` components. */
|
||||
|
||||
@@ -118,25 +118,8 @@ private class ContainsUrlSanitizer extends RequestForgerySanitizer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A check that the URL is relative, and therefore safe for URL redirects.
|
||||
*/
|
||||
private predicate isRelativeUrlSanitizer(Guard guard, Expr e, boolean branch) {
|
||||
guard =
|
||||
any(MethodCall call |
|
||||
call.getMethod().hasQualifiedName("java.net", "URI", "isAbsolute") and
|
||||
e = call.getQualifier() and
|
||||
branch = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A check that the URL is relative, and therefore safe for URL redirects.
|
||||
*/
|
||||
private class RelativeUrlSanitizer extends RequestForgerySanitizer {
|
||||
RelativeUrlSanitizer() {
|
||||
this = DataFlow::BarrierGuard<isRelativeUrlSanitizer/3>::getABarrierNode()
|
||||
}
|
||||
private class DefaultRequestForgerySanitizer extends RequestForgerySanitizer {
|
||||
DefaultRequestForgerySanitizer() { barrierNode(this, "request-forgery") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,22 +149,7 @@ private class HostComparisonSanitizer extends RequestForgerySanitizer {
|
||||
}
|
||||
|
||||
/**
|
||||
* A qualifier in a call to a `.matches()` method that is a sanitizer for URL redirects.
|
||||
*
|
||||
* Matches any method call where the method is named `matches`.
|
||||
* A comparison with a regular expression that is a sanitizer for URL redirects.
|
||||
*/
|
||||
private predicate isMatchesSanitizer(Guard guard, Expr e, boolean branch) {
|
||||
guard =
|
||||
any(MethodCall method |
|
||||
method.getMethod().getName() = "matches" and
|
||||
e = method.getQualifier() and
|
||||
branch = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A qualifier in a call to `.matches()` that is a sanitizer for URL redirects.
|
||||
*/
|
||||
private class MatchesSanitizer extends RequestForgerySanitizer {
|
||||
MatchesSanitizer() { this = DataFlow::BarrierGuard<isMatchesSanitizer/3>::getABarrierNode() }
|
||||
}
|
||||
private class RegexpCheckRequestForgerySanitizer extends RequestForgerySanitizer instanceof RegexpCheckBarrier
|
||||
{ }
|
||||
|
||||
@@ -3,7 +3,9 @@ overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.frameworks.Regex
|
||||
|
||||
/**
|
||||
* A node whose type is a simple type unlikely to carry taint, such as primitives and their boxed counterparts,
|
||||
@@ -29,3 +31,44 @@ class SimpleTypeSanitizer extends DataFlow::Node {
|
||||
this.getType() instanceof EnumType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `guard` holds with branch `branch` if `e` matches a regular expression.
|
||||
*
|
||||
* This is overapproximate: we do not attempt to reason about the correctness of the regexp.
|
||||
*
|
||||
* Use this if you want to define a derived `DataFlow::BarrierGuard` without
|
||||
* make the type recursive. Otherwise use `RegexpCheckBarrier`.
|
||||
*/
|
||||
predicate regexpMatchGuardChecks(Guard guard, Expr e, boolean branch) {
|
||||
exists(Method method, MethodCall mc |
|
||||
method = mc.getMethod() and
|
||||
guard = mc and
|
||||
branch = true
|
||||
|
|
||||
// `String.matches` and other `matches` methods.
|
||||
method.getName() = "matches" and
|
||||
e = mc.getQualifier()
|
||||
or
|
||||
method instanceof PatternMatchesMethod and
|
||||
e = mc.getArgument(1)
|
||||
or
|
||||
method instanceof MatcherMatchesMethod and
|
||||
exists(MethodCall matcherCall |
|
||||
matcherCall.getMethod() instanceof PatternMatcherMethod and
|
||||
e = matcherCall.getArgument(0) and
|
||||
DataFlow::localExprFlow(matcherCall, mc.getQualifier())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A check against a regular expression, considered as a barrier guard.
|
||||
*
|
||||
* This is overapproximate: we do not attempt to reason about the correctness of the regexp.
|
||||
*/
|
||||
class RegexpCheckBarrier extends DataFlow::Node {
|
||||
RegexpCheckBarrier() {
|
||||
this = DataFlow::BarrierGuard<regexpMatchGuardChecks/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,14 @@ import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.security.SensitiveActions
|
||||
import semmle.code.java.frameworks.android.Compose
|
||||
private import semmle.code.java.security.Sanitizers
|
||||
private import semmle.code.java.dataflow.RangeAnalysis
|
||||
|
||||
/** A data flow source node for sensitive logging sources. */
|
||||
abstract class SensitiveLoggerSource extends DataFlow::Node { }
|
||||
|
||||
/** A data flow barrier node for sensitive logging sanitizers. */
|
||||
abstract class SensitiveLoggerBarrier extends DataFlow::Node { }
|
||||
|
||||
/** A variable that may hold sensitive information, judging by its name. */
|
||||
class VariableWithSensitiveName extends Variable {
|
||||
VariableWithSensitiveName() {
|
||||
@@ -40,17 +44,89 @@ private class TypeType extends RefType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer that may remove sensitive information from a string before logging.
|
||||
*
|
||||
* It allows for substring operations taking the first N (or last N, for Kotlin) characters, limited to 7 or fewer.
|
||||
*/
|
||||
private class PrefixSuffixBarrier extends SensitiveLoggerBarrier {
|
||||
PrefixSuffixBarrier() {
|
||||
exists(MethodCall mc, Method m, int limit |
|
||||
limit = 7 and
|
||||
mc.getMethod() = m
|
||||
|
|
||||
// substring in Java
|
||||
(
|
||||
m.hasQualifiedName("java.lang", "String", "substring") or
|
||||
m.hasQualifiedName("java.lang", "StringBuffer", "substring") or
|
||||
m.hasQualifiedName("java.lang", "StringBuilder", "substring")
|
||||
) and
|
||||
(
|
||||
twoArgLimit(mc, limit, false) or
|
||||
singleArgLimit(mc, limit, false)
|
||||
) and
|
||||
this.asExpr() = mc.getQualifier()
|
||||
or
|
||||
// Kotlin string operations, which use extension methods (so the string is the first argument)
|
||||
(
|
||||
m.hasQualifiedName("kotlin.text", "StringsKt", "substring") and
|
||||
(
|
||||
twoArgLimit(mc, limit, true) or
|
||||
singleArgLimit(mc, limit, true)
|
||||
)
|
||||
or
|
||||
m.hasQualifiedName("kotlin.text", "StringsKt", ["take", "takeLast"]) and
|
||||
singleArgLimit(mc, limit, true)
|
||||
) and
|
||||
this.asExpr() = mc.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A predicate to check single-argument method calls for a constant integer below a set limit. */
|
||||
bindingset[limit, isKotlin]
|
||||
private predicate singleArgLimit(MethodCall mc, int limit, boolean isKotlin) {
|
||||
mc.getNumArgument() = 1 and
|
||||
exists(int firstArgIndex, int delta |
|
||||
if isKotlin = true then firstArgIndex = 1 else firstArgIndex = 0
|
||||
|
|
||||
bounded(mc.getArgument(firstArgIndex), any(ZeroBound z), delta, true, _) and
|
||||
delta <= limit
|
||||
)
|
||||
}
|
||||
|
||||
/** A predicate to check two-argument method calls for zero and a constant integer below a set limit. */
|
||||
bindingset[limit, isKotlin]
|
||||
private predicate twoArgLimit(MethodCall mc, int limit, boolean isKotlin) {
|
||||
mc.getNumArgument() = 2 and
|
||||
exists(int firstArgIndex, int secondArgIndex, int delta |
|
||||
isKotlin = true and firstArgIndex = 1 and secondArgIndex = 2
|
||||
or
|
||||
isKotlin = false and firstArgIndex = 0 and secondArgIndex = 1
|
||||
|
|
||||
// mc.getArgument(firstArgIndex).(CompileTimeConstantExpr).getIntValue() = 0 and
|
||||
bounded(mc.getArgument(firstArgIndex), any(ZeroBound z), 0, true, _) and
|
||||
bounded(mc.getArgument(firstArgIndex), any(ZeroBound z), 0, false, _) and
|
||||
bounded(mc.getArgument(secondArgIndex), any(ZeroBound z), delta, true, _) and
|
||||
delta <= limit
|
||||
)
|
||||
}
|
||||
|
||||
private class DefaultSensitiveLoggerBarrier extends SensitiveLoggerBarrier {
|
||||
DefaultSensitiveLoggerBarrier() {
|
||||
this.asExpr() instanceof LiveLiteral or
|
||||
this instanceof SimpleTypeSanitizer or
|
||||
this.getType() instanceof TypeType
|
||||
}
|
||||
}
|
||||
|
||||
/** A data-flow configuration for identifying potentially-sensitive data flowing to a log output. */
|
||||
module SensitiveLoggerConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof SensitiveLoggerSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "log-injection") }
|
||||
|
||||
predicate isBarrier(DataFlow::Node sanitizer) {
|
||||
sanitizer.asExpr() instanceof LiveLiteral or
|
||||
sanitizer instanceof SimpleTypeSanitizer or
|
||||
sanitizer.getType() instanceof TypeType
|
||||
}
|
||||
predicate isBarrier(DataFlow::Node sanitizer) { sanitizer instanceof SensitiveLoggerBarrier }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node node) { isSource(node) }
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.frameworks.owasp.Esapi
|
||||
private import semmle.code.java.security.Sanitizers
|
||||
|
||||
/**
|
||||
@@ -28,25 +27,8 @@ class TrustBoundaryViolationSink extends DataFlow::Node {
|
||||
*/
|
||||
abstract class TrustBoundaryValidationSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A node validated by an OWASP ESAPI validation method.
|
||||
*/
|
||||
private class EsapiValidatedInputSanitizer extends TrustBoundaryValidationSanitizer {
|
||||
EsapiValidatedInputSanitizer() {
|
||||
this = DataFlow::BarrierGuard<esapiIsValidData/3>::getABarrierNode() or
|
||||
this.asExpr().(MethodCall).getMethod() instanceof EsapiGetValidMethod
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `g` is a guard that checks that `e` is valid data according to an OWASP ESAPI validation method.
|
||||
*/
|
||||
private predicate esapiIsValidData(Guard g, Expr e, boolean branch) {
|
||||
branch = true and
|
||||
exists(MethodCall ma | ma.getMethod() instanceof EsapiIsValidMethod |
|
||||
g = ma and
|
||||
e = ma.getArgument(1)
|
||||
)
|
||||
private class DefaultTrustBoundaryValidationSanitizer extends TrustBoundaryValidationSanitizer {
|
||||
DefaultTrustBoundaryValidationSanitizer() { barrierNode(this, "trust-boundary-violation") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,9 +32,9 @@ private predicate validationCall(MethodCall ma, VarAccess va) {
|
||||
}
|
||||
|
||||
private predicate validatedAccess(VarAccess va) {
|
||||
exists(SsaVariable v, MethodCall guardcall |
|
||||
va = v.getAUse() and
|
||||
validationCall(guardcall, v.getAUse())
|
||||
exists(SsaDefinition v, MethodCall guardcall |
|
||||
va = v.getARead() and
|
||||
validationCall(guardcall, v.getARead())
|
||||
|
|
||||
guardcall.(Guard).controls(va.getBasicBlock(), _)
|
||||
or
|
||||
|
||||
@@ -54,12 +54,24 @@ private class DefaultXssSink extends XssSink {
|
||||
}
|
||||
}
|
||||
|
||||
/** A default sanitizer that considers numeric and boolean typed data safe for writing to output. */
|
||||
private class DefaultXssSanitizer extends XssSanitizer {
|
||||
DefaultXssSanitizer() {
|
||||
DefaultXssSanitizer() { barrierNode(this, ["html-injection", "js-injection"]) }
|
||||
}
|
||||
|
||||
/** A sanitizer that considers numeric and boolean typed data safe for writing to output. */
|
||||
private class PrimitiveSanitizer extends XssSanitizer {
|
||||
PrimitiveSanitizer() {
|
||||
this.getType() instanceof NumericType or
|
||||
this.getType() instanceof BooleanType or
|
||||
// Match `org.springframework.web.util.HtmlUtils.htmlEscape` and possibly other methods like it.
|
||||
this.getType() instanceof BooleanType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `org.springframework.web.util.HtmlUtils.htmlEscape`, or possibly
|
||||
* other methods like it, considered as a sanitizer for XSS.
|
||||
*/
|
||||
private class HtmlEscapeXssSanitizer extends XssSanitizer {
|
||||
HtmlEscapeXssSanitizer() {
|
||||
this.asExpr().(MethodCall).getMethod().getName().regexpMatch("(?i)html_?escape.*")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,17 +21,8 @@ private class DefaultRegexInjectionSink extends RegexInjectionSink {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `Pattern.quote` method, which gives metacharacters or escape sequences
|
||||
* no special meaning.
|
||||
*/
|
||||
private class PatternQuoteCall extends RegexInjectionSanitizer {
|
||||
PatternQuoteCall() {
|
||||
exists(MethodCall ma, Method m | m = ma.getMethod() |
|
||||
ma.getArgument(0) = this.asExpr() and
|
||||
m instanceof PatternQuoteMethod
|
||||
)
|
||||
}
|
||||
private class DefaultRegexInjectionSanitizer extends RegexInjectionSanitizer {
|
||||
DefaultRegexInjectionSanitizer() { barrierNode(this, "regex-use") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,6 @@ module;
|
||||
|
||||
import semmle.files.FileSystem
|
||||
private import codeql.xml.Xml
|
||||
private import semmle.code.java.Overlay
|
||||
|
||||
private module Input implements InputSig<File, Location> {
|
||||
class XmlLocatableBase = @xmllocatable or @xmlnamespaceable;
|
||||
@@ -70,13 +69,3 @@ private module Input implements InputSig<File, Location> {
|
||||
}
|
||||
|
||||
import Make<File, Location, Input>
|
||||
|
||||
private class DiscardableXmlAttribute extends DiscardableXmlLocatable, @xmlattribute { }
|
||||
|
||||
private class DiscardableXmlElement extends DiscardableXmlLocatable, @xmlelement { }
|
||||
|
||||
private class DiscardableXmlComment extends DiscardableXmlLocatable, @xmlcomment { }
|
||||
|
||||
private class DiscardableXmlCharacters extends DiscardableXmlLocatable, @xmlcharacters { }
|
||||
|
||||
private class DiscardableXmlDtd extends DiscardableXmlLocatable, @xmldtd { }
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
## 1.10.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Java analysis no longer forces `--source` and `--target` compiler flags for Maven builds. This allows Maven to use the project's own compiler configuration, improving build compatibility.
|
||||
|
||||
## 1.10.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.10.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Operations that extract only a fixed-length prefix or suffix of a string (for example, `substring` in Java or `take` in Kotlin), when limited to a length of at most 7 characters, are now treated as sanitizers for the `java/sensitive-log` query.
|
||||
|
||||
## 1.10.0
|
||||
|
||||
### Query Metadata Changes
|
||||
|
||||
* Reduced the `security-severity` score of the `java/overly-large-range` query from 5.0 to 4.0 to better reflect its impact.
|
||||
* Reduced the `security-severity` score of the `java/insecure-cookie` query from 5.0 to 4.0 to better reflect its impact.
|
||||
|
||||
## 1.9.0
|
||||
|
||||
### New Queries
|
||||
|
||||
@@ -19,10 +19,10 @@ import semmle.code.java.dataflow.RangeUtils
|
||||
import semmle.code.java.dataflow.RangeAnalysis
|
||||
|
||||
pragma[nomagic]
|
||||
predicate ssaArrayLengthBound(SsaVariable arr, Bound b) {
|
||||
predicate ssaArrayLengthBound(SsaDefinition arr, Bound b) {
|
||||
exists(FieldAccess len |
|
||||
len.getField() instanceof ArrayLengthField and
|
||||
len.getQualifier() = arr.getAUse() and
|
||||
len.getQualifier() = arr.getARead() and
|
||||
b.getExpr() = len
|
||||
)
|
||||
}
|
||||
@@ -31,9 +31,9 @@ predicate ssaArrayLengthBound(SsaVariable arr, Bound b) {
|
||||
* Holds if the index expression of `aa` is less than or equal to the array length plus `k`.
|
||||
*/
|
||||
predicate boundedArrayAccess(ArrayAccess aa, int k) {
|
||||
exists(SsaVariable arr, Expr index, Bound b, int delta |
|
||||
exists(SsaDefinition arr, Expr index, Bound b, int delta |
|
||||
aa.getIndexExpr() = index and
|
||||
aa.getArray() = arr.getAUse() and
|
||||
aa.getArray() = arr.getARead() and
|
||||
bounded(index, b, delta, true, _)
|
||||
|
|
||||
ssaArrayLengthBound(arr, b) and
|
||||
|
||||
@@ -127,7 +127,7 @@ Expr overFlowCand() {
|
||||
c.getIntValue() >= 0
|
||||
)
|
||||
or
|
||||
exists(SsaExplicitUpdate x | result = x.getAUse() and x.getDefiningExpr() = overFlowCand())
|
||||
exists(SsaExplicitWrite x | result = x.getARead() and x.getDefiningExpr() = overFlowCand())
|
||||
or
|
||||
result.(AssignExpr).getRhs() = overFlowCand()
|
||||
or
|
||||
@@ -142,27 +142,27 @@ Expr overFlowCand() {
|
||||
predicate positiveOrNegative(Expr e) { positive(e) or negative(e) }
|
||||
|
||||
/** Gets an expression that equals `v` plus a positive or negative value. */
|
||||
Expr increaseOrDecreaseOfVar(SsaVariable v) {
|
||||
Expr increaseOrDecreaseOfVar(SsaDefinition v) {
|
||||
exists(AssignAddExpr add |
|
||||
result = add and
|
||||
positiveOrNegative(add.getDest()) and
|
||||
add.getRhs() = v.getAUse()
|
||||
add.getRhs() = v.getARead()
|
||||
)
|
||||
or
|
||||
exists(AddExpr add, Expr e |
|
||||
result = add and
|
||||
add.hasOperands(v.getAUse(), e) and
|
||||
add.hasOperands(v.getARead(), e) and
|
||||
positiveOrNegative(e)
|
||||
)
|
||||
or
|
||||
exists(SubExpr sub |
|
||||
result = sub and
|
||||
sub.getLeftOperand() = v.getAUse() and
|
||||
sub.getLeftOperand() = v.getARead() and
|
||||
positiveOrNegative(sub.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(SsaExplicitUpdate x |
|
||||
result = x.getAUse() and x.getDefiningExpr() = increaseOrDecreaseOfVar(v)
|
||||
exists(SsaExplicitWrite x |
|
||||
result = x.getARead() and x.getDefiningExpr() = increaseOrDecreaseOfVar(v)
|
||||
)
|
||||
or
|
||||
result.(AssignExpr).getRhs() = increaseOrDecreaseOfVar(v)
|
||||
@@ -172,7 +172,7 @@ Expr increaseOrDecreaseOfVar(SsaVariable v) {
|
||||
|
||||
predicate overFlowTest(ComparisonExpr comp) {
|
||||
(
|
||||
exists(SsaVariable v | comp.hasOperands(increaseOrDecreaseOfVar(v), v.getAUse()))
|
||||
exists(SsaDefinition v | comp.hasOperands(increaseOrDecreaseOfVar(v), v.getARead()))
|
||||
or
|
||||
comp.getLesserOperand() = overFlowCand() and
|
||||
comp.getGreaterOperand().(IntegerLiteral).getIntValue() = 0
|
||||
@@ -195,9 +195,9 @@ predicate concurrentModificationTest(BinaryExpr test) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate guardedTest(EqualityTest test, Guard guard, boolean isEq, int i1, int i2) {
|
||||
exists(SsaVariable v, CompileTimeConstantExpr c1, CompileTimeConstantExpr c2 |
|
||||
guard.isEquality(v.getAUse(), c1, isEq) and
|
||||
test.hasOperands(v.getAUse(), c2) and
|
||||
exists(SsaDefinition v, CompileTimeConstantExpr c1, CompileTimeConstantExpr c2 |
|
||||
guard.isEquality(v.getARead(), c1, isEq) and
|
||||
test.hasOperands(v.getARead(), c2) and
|
||||
i1 = c1.getIntValue() and
|
||||
i2 = c2.getIntValue() and
|
||||
v.getSourceVariable().getType() instanceof IntegralType
|
||||
|
||||
@@ -27,14 +27,14 @@ class BoundKind extends string {
|
||||
*/
|
||||
predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) {
|
||||
exists(
|
||||
ConditionBlock cb, SsaVariable v, BinaryExpr cond, boolean condIsTrue, int k1, int k2,
|
||||
ConditionBlock cb, SsaDefinition v, BinaryExpr cond, boolean condIsTrue, int k1, int k2,
|
||||
CompileTimeConstantExpr c1, CompileTimeConstantExpr c2
|
||||
|
|
||||
s1.getCondition() = cond and
|
||||
cb.getCondition() = cond and
|
||||
cond.hasOperands(v.getAUse(), c1) and
|
||||
cond.hasOperands(v.getARead(), c1) and
|
||||
c1.getIntValue() = k1 and
|
||||
test.hasOperands(v.getAUse(), c2) and
|
||||
test.hasOperands(v.getARead(), c2) and
|
||||
c2.getIntValue() = k2 and
|
||||
v.getSourceVariable().getVariable() instanceof LocalScopeVariable and
|
||||
cb.controls(test.getBasicBlock(), condIsTrue) and
|
||||
@@ -49,7 +49,7 @@ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) {
|
||||
)
|
||||
or
|
||||
exists(ComparisonExpr comp | comp = cond |
|
||||
comp.getLesserOperand() = v.getAUse() and
|
||||
comp.getLesserOperand() = v.getARead() and
|
||||
(
|
||||
condIsTrue = true and
|
||||
boundKind.isUpper() and
|
||||
@@ -60,7 +60,7 @@ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) {
|
||||
(if comp.isStrict() then bound = k1 else bound = k1 + 1)
|
||||
)
|
||||
or
|
||||
comp.getGreaterOperand() = v.getAUse() and
|
||||
comp.getGreaterOperand() = v.getARead() and
|
||||
(
|
||||
condIsTrue = true and
|
||||
boundKind.isLower() and
|
||||
@@ -88,7 +88,7 @@ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) {
|
||||
)
|
||||
or
|
||||
exists(ComparisonExpr comp | comp = test |
|
||||
comp.getLesserOperand() = v.getAUse() and
|
||||
comp.getLesserOperand() = v.getARead() and
|
||||
(
|
||||
boundKind.providesLowerBound() and
|
||||
testIsTrue = false and
|
||||
@@ -107,7 +107,7 @@ predicate uselessTest(ConditionNode s1, BinaryExpr test, boolean testIsTrue) {
|
||||
)
|
||||
)
|
||||
or
|
||||
comp.getGreaterOperand() = v.getAUse() and
|
||||
comp.getGreaterOperand() = v.getARead() and
|
||||
(
|
||||
boundKind.providesLowerBound() and
|
||||
testIsTrue = true and
|
||||
|
||||
@@ -8,9 +8,9 @@ import semmle.code.java.dataflow.SSA
|
||||
private Expr getAFieldRead(Field f) {
|
||||
result = f.getAnAccess()
|
||||
or
|
||||
exists(SsaExplicitUpdate v | v.getSourceVariable().getVariable() instanceof LocalScopeVariable |
|
||||
result = v.getAUse() and
|
||||
v.getDefiningExpr().(VariableAssign).getSource() = getAFieldRead(f)
|
||||
exists(SsaExplicitWrite v | v.getSourceVariable().getVariable() instanceof LocalScopeVariable |
|
||||
result = v.getARead() and
|
||||
v.getValue() = getAFieldRead(f)
|
||||
)
|
||||
or
|
||||
result.(AssignExpr).getSource() = getAFieldRead(f)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user