From ab618dcf2f6d1e970ebcd4a402c5441979db6fca Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 21 Sep 2020 17:41:51 -0400 Subject: [PATCH 001/166] Java: QL Query Detector for JHipster Generated CVE-2019-16303 --- .../CWE/CWE-338/JHipsterGeneratedPRNG.qhelp | 55 +++ .../CWE/CWE-338/JHipsterGeneratedPRNG.ql | 48 +++ .../CWE-338/JHipsterGeneratedPRNGFixed.java | 69 ++++ .../JHipsterGeneratedPRNGVulnerable.java | 58 +++ .../code/java/frameworks/apache/Lang.qll | 9 + .../tests/JHipsterGeneratedPRNG.expected | 5 + .../semmle/tests/JHipsterGeneratedPRNG.qlref | 1 + .../semmle/tests/fixed/RandomUtil.java | 71 ++++ .../security/CWE-338/semmle/tests/options | 1 + .../semmle/tests/vulnerable/RandomUtil.java | 60 +++ .../apache-commons-lang3-3.7/LICENSE.txt | 203 +++++++++ .../commons/lang3/RandomStringUtils.java | 388 ++++++++++++++++++ 12 files changed, 968 insertions(+) create mode 100644 java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp create mode 100644 java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql create mode 100644 java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNGFixed.java create mode 100644 java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNGVulnerable.java create mode 100644 java/ql/src/semmle/code/java/frameworks/apache/Lang.qll create mode 100644 java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.expected create mode 100644 java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.qlref create mode 100644 java/ql/test/query-tests/security/CWE-338/semmle/tests/fixed/RandomUtil.java create mode 100644 java/ql/test/query-tests/security/CWE-338/semmle/tests/options create mode 100644 java/ql/test/query-tests/security/CWE-338/semmle/tests/vulnerable/RandomUtil.java create mode 100644 java/ql/test/stubs/apache-commons-lang3-3.7/LICENSE.txt create mode 100644 java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/RandomStringUtils.java diff --git a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp new file mode 100644 index 00000000000..8aa9f27c028 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp @@ -0,0 +1,55 @@ + + + +

This query detects instances of RandomUtil.java generated by a JHipster version vulnerable to CVE-2019-16303. + +

Using one password reset token from your app combined with the proof of concept (POC) linked below, an attacker can determine all future password reset tokens to be generated by this server. +This allows an attacker to pick and choose what account they would like to takeover by sending account password reset requests for targeted accounts.

+ +

This vulnerability has a + + CVSS v3.0 Base Score of 9.8/10 +.

+
+ + + +

The example below shows the vulnerable RandomUtil class generated by JHipster. + + +

Below is a fixed version of the RandomUtil class.

+ + +
+ + + +An automated refactoring rewrite module can be found here. + + + + +
  • + + Cloudflare Blog: Why secure systems require random numbers + +
  • +
  • + + How I Hacked Hacker News (with arc security advisory) + +
  • +
  • + Research (Hacking Apache Commons RandomStringUtils): + + The Java Soothsayer: A practical application for insecure randomness. (Includes free 0day) + +
  • + + + +
    + +
    diff --git a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql new file mode 100644 index 00000000000..9717b9ce7e0 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql @@ -0,0 +1,48 @@ +/** + * @name Detect JHipster Generator Vulnnerability CVE-2019-16303 + * @description Detector for the CVE-2019-16303 vulnerability that existed in the JHipster code generator. + * @kind problem + * @problem.severity error + * @precision very-high + * @id java/jhipster-prng + * @tags security + * external/cwe/cwe-338 + */ + +import java +import semmle.code.java.frameworks.apache.Lang + +private class PredictableApacheRandomStringUtilsMethod extends Method { + PredictableApacheRandomStringUtilsMethod() { + this.getDeclaringType() instanceof TypeApacheRandomStringUtils + } +} + +private class PredictableApacheRandomStringUtilsMethodAccess extends MethodAccess { + PredictableApacheRandomStringUtilsMethodAccess() { + this.getMethod() instanceof PredictableApacheRandomStringUtilsMethod and + // The one valid use of this type that uses SecureRandom as a source of data. + not this.getMethod().getName() = "random" + } +} + +private class VulnerableJHipsterRandomUtilClass extends Class { + VulnerableJHipsterRandomUtilClass() { getName() = "RandomUtil" } +} + +private class VulnerableJHipsterRandomUtilMethod extends Method { + VulnerableJHipsterRandomUtilMethod() { + this.getDeclaringType() instanceof VulnerableJHipsterRandomUtilClass and + this.getName().matches("generate%") and + this.getReturnType() instanceof TypeString and + exists(ReturnStmt s, PredictableApacheRandomStringUtilsMethodAccess access | + s = this.getBody().(SingletonBlock).getStmt() + | + s.getResult() = access + ) + } +} + +from VulnerableJHipsterRandomUtilMethod the_method +select the_method, + "RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303" diff --git a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNGFixed.java b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNGFixed.java new file mode 100644 index 00000000000..42cf387806a --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNGFixed.java @@ -0,0 +1,69 @@ +import org.apache.commons.lang3.RandomStringUtils; + +import java.security.SecureRandom; + +/** + * Utility class for generating random Strings. + */ +public final class RandomUtil { + private static final SecureRandom SECURE_RANDOM = new SecureRandom(); + + private static final int DEF_COUNT = 20; + + static { + SECURE_RANDOM.nextBytes(new byte[64]); + } + + private RandomUtil() { + } + + private static String generateRandomAlphanumericString() { + return RandomStringUtils.random(DEF_COUNT, 0, 0, true, true, null, SECURE_RANDOM); + } + + /** + * Generate a password. + * + * @return the generated password. + */ + public static String generatePassword() { + return generateRandomAlphanumericString(); + } + + /** + * Generate an activation key. + * + * @return the generated activation key. + */ + public static String generateActivationKey() { + return generateRandomAlphanumericString(); + } + + /** + * Generate a reset key. + * + * @return the generated reset key. + */ + public static String generateResetKey() { + return generateRandomAlphanumericString(); + } + + /** + * Generate a unique series to validate a persistent token, used in the + * authentication remember-me mechanism. + * + * @return the generated series data. + */ + public static String generateSeriesData() { + return generateRandomAlphanumericString(); + } + + /** + * Generate a persistent token, used in the authentication remember-me mechanism. + * + * @return the generated token data. + */ + public static String generateTokenData() { + return generateRandomAlphanumericString(); + } +} diff --git a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNGVulnerable.java b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNGVulnerable.java new file mode 100644 index 00000000000..d1a7f4cd924 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNGVulnerable.java @@ -0,0 +1,58 @@ +import org.apache.commons.lang3.RandomStringUtils; + +/** + * Utility class for generating random Strings. + */ +public final class RandomUtil { + + private static final int DEF_COUNT = 20; + + private RandomUtil() { + } + + /** + * Generate a password. + * + * @return the generated password. + */ + public static String generatePassword() { + return RandomStringUtils.randomAlphanumeric(DEF_COUNT); + } + + /** + * Generate an activation key. + * + * @return the generated activation key. + */ + public static String generateActivationKey() { + return RandomStringUtils.randomNumeric(DEF_COUNT); + } + + /** + * Generate a reset key. + * + * @return the generated reset key. + */ + public static String generateResetKey() { + return RandomStringUtils.randomNumeric(DEF_COUNT); + } + + /** + * Generate a unique series to validate a persistent token, used in the + * authentication remember-me mechanism. + * + * @return the generated series data. + */ + public static String generateSeriesData() { + return RandomStringUtils.randomAlphanumeric(DEF_COUNT); + } + + /** + * Generate a persistent token, used in the authentication remember-me mechanism. + * + * @return the generated token data. + */ + public static String generateTokenData() { + return RandomStringUtils.randomAlphanumeric(DEF_COUNT); + } +} diff --git a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll new file mode 100644 index 00000000000..2aa00a007f3 --- /dev/null +++ b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll @@ -0,0 +1,9 @@ +/* Definitions related to the Apache Commons Exec library. */ +import semmle.code.java.Type + +class TypeApacheRandomStringUtils extends Class { + TypeApacheRandomStringUtils() { + hasQualifiedName("org.apache.commons.lang", "RandomStringUtils") or + hasQualifiedName("org.apache.commons.lang3", "RandomStringUtils") + } +} diff --git a/java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.expected b/java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.expected new file mode 100644 index 00000000000..7234f316b6e --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.expected @@ -0,0 +1,5 @@ +| vulnerable/RandomUtil.java:20:26:20:41 | generatePassword | RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303 | +| vulnerable/RandomUtil.java:29:26:29:46 | generateActivationKey | RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303 | +| vulnerable/RandomUtil.java:38:26:38:41 | generateResetKey | RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303 | +| vulnerable/RandomUtil.java:48:26:48:43 | generateSeriesData | RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303 | +| vulnerable/RandomUtil.java:57:26:57:42 | generateTokenData | RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303 | diff --git a/java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.qlref b/java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.qlref new file mode 100644 index 00000000000..441bcf25929 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql diff --git a/java/ql/test/query-tests/security/CWE-338/semmle/tests/fixed/RandomUtil.java b/java/ql/test/query-tests/security/CWE-338/semmle/tests/fixed/RandomUtil.java new file mode 100644 index 00000000000..076115bad7d --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-338/semmle/tests/fixed/RandomUtil.java @@ -0,0 +1,71 @@ +package test.cwe338.cwe.examples.fixed; + +import org.apache.commons.lang3.RandomStringUtils; + +import java.security.SecureRandom; + +/** + * Utility class for generating random Strings. + */ +public final class RandomUtil { + private static final SecureRandom SECURE_RANDOM = new SecureRandom(); + + private static final int DEF_COUNT = 20; + + static { + SECURE_RANDOM.nextBytes(new byte[64]); + } + + private RandomUtil() { + } + + private static String generateRandomAlphanumericString() { + return RandomStringUtils.random(DEF_COUNT, 0, 0, true, true, null, SECURE_RANDOM); + } + + /** + * Generate a password. + * + * @return the generated password. + */ + public static String generatePassword() { + return generateRandomAlphanumericString(); + } + + /** + * Generate an activation key. + * + * @return the generated activation key. + */ + public static String generateActivationKey() { + return generateRandomAlphanumericString(); + } + + /** + * Generate a reset key. + * + * @return the generated reset key. + */ + public static String generateResetKey() { + return generateRandomAlphanumericString(); + } + + /** + * Generate a unique series to validate a persistent token, used in the + * authentication remember-me mechanism. + * + * @return the generated series data. + */ + public static String generateSeriesData() { + return generateRandomAlphanumericString(); + } + + /** + * Generate a persistent token, used in the authentication remember-me mechanism. + * + * @return the generated token data. + */ + public static String generateTokenData() { + return generateRandomAlphanumericString(); + } +} diff --git a/java/ql/test/query-tests/security/CWE-338/semmle/tests/options b/java/ql/test/query-tests/security/CWE-338/semmle/tests/options new file mode 100644 index 00000000000..2154f71c225 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-338/semmle/tests/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/apache-commons-lang3-3.7 diff --git a/java/ql/test/query-tests/security/CWE-338/semmle/tests/vulnerable/RandomUtil.java b/java/ql/test/query-tests/security/CWE-338/semmle/tests/vulnerable/RandomUtil.java new file mode 100644 index 00000000000..22e0c0b9150 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-338/semmle/tests/vulnerable/RandomUtil.java @@ -0,0 +1,60 @@ +package test.cwe338.cwe.examples.vulnerable; + +import org.apache.commons.lang3.RandomStringUtils; + +/** + * Utility class for generating random Strings. + */ +public final class RandomUtil { + + private static final int DEF_COUNT = 20; + + private RandomUtil() { + } + + /** + * Generate a password. + * + * @return the generated password. + */ + public static String generatePassword() { + return RandomStringUtils.randomAlphanumeric(DEF_COUNT); + } + + /** + * Generate an activation key. + * + * @return the generated activation key. + */ + public static String generateActivationKey() { + return RandomStringUtils.randomNumeric(DEF_COUNT); + } + + /** + * Generate a reset key. + * + * @return the generated reset key. + */ + public static String generateResetKey() { + return RandomStringUtils.randomNumeric(DEF_COUNT); + } + + /** + * Generate a unique series to validate a persistent token, used in the + * authentication remember-me mechanism. + * + * @return the generated series data. + */ + public static String generateSeriesData() { + return RandomStringUtils.randomAlphanumeric(DEF_COUNT); + } + + /** + * Generate a persistent token, used in the authentication remember-me mechanism. + * + * @return the generated token data. + */ + public static String generateTokenData() { + return RandomStringUtils.randomAlphanumeric(DEF_COUNT); + } +} diff --git a/java/ql/test/stubs/apache-commons-lang3-3.7/LICENSE.txt b/java/ql/test/stubs/apache-commons-lang3-3.7/LICENSE.txt new file mode 100644 index 00000000000..f4f87bd4ed6 --- /dev/null +++ b/java/ql/test/stubs/apache-commons-lang3-3.7/LICENSE.txt @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + \ No newline at end of file diff --git a/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/RandomStringUtils.java b/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/RandomStringUtils.java new file mode 100644 index 00000000000..e4066410ec8 --- /dev/null +++ b/java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/RandomStringUtils.java @@ -0,0 +1,388 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.util.Random; + +/** + *

    Operations for random {@code String}s.

    + *

    Currently private high surrogate characters are ignored. + * These are Unicode characters that fall between the values 56192 (db80) + * and 56319 (dbff) as we don't know how to handle them. + * High and low surrogates are correctly dealt with - that is if a + * high surrogate is randomly chosen, 55296 (d800) to 56191 (db7f) + * then it is followed by a low surrogate. If a low surrogate is chosen, + * 56320 (dc00) to 57343 (dfff) then it is placed after a randomly + * chosen high surrogate.

    + *

    RandomStringUtils is intended for simple use cases. For more advanced + * use cases consider using commons-text + * + * RandomStringGenerator instead.

    + * + *

    #ThreadSafe#

    + * @since 1.0 + */ +public class RandomStringUtils { + + /** + *

    Random object used by random method. This has to be not local + * to the random method so as to not return the same value in the + * same millisecond.

    + */ + private static final Random RANDOM = new Random(); + + /** + *

    {@code RandomStringUtils} instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * {@code RandomStringUtils.random(5);}.

    + * + *

    This constructor is public to permit tools that require a JavaBean instance + * to operate.

    + */ + public RandomStringUtils() { + super(); + } + + // Random + //----------------------------------------------------------------------- + /** + *

    Creates a random string whose length is the number of characters + * specified.

    + * + *

    Characters will be chosen from the set of all characters.

    + * + * @param count the length of random string to create + * @return the random string + */ + public static String random(final int count) { + return ""; + } + + /** + *

    Creates a random string whose length is the number of characters + * specified.

    + * + *

    Characters will be chosen from the set of characters whose + * ASCII value is between {@code 32} and {@code 126} (inclusive).

    + * + * @param count the length of random string to create + * @return the random string + */ + public static String randomAscii(final int count) { + return ""; + } + + /** + *

    Creates a random string whose length is between the inclusive minimum and + * the exclusive maximum.

    + * + *

    Characters will be chosen from the set of characters whose + * ASCII value is between {@code 32} and {@code 126} (inclusive).

    + * + * @param minLengthInclusive the inclusive minimum length of the string to generate + * @param maxLengthExclusive the exclusive maximum length of the string to generate + * @return the random string + * @since 3.5 + */ + public static String randomAscii(final int minLengthInclusive, final int maxLengthExclusive) { + return ""; + } + + /** + *

    Creates a random string whose length is the number of characters + * specified.

    + * + *

    Characters will be chosen from the set of Latin alphabetic + * characters (a-z, A-Z).

    + * + * @param count the length of random string to create + * @return the random string + */ + public static String randomAlphabetic(final int count) { + return ""; + } + + /** + *

    Creates a random string whose length is between the inclusive minimum and + * the exclusive maximum.

    + * + *

    Characters will be chosen from the set of Latin alphabetic characters (a-z, A-Z).

    + * + * @param minLengthInclusive the inclusive minimum length of the string to generate + * @param maxLengthExclusive the exclusive maximum length of the string to generate + * @return the random string + * @since 3.5 + */ + public static String randomAlphabetic(final int minLengthInclusive, final int maxLengthExclusive) { + return ""; + } + + /** + *

    Creates a random string whose length is the number of characters + * specified.

    + * + *

    Characters will be chosen from the set of Latin alphabetic + * characters (a-z, A-Z) and the digits 0-9.

    + * + * @param count the length of random string to create + * @return the random string + */ + public static String randomAlphanumeric(final int count) { + return ""; + } + + /** + *

    Creates a random string whose length is between the inclusive minimum and + * the exclusive maximum.

    + * + *

    Characters will be chosen from the set of Latin alphabetic + * characters (a-z, A-Z) and the digits 0-9.

    + * + * @param minLengthInclusive the inclusive minimum length of the string to generate + * @param maxLengthExclusive the exclusive maximum length of the string to generate + * @return the random string + * @since 3.5 + */ + public static String randomAlphanumeric(final int minLengthInclusive, final int maxLengthExclusive) { + return ""; + } + + /** + *

    Creates a random string whose length is the number of characters specified.

    + * + *

    Characters will be chosen from the set of characters which match the POSIX [:graph:] + * regular expression character class. This class contains all visible ASCII characters + * (i.e. anything except spaces and control characters).

    + * + * @param count the length of random string to create + * @return the random string + * @since 3.5 + */ + public static String randomGraph(final int count) { + return ""; + } + + /** + *

    Creates a random string whose length is between the inclusive minimum and + * the exclusive maximum.

    + * + *

    Characters will be chosen from the set of \p{Graph} characters.

    + * + * @param minLengthInclusive the inclusive minimum length of the string to generate + * @param maxLengthExclusive the exclusive maximum length of the string to generate + * @return the random string + * @since 3.5 + */ + public static String randomGraph(final int minLengthInclusive, final int maxLengthExclusive) { + return ""; + } + + /** + *

    Creates a random string whose length is the number of characters + * specified.

    + * + *

    Characters will be chosen from the set of numeric + * characters.

    + * + * @param count the length of random string to create + * @return the random string + */ + public static String randomNumeric(final int count) { + return ""; + } + + /** + *

    Creates a random string whose length is between the inclusive minimum and + * the exclusive maximum.

    + * + *

    Characters will be chosen from the set of \p{Digit} characters.

    + * + * @param minLengthInclusive the inclusive minimum length of the string to generate + * @param maxLengthExclusive the exclusive maximum length of the string to generate + * @return the random string + * @since 3.5 + */ + public static String randomNumeric(final int minLengthInclusive, final int maxLengthExclusive) { + return ""; + } + + /** + *

    Creates a random string whose length is the number of characters specified.

    + * + *

    Characters will be chosen from the set of characters which match the POSIX [:print:] + * regular expression character class. This class includes all visible ASCII characters and spaces + * (i.e. anything except control characters).

    + * + * @param count the length of random string to create + * @return the random string + * @since 3.5 + */ + public static String randomPrint(final int count) { + return ""; + } + + /** + *

    Creates a random string whose length is between the inclusive minimum and + * the exclusive maximum.

    + * + *

    Characters will be chosen from the set of \p{Print} characters.

    + * + * @param minLengthInclusive the inclusive minimum length of the string to generate + * @param maxLengthExclusive the exclusive maximum length of the string to generate + * @return the random string + * @since 3.5 + */ + public static String randomPrint(final int minLengthInclusive, final int maxLengthExclusive) { + return ""; + } + + /** + *

    Creates a random string whose length is the number of characters + * specified.

    + * + *

    Characters will be chosen from the set of alpha-numeric + * characters as indicated by the arguments.

    + * + * @param count the length of random string to create + * @param letters if {@code true}, generated string may include + * alphabetic characters + * @param numbers if {@code true}, generated string may include + * numeric characters + * @return the random string + */ + public static String random(final int count, final boolean letters, final boolean numbers) { + return ""; + } + + /** + *

    Creates a random string whose length is the number of characters + * specified.

    + * + *

    Characters will be chosen from the set of alpha-numeric + * characters as indicated by the arguments.

    + * + * @param count the length of random string to create + * @param start the position in set of chars to start at + * @param end the position in set of chars to end before + * @param letters if {@code true}, generated string may include + * alphabetic characters + * @param numbers if {@code true}, generated string may include + * numeric characters + * @return the random string + */ + public static String random(final int count, final int start, final int end, final boolean letters, final boolean numbers) { + return ""; + } + + /** + *

    Creates a random string based on a variety of options, using + * default source of randomness.

    + * + *

    This method has exactly the same semantics as + * {@link #random(int,int,int,boolean,boolean,char[],Random)}, but + * instead of using an externally supplied source of randomness, it uses + * the internal static {@link Random} instance.

    + * + * @param count the length of random string to create + * @param start the position in set of chars to start at + * @param end the position in set of chars to end before + * @param letters only allow letters? + * @param numbers only allow numbers? + * @param chars the set of chars to choose randoms from. + * If {@code null}, then it will use the set of all chars. + * @return the random string + * @throws ArrayIndexOutOfBoundsException if there are not + * {@code (end - start) + 1} characters in the set array. + */ + public static String random(final int count, final int start, final int end, final boolean letters, final boolean numbers, final char... chars) { + return ""; + } + + /** + *

    Creates a random string based on a variety of options, using + * supplied source of randomness.

    + * + *

    If start and end are both {@code 0}, start and end are set + * to {@code ' '} and {@code 'z'}, the ASCII printable + * characters, will be used, unless letters and numbers are both + * {@code false}, in which case, start and end are set to + * {@code 0} and {@link Character#MAX_CODE_POINT}. + * + *

    If set is not {@code null}, characters between start and + * end are chosen.

    + * + *

    This method accepts a user-supplied {@link Random} + * instance to use as a source of randomness. By seeding a single + * {@link Random} instance with a fixed seed and using it for each call, + * the same random sequence of strings can be generated repeatedly + * and predictably.

    + * + * @param count the length of random string to create + * @param start the position in set of chars to start at (inclusive) + * @param end the position in set of chars to end before (exclusive) + * @param letters only allow letters? + * @param numbers only allow numbers? + * @param chars the set of chars to choose randoms from, must not be empty. + * If {@code null}, then it will use the set of all chars. + * @param random a source of randomness. + * @return the random string + * @throws ArrayIndexOutOfBoundsException if there are not + * {@code (end - start) + 1} characters in the set array. + * @throws IllegalArgumentException if {@code count} < 0 or the provided chars array is empty. + * @since 2.0 + */ + public static String random(int count, int start, int end, final boolean letters, final boolean numbers, + final char[] chars, final Random random) { + return ""; + } + + + /** + *

    Creates a random string whose length is the number of characters + * specified.

    + * + *

    Characters will be chosen from the set of characters + * specified by the string, must not be empty. + * If null, the set of all characters is used.

    + * + * @param count the length of random string to create + * @param chars the String containing the set of characters to use, + * may be null, but must not be empty + * @return the random string + * @throws IllegalArgumentException if {@code count} < 0 or the string is empty. + */ + public static String random(final int count, final String chars) { + return ""; + } + + /** + *

    Creates a random string whose length is the number of characters + * specified.

    + * + *

    Characters will be chosen from the set of characters specified.

    + * + * @param count the length of random string to create + * @param chars the character array containing the set of characters to use, + * may be null + * @return the random string + * @throws IllegalArgumentException if {@code count} < 0. + */ + public static String random(final int count, final char... chars) { + return ""; + } + +} From 535c8cc87e9c2cde062591a8d6778f596128e8db Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 17 Sep 2020 21:13:02 +0200 Subject: [PATCH 002/166] C++: Cache simpleLocalFlowStep instead of simpleInstructionLocalFlowStep --- .../src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index aedf3ab494a..88015d3dfdc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -545,6 +545,7 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFr * This is the local flow predicate that's used as a building block in global * data flow. It may have less flow than the `localFlowStep` predicate. */ +cached predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { // Operand -> Instruction flow simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction()) @@ -605,7 +606,6 @@ private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) { ) } -cached private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) { iTo.(CopyInstruction).getSourceValueOperand() = opFrom or From 24fe3d066389f85240a6751f80b552d40da65f6c Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Tue, 22 Sep 2020 13:11:11 -0400 Subject: [PATCH 003/166] Apply suggestions from code review Co-authored-by: Felicity Chapman --- .../ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp index 8aa9f27c028..b6bbf62622e 100644 --- a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp +++ b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp @@ -3,7 +3,7 @@ "qhelp.dtd"> -

    This query detects instances of RandomUtil.java generated by a JHipster version vulnerable to CVE-2019-16303. +

    This query detects instances of RandomUtil.java generated by a JHipster version vulnerable to CVE-2019-16303.

    Using one password reset token from your app combined with the proof of concept (POC) linked below, an attacker can determine all future password reset tokens to be generated by this server. This allows an attacker to pick and choose what account they would like to takeover by sending account password reset requests for targeted accounts.

    @@ -16,7 +16,7 @@ This allows an attacker to pick and choose what account they would like to takeo -

    The example below shows the vulnerable RandomUtil class generated by JHipster. +

    The example below shows the vulnerable RandomUtil class generated by JHipster.

    Below is a fixed version of the RandomUtil class.

    @@ -26,7 +26,7 @@ This allows an attacker to pick and choose what account they would like to takeo -An automated refactoring rewrite module can be found here. +

    An automated refactoring rewrite module can be found here.

    From 8578bc5cf0f8f08153770e6206e877575b19634e Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Tue, 22 Sep 2020 15:02:00 -0400 Subject: [PATCH 004/166] Update java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp Co-authored-by: Felicity Chapman --- java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp index b6bbf62622e..88a5474e5e8 100644 --- a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp +++ b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp @@ -40,7 +40,7 @@ This allows an attacker to pick and choose what account they would like to takeo How I Hacked Hacker News (with arc security advisory) -
  • +
  • Research (Hacking Apache Commons RandomStringUtils): From 645d7c883114915937f002f99bb63a4a8bc8d903 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Tue, 22 Sep 2020 15:04:06 -0400 Subject: [PATCH 005/166] Fix documentation in `apache/Lang.qll` --- java/ql/src/semmle/code/java/frameworks/apache/Lang.qll | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll index 2aa00a007f3..394ba2014ab 100644 --- a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll +++ b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll @@ -1,6 +1,8 @@ -/* Definitions related to the Apache Commons Exec library. */ +/* Definitions related to the Apache Commons Lang library. */ import semmle.code.java.Type +/*--- Types ---*/ +/** The class `org.apache.commons.lang.RandomStringUtils` or `org.apache.commons.lang3.RandomStringUtils`. */ class TypeApacheRandomStringUtils extends Class { TypeApacheRandomStringUtils() { hasQualifiedName("org.apache.commons.lang", "RandomStringUtils") or From 17603c80915c7598ea22fccd0af635348e411e82 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Wed, 23 Sep 2020 13:59:49 -0400 Subject: [PATCH 006/166] Update java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp Co-authored-by: Felicity Chapman --- java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp index 88a5474e5e8..8c0bea090e6 100644 --- a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp +++ b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp @@ -17,7 +17,7 @@ This allows an attacker to pick and choose what account they would like to takeo

    The example below shows the vulnerable RandomUtil class generated by JHipster.

    - +

    Below is a fixed version of the RandomUtil class.

    From f5244aab8cfdbb27a4d4fab7e9518cbb10e742ab Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 13 Aug 2020 15:43:40 +0200 Subject: [PATCH 007/166] Python: Add testfiles --- .../dataflow/coverage/argumentPassing.py | 161 +++++++ .../coverage/argumentRouting1.expected | 40 +- .../dataflow/coverage/argumentRouting1.ql | 6 +- .../coverage/argumentRouting2.expected | 34 +- .../dataflow/coverage/argumentRouting2.ql | 4 +- .../coverage/argumentRouting3.expected | 3 +- .../dataflow/coverage/argumentRouting3.ql | 4 +- .../dataflow/coverage/argumentRouting4.ql | 4 +- .../experimental/dataflow/coverage/classes.py | 391 +++++---------- .../dataflow/coverage/dataflow.expected | 446 +++++++++--------- .../dataflow/coverage/localFlow.expected | 72 +-- .../experimental/dataflow/coverage/test.py | 123 +++-- .../experimental/dataflow/coverage/testlib.py | 27 ++ .../dataflow/coverage/validTest.py | 20 +- 14 files changed, 680 insertions(+), 655 deletions(-) create mode 100644 python/ql/test/experimental/dataflow/coverage/argumentPassing.py create mode 100644 python/ql/test/experimental/dataflow/coverage/testlib.py diff --git a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py new file mode 100644 index 00000000000..8bd4f4344ef --- /dev/null +++ b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py @@ -0,0 +1,161 @@ +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname((__file__)))) +from testlib import * + +arg = "source" +arg1 = "source1" +arg2 = "source2" +arg3 = "source3" + + +def SINK(x, expected=arg): + if x == expected: + print("OK") + else: + print("Unexpected flow", x) + + +def SINK1(x): + SINK(x, expected=arg1) + + +def SINK2(x): + SINK(x, expected=arg2) + + +def SINK3(x): + SINK(x, expected=arg3) + + +def argument_passing( + a, + b, + /, + c, + d="", + *, + e="", + f, + **g, +): + SINK1(a) + SINK2(b) + SINK3(c) + SINK(f) + + +@expects(4) +def test_argument_passing(): + argument_passing(arg1, arg2, arg3, f=arg) + + +def with_pos_only(a, /, b): + SINK1(a) + SINK2(b) + + +@expects(6) +def test_pos_only(): + with_pos_only(arg1, arg2) + with_pos_only(arg1, b=arg2) + with_pos_only(arg1, *(arg2,)) + + +def with_multiple_kw_args(a, b, c): + SINK1(a) + SINK2(b) + SINK3(c) + + +@expects(9) +def test_multiple_kw_args(): + with_multiple_kw_args(b=arg2, c=arg3, a=arg1) + with_multiple_kw_args(arg1, *(arg2,), arg3) + with_multiple_kw_args(arg1, **{"c": arg3}, b=arg2) + + +def with_default_arguments(a=arg1, b=arg2, c=arg3): + SINK1(a) + SINK2(b) + SINK3(c) + + +@expects(12) +def test_default_arguments(): + with_default_arguments() + with_default_arguments(arg1) + with_default_arguments(b=arg2) + with_default_arguments(**{"c": arg3}) + + +# Nested constructor pattern +def grab_foo_bar_baz(foo, **kwargs): + SINK1(foo) + grab_bar_baz(**kwargs) + + +def grab_bar_baz(bar, **kwargs): + SINK2(bar) + grab_baz(**kwargs) + + +def grab_baz(baz): + SINK3(baz) + + +@expects(3) +def test_grab(): + grab_foo_bar_baz(baz=arg3, bar=arg2, foo=arg1) + + +# All combinations +def test_pos_pos(): + def with_pos(a): + SINK1(a) + + with_pos(arg1) + + +def test_pos_pos_only(): + def with_pos_only(a, /): + SINK1(a) + + with_pos_only(arg1) + + +def test_pos_star(): + def with_star(*a): + if len(a) > 0: + SINK1(a[0]) + + with_star(arg1) + + +def test_pos_kw(): + def with_kw(a=""): + SINK1(a) + + with_kw(arg1) + + +def test_kw_pos(): + def with_pos(a): + SINK1(a) + + with_pos(a=arg1) + + +def test_kw_kw(): + def with_kw(a=""): + SINK1(a) + + with_kw(a=arg1) + + +def test_kw_doublestar(): + def with_doublestar(**a): + SINK1(a["a"]) + + with_doublestar(a=arg1) diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected index 1bb41afa870..eba722029f7 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected @@ -1,16 +1,24 @@ -| classes.py:620:5:620:16 | SSA variable with_getitem | classes.py:614:15:614:18 | ControlFlowNode for self | -| classes.py:637:5:637:16 | SSA variable with_setitem | classes.py:632:15:632:18 | ControlFlowNode for self | -| classes.py:654:5:654:16 | SSA variable with_delitem | classes.py:649:15:649:18 | ControlFlowNode for self | -| classes.py:735:5:735:12 | SSA variable with_add | classes.py:729:15:729:18 | ControlFlowNode for self | -| classes.py:752:5:752:12 | SSA variable with_sub | classes.py:746:15:746:18 | ControlFlowNode for self | -| classes.py:769:5:769:12 | SSA variable with_mul | classes.py:763:15:763:18 | ControlFlowNode for self | -| classes.py:786:5:786:15 | SSA variable with_matmul | classes.py:780:15:780:18 | ControlFlowNode for self | -| classes.py:803:5:803:16 | SSA variable with_truediv | classes.py:797:15:797:18 | ControlFlowNode for self | -| classes.py:820:5:820:17 | SSA variable with_floordiv | classes.py:814:15:814:18 | ControlFlowNode for self | -| classes.py:837:5:837:12 | SSA variable with_mod | classes.py:831:15:831:18 | ControlFlowNode for self | -| classes.py:877:5:877:12 | SSA variable with_pow | classes.py:865:15:865:18 | ControlFlowNode for self | -| classes.py:894:5:894:15 | SSA variable with_lshift | classes.py:888:15:888:18 | ControlFlowNode for self | -| classes.py:911:5:911:15 | SSA variable with_rshift | classes.py:905:15:905:18 | ControlFlowNode for self | -| classes.py:928:5:928:12 | SSA variable with_and | classes.py:922:15:922:18 | ControlFlowNode for self | -| classes.py:945:5:945:12 | SSA variable with_xor | classes.py:939:15:939:18 | ControlFlowNode for self | -| classes.py:962:5:962:11 | SSA variable with_or | classes.py:956:15:956:18 | ControlFlowNode for self | +| argumentPassing.py:51:22:51:25 | ControlFlowNode for arg1 | argumentPassing.py:43:11:43:11 | ControlFlowNode for a | +| argumentPassing.py:61:19:61:22 | ControlFlowNode for arg1 | argumentPassing.py:55:11:55:11 | ControlFlowNode for a | +| argumentPassing.py:62:19:62:22 | ControlFlowNode for arg1 | argumentPassing.py:55:11:55:11 | ControlFlowNode for a | +| argumentPassing.py:63:19:63:22 | ControlFlowNode for arg1 | argumentPassing.py:55:11:55:11 | ControlFlowNode for a | +| argumentPassing.py:74:45:74:48 | ControlFlowNode for arg1 | argumentPassing.py:67:11:67:11 | ControlFlowNode for a | +| argumentPassing.py:75:27:75:30 | ControlFlowNode for arg1 | argumentPassing.py:67:11:67:11 | ControlFlowNode for a | +| argumentPassing.py:76:27:76:30 | ControlFlowNode for arg1 | argumentPassing.py:67:11:67:11 | ControlFlowNode for a | +| argumentPassing.py:88:28:88:31 | ControlFlowNode for arg1 | argumentPassing.py:80:11:80:11 | ControlFlowNode for a | +| classes.py:563:5:563:16 | SSA variable with_getitem | classes.py:557:15:557:18 | ControlFlowNode for self | +| classes.py:578:5:578:16 | SSA variable with_setitem | classes.py:573:15:573:18 | ControlFlowNode for self | +| classes.py:593:5:593:16 | SSA variable with_delitem | classes.py:588:15:588:18 | ControlFlowNode for self | +| classes.py:665:5:665:12 | SSA variable with_add | classes.py:659:15:659:18 | ControlFlowNode for self | +| classes.py:680:5:680:12 | SSA variable with_sub | classes.py:674:15:674:18 | ControlFlowNode for self | +| classes.py:695:5:695:12 | SSA variable with_mul | classes.py:689:15:689:18 | ControlFlowNode for self | +| classes.py:710:5:710:15 | SSA variable with_matmul | classes.py:704:15:704:18 | ControlFlowNode for self | +| classes.py:725:5:725:16 | SSA variable with_truediv | classes.py:719:15:719:18 | ControlFlowNode for self | +| classes.py:740:5:740:17 | SSA variable with_floordiv | classes.py:734:15:734:18 | ControlFlowNode for self | +| classes.py:755:5:755:12 | SSA variable with_mod | classes.py:749:15:749:18 | ControlFlowNode for self | +| classes.py:791:5:791:12 | SSA variable with_pow | classes.py:779:15:779:18 | ControlFlowNode for self | +| classes.py:806:5:806:15 | SSA variable with_lshift | classes.py:800:15:800:18 | ControlFlowNode for self | +| classes.py:821:5:821:15 | SSA variable with_rshift | classes.py:815:15:815:18 | ControlFlowNode for self | +| classes.py:836:5:836:12 | SSA variable with_and | classes.py:830:15:830:18 | ControlFlowNode for self | +| classes.py:851:5:851:12 | SSA variable with_xor | classes.py:845:15:845:18 | ControlFlowNode for self | +| classes.py:866:5:866:11 | SSA variable with_or | classes.py:860:15:860:18 | ControlFlowNode for self | diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql index 90878dc9052..fab8f93125d 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.ql @@ -9,6 +9,8 @@ class ArgumentRoutingConfig extends DataFlow::Configuration { ArgumentRoutingConfig() { this = "ArgumentRoutingConfig" } override predicate isSource(DataFlow::Node node) { + node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "arg1" + or exists(AssignmentDefinition def, DataFlowPrivate::DataFlowCall call | def.getVariable() = node.(DataFlow::EssaNode).getVar() and def.getValue() = call.getNode() and @@ -27,7 +29,7 @@ class ArgumentRoutingConfig extends DataFlow::Configuration { from DataFlow::Node source, DataFlow::Node sink where - source.getLocation().getFile().getBaseName() = "classes.py" and - sink.getLocation().getFile().getBaseName() = "classes.py" and + source.getLocation().getFile().getBaseName() in ["classes.py", "argumentPassing.py"] and + sink.getLocation().getFile().getBaseName() in ["classes.py", "argumentPassing.py"] and exists(ArgumentRoutingConfig cfg | cfg.hasFlow(source, sink)) select source, sink diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected index 79ae2de5c94..62ea005e68f 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected @@ -1,16 +1,18 @@ -| classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:613:15:613:17 | ControlFlowNode for key | -| classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:631:15:631:17 | ControlFlowNode for key | -| classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:648:15:648:17 | ControlFlowNode for key | -| classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:728:15:728:19 | ControlFlowNode for other | -| classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:745:15:745:19 | ControlFlowNode for other | -| classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:762:15:762:19 | ControlFlowNode for other | -| classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:779:15:779:19 | ControlFlowNode for other | -| classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:796:15:796:19 | ControlFlowNode for other | -| classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:813:15:813:19 | ControlFlowNode for other | -| classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:830:15:830:19 | ControlFlowNode for other | -| classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:864:15:864:19 | ControlFlowNode for other | -| classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:887:15:887:19 | ControlFlowNode for other | -| classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:904:15:904:19 | ControlFlowNode for other | -| classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:921:15:921:19 | ControlFlowNode for other | -| classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:938:15:938:19 | ControlFlowNode for other | -| classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:955:15:955:19 | ControlFlowNode for other | +| argumentPassing.py:51:28:51:31 | ControlFlowNode for arg2 | argumentPassing.py:44:11:44:11 | ControlFlowNode for b | +| argumentPassing.py:61:25:61:28 | ControlFlowNode for arg2 | argumentPassing.py:56:11:56:11 | ControlFlowNode for b | +| classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:556:15:556:17 | ControlFlowNode for key | +| classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:572:15:572:17 | ControlFlowNode for key | +| classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:587:15:587:17 | ControlFlowNode for key | +| classes.py:667:16:667:19 | ControlFlowNode for arg2 | classes.py:658:15:658:19 | ControlFlowNode for other | +| classes.py:682:16:682:19 | ControlFlowNode for arg2 | classes.py:673:15:673:19 | ControlFlowNode for other | +| classes.py:697:16:697:19 | ControlFlowNode for arg2 | classes.py:688:15:688:19 | ControlFlowNode for other | +| classes.py:712:19:712:22 | ControlFlowNode for arg2 | classes.py:703:15:703:19 | ControlFlowNode for other | +| classes.py:727:20:727:23 | ControlFlowNode for arg2 | classes.py:718:15:718:19 | ControlFlowNode for other | +| classes.py:742:22:742:25 | ControlFlowNode for arg2 | classes.py:733:15:733:19 | ControlFlowNode for other | +| classes.py:757:16:757:19 | ControlFlowNode for arg2 | classes.py:748:15:748:19 | ControlFlowNode for other | +| classes.py:793:17:793:20 | ControlFlowNode for arg2 | classes.py:778:15:778:19 | ControlFlowNode for other | +| classes.py:808:20:808:23 | ControlFlowNode for arg2 | classes.py:799:15:799:19 | ControlFlowNode for other | +| classes.py:823:20:823:23 | ControlFlowNode for arg2 | classes.py:814:15:814:19 | ControlFlowNode for other | +| classes.py:838:16:838:19 | ControlFlowNode for arg2 | classes.py:829:15:829:19 | ControlFlowNode for other | +| classes.py:853:16:853:19 | ControlFlowNode for arg2 | classes.py:844:15:844:19 | ControlFlowNode for other | +| classes.py:868:15:868:18 | ControlFlowNode for arg2 | classes.py:859:15:859:19 | ControlFlowNode for other | diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.ql b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.ql index 4baa3b35714..d362753d573 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.ql +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.ql @@ -21,7 +21,7 @@ class ArgumentRoutingConfig extends DataFlow::Configuration { from DataFlow::Node source, DataFlow::Node sink where - source.getLocation().getFile().getBaseName() = "classes.py" and - sink.getLocation().getFile().getBaseName() = "classes.py" and + source.getLocation().getFile().getBaseName() in ["classes.py", "argumentPassing.py"] and + sink.getLocation().getFile().getBaseName() in ["classes.py", "argumentPassing.py"] and exists(ArgumentRoutingConfig cfg | cfg.hasFlow(source, sink)) select source, sink diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected index d2b9bb03301..6ce32c993f9 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected @@ -1 +1,2 @@ -| classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:630:15:630:19 | ControlFlowNode for value | +| argumentPassing.py:51:34:51:37 | ControlFlowNode for arg3 | argumentPassing.py:45:11:45:11 | ControlFlowNode for c | +| classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:571:15:571:19 | ControlFlowNode for value | diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.ql b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.ql index 3aa76402a74..0900bd2cf8e 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.ql +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.ql @@ -21,7 +21,7 @@ class ArgumentRoutingConfig extends DataFlow::Configuration { from DataFlow::Node source, DataFlow::Node sink where - source.getLocation().getFile().getBaseName() = "classes.py" and - sink.getLocation().getFile().getBaseName() = "classes.py" and + source.getLocation().getFile().getBaseName() in ["classes.py", "argumentPassing.py"] and + sink.getLocation().getFile().getBaseName() in ["classes.py", "argumentPassing.py"] and exists(ArgumentRoutingConfig cfg | cfg.hasFlow(source, sink)) select source, sink diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting4.ql b/python/ql/test/experimental/dataflow/coverage/argumentRouting4.ql index a4ae253de6c..170c45d88ba 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting4.ql +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting4.ql @@ -21,7 +21,7 @@ class ArgumentRoutingConfig extends DataFlow::Configuration { from DataFlow::Node source, DataFlow::Node sink where - source.getLocation().getFile().getBaseName() = "classes.py" and - sink.getLocation().getFile().getBaseName() = "classes.py" and + source.getLocation().getFile().getBaseName() in ["classes.py", "argumentPassing.py"] and + sink.getLocation().getFile().getBaseName() in ["classes.py", "argumentPassing.py"] and exists(ArgumentRoutingConfig cfg | cfg.hasFlow(source, sink)) select source, sink diff --git a/python/ql/test/experimental/dataflow/coverage/classes.py b/python/ql/test/experimental/dataflow/coverage/classes.py index 830ad72d1c5..1977998ab64 100644 --- a/python/ql/test/experimental/dataflow/coverage/classes.py +++ b/python/ql/test/experimental/dataflow/coverage/classes.py @@ -5,9 +5,15 @@ # These tests should cover all the class calls that we hope to support. # It is based on https://docs.python.org/3/reference/datamodel.html, and headings refer there. # -# All functions starting with "test_" should run and execute `print("OK")` one or more times. +# All functions starting with "test_" should run and execute `print("OK")` exactly once. # This can be checked by running validTest.py. +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname((__file__)))) +from testlib import * + import asyncio @@ -30,11 +36,9 @@ def SINK4(x): def OK(): print("OK") + # object.__new__(cls[, ...]) - - class With_new: - def __new__(cls): SINK1(cls) # Flow not found OK() # Call not found @@ -44,11 +48,9 @@ class With_new: def test_new(): with_new = With_new() + # object.__init__(self[, ...]) - - class With_init: - def __init__(self): SINK1(self) OK() @@ -57,11 +59,9 @@ class With_init: def test_init(): with_init = With_init() + # object.__del__(self) - - class With_del: - def __del__(self): SINK1(self) # Flow not found OK() # Call not found @@ -71,11 +71,9 @@ def test_del(): with_del = With_del() del with_del + # object.__repr__(self) - - class With_repr: - def __repr__(self): SINK1(self) # Flow not found OK() # Call not found @@ -86,11 +84,9 @@ def test_repr(): with_repr = With_repr() repr(with_repr) + # object.__str__(self) - - class With_str: - def __str__(self): SINK1(self) # Flow not found OK() # Call not found @@ -101,11 +97,9 @@ def test_str(): with_str = With_str() str(with_str) + # object.__bytes__(self) - - class With_bytes: - def __bytes__(self): SINK1(self) # Flow not found OK() # Call not found @@ -116,11 +110,9 @@ def test_bytes(): with_bytes = With_bytes() bytes(with_bytes) + # object.__format__(self, format_spec) - - class With_format: - def __format__(self, format_spec): SINK2(format_spec) # Flow not found SINK1(self) # Flow not found @@ -143,11 +135,9 @@ def test_format_fstr(): with_format = With_format() f"{with_format}" + # object.__lt__(self, other) - - class With_lt: - def __lt__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -160,11 +150,9 @@ def test_lt(): arg2 = with_lt with_lt < arg2 + # object.__le__(self, other) - - class With_le: - def __le__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -177,11 +165,9 @@ def test_le(): arg2 = with_le with_le <= arg2 + # object.__eq__(self, other) - - class With_eq: - def __eq__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -193,11 +179,9 @@ def test_eq(): with_eq = With_eq() with_eq == with_eq + # object.__ne__(self, other) - - class With_ne: - def __ne__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -209,11 +193,9 @@ def test_ne(): with_ne = With_ne() with_ne != with_ne + # object.__gt__(self, other) - - class With_gt: - def __gt__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -226,11 +208,9 @@ def test_gt(): arg2 = with_gt with_gt > arg2 + # object.__ge__(self, other) - - class With_ge: - def __ge__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -243,11 +223,9 @@ def test_ge(): arg2 = with_ge with_ge >= arg2 + # object.__hash__(self) - - class With_hash: - def __hash__(self): SINK1(self) # Flow not found OK() # Call not found @@ -273,11 +251,9 @@ def test_hash_dict(): with_hash = With_hash() len(dict({with_hash: 0})) + # object.__bool__(self) - - class With_bool: - def __bool__(self): SINK1(self) # Flow not found OK() # Call not found @@ -294,12 +270,10 @@ def test_bool_if(): if with_bool: pass + # 3.3.2. Customizing attribute access # object.__getattr__(self, name) - - class With_getattr: - def __getattr__(self, name): SINK2(name) # Flow not found SINK1(self) # Flow not found @@ -311,11 +285,9 @@ def test_getattr(): with_getattr = With_getattr() with_getattr.arg2 + # object.__getattribute__(self, name) - - class With_getattribute: - def __getattribute__(self, name): SINK2(name) # Flow not found SINK1(self) # Flow not found @@ -327,11 +299,9 @@ def test_getattribute(): with_getattribute = With_getattribute() with_getattribute.arg2 + # object.__setattr__(self, name, value) - - class With_setattr: - def __setattr__(self, name, value): SINK3(value) # Flow not found SINK2(name) # Flow not found @@ -344,11 +314,9 @@ def test_setattr(): arg3 = "" with_setattr.arg2 = arg3 + # object.__delattr__(self, name) - - class With_delattr: - def __delattr__(self, name): SINK2(name) # Flow not found SINK1(self) # Flow not found @@ -359,11 +327,9 @@ def test_delattr(): with_delattr = With_delattr() del with_delattr.arg2 + # object.__dir__(self) - - class With_dir: - def __dir__(self): SINK1(self) # Flow not found OK() # Call not found @@ -379,11 +345,9 @@ def test_dir(): class Owner: pass + # object.__get__(self, instance, owner=None) - - class With_get: - def __get__(self, instance, owner=None): SINK3(owner) # Flow not testsed, use class `Owner` as source to test SINK2(instance) # Flow not found @@ -401,11 +365,9 @@ def test_get(): arg2 = arg3() arg2.attr + # object.__set__(self, instance, value) - - class With_set: - def __set__(self, instance, value): SINK3(value) # Flow not found SINK2(instance) # Flow not found @@ -420,11 +382,9 @@ def test_set(): arg3 = "" arg2.attr = arg3 + # object.__delete__(self, instance) - - class With_delete: - def __delete__(self, instance): SINK2(instance) # Flow not found SINK1(self) # Flow not found @@ -437,11 +397,9 @@ def test_delete(): arg2 = Owner() del arg2.attr + # object.__set_name__(self, owner, name) - - class With_set_name: - def __set_name__(self, owner, name): SINK3(name) # Flow not found SINK2(owner) # Flow not found @@ -453,6 +411,7 @@ def test_set_name(): with_set_name = With_set_name() type("arg2", (object,), dict(arg3=with_set_name)) + # 3.3.2.4. __slots__ // We are not testing the suppression of -weakref_ and _dict_ here # object.__slots__ # __weakref__ @@ -460,10 +419,7 @@ def test_set_name(): # 3.3.3. Customizing class creation # classmethod object.__init_subclass__(cls) - - class With_init_subclass: - def __init_subclass__(cls): SINK1(cls) # Flow not found OK() # Call not found @@ -472,6 +428,7 @@ class With_init_subclass: def test_init_subclass(): type("Subclass", (With_init_subclass,), {}) + # 3.3.3.1. Metaclasses # By default, classes are constructed using type(). The class body is executed in a new namespace and the class name is bound locally to the result of type(name, bases, namespace). @@ -480,10 +437,7 @@ def test_init_subclass(): # 3.3.3.4. Preparing the class namespace # metaclass.__prepare__(name, bases, **kwds) - - class With_prepare(type): - def __prepare__(name, bases, **kwds): SINK3(kwds) # Flow not tested SINK2(bases) # Flow not tested @@ -496,12 +450,10 @@ def test_prepare(): class arg1(metaclass=With_prepare): pass + # 3.3.4. Customizing instance and subclass checks # class.__instancecheck__(self, instance) - - class With_instancecheck: - def __instancecheck__(self, instance): SINK2(instance) # Flow not found SINK1(self) # Flow not found @@ -514,11 +466,9 @@ def test_instancecheck(): arg2 = "" isinstance(arg2, with_instancecheck) + # class.__subclasscheck__(self, subclass) - - class With_subclasscheck: - def __subclasscheck__(self, subclass): SINK2(subclass) # Flow not found SINK1(self) # Flow not found @@ -535,7 +485,6 @@ def test_subclasscheck(): # 3.3.5. Emulating generic types # classmethod object.__class_getitem__(cls, key) class With_class_getitem: - def __class_getitem__(cls, key): SINK2(key) # Flow not found SINK1(cls) # Flow not found @@ -551,7 +500,6 @@ def test_class_getitem(): # 3.3.6. Emulating callable objects # object.__call__(self[, args...]) class With_call: - def __call__(self): SINK1(self) # Flow not found OK() # Call not found @@ -561,12 +509,10 @@ def test_call(): with_call = With_call() with_call() + # 3.3.7. Emulating container types # object.__len__(self) - - class With_len: - def __len__(self): SINK1(self) # Flow not found OK() # Call not found @@ -588,11 +534,9 @@ def test_len_if(): if with_len: pass + # object.__length_hint__(self) - - class With_length_hint: - def __length_hint__(self): SINK1(self) # Flow not found OK() # Call not found @@ -601,14 +545,13 @@ class With_length_hint: def test_length_hint(): import operator + with_length_hint = With_length_hint() operator.length_hint(with_length_hint) + # object.__getitem__(self, key) - - class With_getitem: - def __getitem__(self, key): SINK2(key) SINK1(self) @@ -621,11 +564,9 @@ def test_getitem(): arg2 = 0 with_getitem[arg2] + # object.__setitem__(self, key, value) - - class With_setitem: - def __setitem__(self, key, value): SINK3(value) SINK2(key) @@ -639,11 +580,9 @@ def test_setitem(): arg3 = "" with_setitem[arg2] = arg3 + # object.__delitem__(self, key) - - class With_delitem: - def __delitem__(self, key): SINK2(key) SINK1(self) @@ -655,11 +594,9 @@ def test_delitem(): arg2 = 0 del with_delitem[arg2] + # object.__missing__(self, key) - - class With_missing(dict): - def __missing__(self, key): SINK2(key) # Flow not found SINK1(self) # Flow not found @@ -672,11 +609,9 @@ def test_missing(): arg2 = 0 with_missing[arg2] + # object.__iter__(self) - - class With_iter: - def __iter__(self): SINK1(self) # Flow not found OK() # Call not found @@ -687,11 +622,9 @@ def test_iter(): with_iter = With_iter() [x for x in with_iter] + # object.__reversed__(self) - - class With_reversed: - def __reversed__(self): SINK1(self) # Flow not found OK() # Call not found @@ -702,11 +635,9 @@ def test_reversed(): with_reversed = With_reversed() reversed(with_reversed) + # object.__contains__(self, item) - - class With_contains: - def __contains__(self, item): SINK2(item) # Flow not found SINK1(self) # Flow not found @@ -723,7 +654,6 @@ def test_contains(): # 3.3.8. Emulating numeric types # object.__add__(self, other) class With_add: - def __add__(self, other): SINK2(other) SINK1(self) @@ -736,11 +666,9 @@ def test_add(): arg2 = with_add with_add + arg2 + # object.__sub__(self, other) - - class With_sub: - def __sub__(self, other): SINK2(other) SINK1(self) @@ -753,11 +681,9 @@ def test_sub(): arg2 = with_sub with_sub - arg2 + # object.__mul__(self, other) - - class With_mul: - def __mul__(self, other): SINK2(other) SINK1(self) @@ -770,11 +696,9 @@ def test_mul(): arg2 = with_mul with_mul * arg2 + # object.__matmul__(self, other) - - class With_matmul: - def __matmul__(self, other): SINK2(other) SINK1(self) @@ -787,11 +711,9 @@ def test_matmul(): arg2 = with_matmul with_matmul @ arg2 + # object.__truediv__(self, other) - - class With_truediv: - def __truediv__(self, other): SINK2(other) SINK1(self) @@ -804,11 +726,9 @@ def test_truediv(): arg2 = with_truediv with_truediv / arg2 + # object.__floordiv__(self, other) - - class With_floordiv: - def __floordiv__(self, other): SINK2(other) SINK1(self) @@ -821,11 +741,9 @@ def test_floordiv(): arg2 = with_floordiv with_floordiv // arg2 + # object.__mod__(self, other) - - class With_mod: - def __mod__(self, other): SINK2(other) SINK1(self) @@ -838,11 +756,9 @@ def test_mod(): arg2 = with_mod with_mod % arg2 + # object.__divmod__(self, other) - - class With_divmod: - def __divmod__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -855,11 +771,9 @@ def test_divmod(): arg2 = With_divmod divmod(with_divmod, arg2) + # object.__pow__(self, other[, modulo]) - - class With_pow: - def __pow__(self, other): SINK2(other) SINK1(self) @@ -878,11 +792,9 @@ def test_pow_op(): arg2 = with_pow with_pow ** arg2 + # object.__lshift__(self, other) - - class With_lshift: - def __lshift__(self, other): SINK2(other) SINK1(self) @@ -895,11 +807,9 @@ def test_lshift(): arg2 = with_lshift with_lshift << arg2 + # object.__rshift__(self, other) - - class With_rshift: - def __rshift__(self, other): SINK2(other) SINK1(self) @@ -912,11 +822,9 @@ def test_rshift(): arg2 = with_rshift with_rshift >> arg2 + # object.__and__(self, other) - - class With_and: - def __and__(self, other): SINK2(other) SINK1(self) @@ -929,11 +837,9 @@ def test_and(): arg2 = with_and with_and & arg2 + # object.__xor__(self, other) - - class With_xor: - def __xor__(self, other): SINK2(other) SINK1(self) @@ -946,11 +852,9 @@ def test_xor(): arg2 = with_xor with_xor ^ arg2 + # object.__or__(self, other) - - class With_or: - def __or__(self, other): SINK2(other) SINK1(self) @@ -963,11 +867,9 @@ def test_or(): arg2 = with_or with_or | arg2 + # object.__radd__(self, other) - - class With_radd: - def __radd__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -980,11 +882,9 @@ def test_radd(): arg2 = "" arg2 + with_radd + # object.__rsub__(self, other) - - class With_rsub: - def __rsub__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -997,11 +897,9 @@ def test_rsub(): arg2 = "" arg2 - with_rsub + # object.__rmul__(self, other) - - class With_rmul: - def __rmul__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1014,11 +912,9 @@ def test_rmul(): arg2 = "" arg2 * with_rmul + # object.__rmatmul__(self, other) - - class With_rmatmul: - def __rmatmul__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1031,11 +927,9 @@ def test_rmatmul(): arg2 = "" arg2 @ with_rmatmul + # object.__rtruediv__(self, other) - - class With_rtruediv: - def __rtruediv__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1048,11 +942,9 @@ def test_rtruediv(): arg2 = "" arg2 / with_rtruediv + # object.__rfloordiv__(self, other) - - class With_rfloordiv: - def __rfloordiv__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1065,11 +957,9 @@ def test_rfloordiv(): arg2 = "" arg2 // with_rfloordiv + # object.__rmod__(self, other) - - class With_rmod: - def __rmod__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1082,11 +972,9 @@ def test_rmod(): arg2 = {} arg2 % with_rmod + # object.__rdivmod__(self, other) - - class With_rdivmod: - def __rdivmod__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1099,11 +987,9 @@ def test_rdivmod(): arg2 = "" divmod(arg2, with_rdivmod) + # object.__rpow__(self, other[, modulo]) - - class With_rpow: - def __rpow__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1122,11 +1008,9 @@ def test_rpow_op(): arg2 = "" arg2 ** with_rpow + # object.__rlshift__(self, other) - - class With_rlshift: - def __rlshift__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1139,11 +1023,9 @@ def test_rlshift(): arg2 = "" arg2 << with_rlshift + # object.__rrshift__(self, other) - - class With_rrshift: - def __rrshift__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1156,11 +1038,9 @@ def test_rrshift(): arg2 = "" arg2 >> with_rrshift + # object.__rand__(self, other) - - class With_rand: - def __rand__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1173,11 +1053,9 @@ def test_rand(): arg2 = "" arg2 & with_rand + # object.__rxor__(self, other) - - class With_rxor: - def __rxor__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1190,11 +1068,9 @@ def test_rxor(): arg2 = "" arg2 ^ with_rxor + # object.__ror__(self, other) - - class With_ror: - def __ror__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1207,11 +1083,9 @@ def test_ror(): arg2 = "" arg2 | with_ror + # object.__iadd__(self, other) - - class With_iadd: - def __iadd__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1224,11 +1098,9 @@ def test_iadd(): arg2 = with_iadd with_iadd += arg2 + # object.__isub__(self, other) - - class With_isub: - def __isub__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1241,11 +1113,9 @@ def test_isub(): arg2 = with_isub with_isub -= arg2 + # object.__imul__(self, other) - - class With_imul: - def __imul__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1258,11 +1128,9 @@ def test_imul(): arg2 = with_imul with_imul *= arg2 + # object.__imatmul__(self, other) - - class With_imatmul: - def __imatmul__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1275,11 +1143,9 @@ def test_imatmul(): arg2 = with_imatmul with_imatmul @= arg2 + # object.__itruediv__(self, other) - - class With_itruediv: - def __itruediv__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1292,11 +1158,9 @@ def test_itruediv(): arg2 = with_itruediv with_itruediv /= arg2 + # object.__ifloordiv__(self, other) - - class With_ifloordiv: - def __ifloordiv__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1309,11 +1173,9 @@ def test_ifloordiv(): arg2 = with_ifloordiv with_ifloordiv //= arg2 + # object.__imod__(self, other) - - class With_imod: - def __imod__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1326,11 +1188,9 @@ def test_imod(): arg2 = with_imod with_imod %= arg2 + # object.__ipow__(self, other[, modulo]) - - class With_ipow: - def __ipow__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1343,11 +1203,9 @@ def test_ipow(): arg2 = with_ipow with_ipow **= arg2 + # object.__ilshift__(self, other) - - class With_ilshift: - def __ilshift__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1360,11 +1218,9 @@ def test_ilshift(): arg2 = with_ilshift with_ilshift <<= arg2 + # object.__irshift__(self, other) - - class With_irshift: - def __irshift__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1377,11 +1233,9 @@ def test_irshift(): arg2 = with_irshift with_irshift >>= arg2 + # object.__iand__(self, other) - - class With_iand: - def __iand__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1394,11 +1248,9 @@ def test_iand(): arg2 = with_iand with_iand &= arg2 + # object.__ixor__(self, other) - - class With_ixor: - def __ixor__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1411,11 +1263,9 @@ def test_ixor(): arg2 = with_ixor with_ixor ^= arg2 + # object.__ior__(self, other) - - class With_ior: - def __ior__(self, other): SINK2(other) # Flow not found SINK1(self) # Flow not found @@ -1428,11 +1278,9 @@ def test_ior(): arg2 = with_ior with_ior |= arg2 + # object.__neg__(self) - - class With_neg: - def __neg__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1443,11 +1291,9 @@ def test_neg(): with_neg = With_neg() -with_neg + # object.__pos__(self) - - class With_pos: - def __pos__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1458,11 +1304,9 @@ def test_pos(): with_pos = With_pos() +with_pos + # object.__abs__(self) - - class With_abs: - def __abs__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1473,11 +1317,9 @@ def test_abs(): with_abs = With_abs() abs(with_abs) + # object.__invert__(self) - - class With_invert: - def __invert__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1488,11 +1330,9 @@ def test_invert(): with_invert = With_invert() ~with_invert + # object.__complex__(self) - - class With_complex: - def __complex__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1503,11 +1343,9 @@ def test_complex(): with_complex = With_complex() complex(with_complex) + # object.__int__(self) - - class With_int: - def __int__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1518,11 +1356,9 @@ def test_int(): with_int = With_int() int(with_int) + # object.__float__(self) - - class With_float: - def __float__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1533,11 +1369,9 @@ def test_float(): with_float = With_float() float(with_float) + # object.__index__(self) - - class With_index: - def __index__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1546,6 +1380,7 @@ class With_index: def test_index(): import operator + with_index = With_index() operator.index(with_index) @@ -1584,11 +1419,9 @@ def test_index_complex(): with_index = With_index() complex(with_index) + # object.__round__(self[, ndigits]) - - class With_round: - def __round__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1599,11 +1432,9 @@ def test_round(): with_round = With_round() round(with_round) + # object.__trunc__(self) - - class With_trunc: - def __trunc__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1613,13 +1444,12 @@ class With_trunc: def test_trunc(): with_trunc = With_trunc() import math + math.trunc(with_trunc) + # object.__floor__(self) - - class With_floor: - def __floor__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1629,13 +1459,12 @@ class With_floor: def test_floor(): with_floor = With_floor() import math + math.floor(with_floor) + # object.__ceil__(self) - - class With_ceil: - def __ceil__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1645,13 +1474,13 @@ class With_ceil: def test_ceil(): with_ceil = With_ceil() import math + math.ceil(with_ceil) # 3.3.9. With Statement Context Managers # object.__enter__(self) class With_enter: - def __enter__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1665,11 +1494,9 @@ def test_enter(): with With_enter(): pass + # object.__exit__(self, exc_type, exc_value, traceback) - - class With_exit: - def __enter__(self): return @@ -1690,9 +1517,7 @@ def test_exit(): # 3.4.1. Awaitable Objects # object.__await__(self) - class With_await: - def __await__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1701,7 +1526,7 @@ class With_await: async def atest_await(): with_await = With_await() - await(with_await) + await (with_await) # # 3.4.2. Coroutine Objects // These should be handled as normal function calls @@ -1712,7 +1537,6 @@ async def atest_await(): # 3.4.3. Asynchronous Iterators # object.__aiter__(self) class With_aiter: - def __aiter__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1727,11 +1551,9 @@ async def atest_aiter(): async for x in with_aiter: pass + # object.__anext__(self) - - class With_anext: - def __aiter__(self): return self @@ -1750,7 +1572,6 @@ async def atest_anext(): # 3.4.4. Asynchronous Context Managers # object.__aenter__(self) class With_aenter: - async def __aenter__(self): SINK1(self) # Flow not found OK() # Call not found @@ -1764,11 +1585,9 @@ async def atest_aenter(): async with with_aenter: pass + # object.__aexit__(self, exc_type, exc_value, traceback) - - class With_aexit: - async def __aenter__(self): pass diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index 379b44a3090..6eda1e4152f 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -41,116 +41,116 @@ edges | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:139:10:139:15 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:152:15:152:20 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:157:15:157:20 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:184:23:184:28 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:189:25:189:30 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:200:34:200:39 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:344:11:344:16 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:348:11:348:16 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:352:16:352:21 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:375:28:375:33 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:457:12:457:17 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:462:28:462:33 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:504:9:504:14 | ControlFlowNode for SOURCE | -| test.py:14:1:14:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | -| test.py:14:10:14:17 | ControlFlowNode for Str | test.py:14:1:14:6 | GSSA Variable SOURCE | -| test.py:36:10:36:26 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:37:9:37:9 | ControlFlowNode for x [Tuple element at index 1] | -| test.py:36:21:36:26 | ControlFlowNode for SOURCE | test.py:36:10:36:26 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:37:9:37:9 | ControlFlowNode for x [Tuple element at index 1] | test.py:37:9:37:12 | ControlFlowNode for Subscript | -| test.py:37:9:37:12 | ControlFlowNode for Subscript | test.py:38:10:38:10 | ControlFlowNode for y | -| test.py:50:9:50:14 | ControlFlowNode for SOURCE | test.py:51:10:51:10 | ControlFlowNode for x | -| test.py:57:9:57:16 | ControlFlowNode for Str | test.py:58:10:58:10 | ControlFlowNode for x | -| test.py:62:9:62:17 | ControlFlowNode for Str | test.py:63:10:63:10 | ControlFlowNode for x | -| test.py:67:9:67:10 | ControlFlowNode for IntegerLiteral | test.py:68:10:68:10 | ControlFlowNode for x | -| test.py:72:9:72:12 | ControlFlowNode for FloatLiteral | test.py:73:10:73:10 | ControlFlowNode for x | -| test.py:84:10:84:15 | ControlFlowNode for SOURCE | test.py:85:10:85:10 | ControlFlowNode for x | -| test.py:91:9:91:16 | ControlFlowNode for List [List element] | test.py:92:10:92:10 | ControlFlowNode for x [List element] | -| test.py:91:10:91:15 | ControlFlowNode for SOURCE | test.py:91:9:91:16 | ControlFlowNode for List [List element] | -| test.py:92:10:92:10 | ControlFlowNode for x [List element] | test.py:92:10:92:13 | ControlFlowNode for Subscript | -| test.py:101:9:101:37 | ControlFlowNode for ListComp [List element] | test.py:102:10:102:10 | ControlFlowNode for x [List element] | -| test.py:101:10:101:15 | ControlFlowNode for SOURCE | test.py:101:9:101:37 | ControlFlowNode for ListComp [List element] | -| test.py:102:10:102:10 | ControlFlowNode for x [List element] | test.py:102:10:102:13 | ControlFlowNode for Subscript | -| test.py:106:9:106:29 | ControlFlowNode for ListComp [List element] | test.py:107:10:107:10 | ControlFlowNode for x [List element] | -| test.py:106:10:106:10 | ControlFlowNode for y | test.py:106:9:106:29 | ControlFlowNode for ListComp [List element] | -| test.py:106:16:106:16 | SSA variable y | test.py:106:10:106:10 | ControlFlowNode for y | -| test.py:106:21:106:28 | ControlFlowNode for List [List element] | test.py:106:16:106:16 | SSA variable y | -| test.py:106:22:106:27 | ControlFlowNode for SOURCE | test.py:106:21:106:28 | ControlFlowNode for List [List element] | -| test.py:107:10:107:10 | ControlFlowNode for x [List element] | test.py:107:10:107:13 | ControlFlowNode for Subscript | -| test.py:111:9:111:16 | ControlFlowNode for List [List element] | test.py:112:21:112:21 | ControlFlowNode for l [List element] | -| test.py:111:10:111:15 | ControlFlowNode for SOURCE | test.py:111:9:111:16 | ControlFlowNode for List [List element] | -| test.py:112:9:112:22 | ControlFlowNode for ListComp [List element] | test.py:113:10:113:10 | ControlFlowNode for x [List element] | -| test.py:112:10:112:10 | ControlFlowNode for y | test.py:112:9:112:22 | ControlFlowNode for ListComp [List element] | -| test.py:112:16:112:16 | SSA variable y | test.py:112:10:112:10 | ControlFlowNode for y | -| test.py:112:21:112:21 | ControlFlowNode for l [List element] | test.py:112:16:112:16 | SSA variable y | -| test.py:113:10:113:10 | ControlFlowNode for x [List element] | test.py:113:10:113:13 | ControlFlowNode for Subscript | -| test.py:124:9:124:16 | ControlFlowNode for Set [List element] | test.py:125:10:125:10 | ControlFlowNode for x [List element] | -| test.py:124:10:124:15 | ControlFlowNode for SOURCE | test.py:124:9:124:16 | ControlFlowNode for Set [List element] | -| test.py:125:10:125:10 | ControlFlowNode for x [List element] | test.py:125:10:125:16 | ControlFlowNode for Attribute() | -| test.py:129:9:129:37 | ControlFlowNode for SetComp [Set element] | test.py:130:10:130:10 | ControlFlowNode for x [Set element] | -| test.py:129:10:129:15 | ControlFlowNode for SOURCE | test.py:129:9:129:37 | ControlFlowNode for SetComp [Set element] | -| test.py:130:10:130:10 | ControlFlowNode for x [Set element] | test.py:130:10:130:16 | ControlFlowNode for Attribute() | -| test.py:134:9:134:29 | ControlFlowNode for SetComp [Set element] | test.py:135:10:135:10 | ControlFlowNode for x [Set element] | -| test.py:134:10:134:10 | ControlFlowNode for y | test.py:134:9:134:29 | ControlFlowNode for SetComp [Set element] | -| test.py:134:16:134:16 | SSA variable y | test.py:134:10:134:10 | ControlFlowNode for y | -| test.py:134:21:134:28 | ControlFlowNode for List [List element] | test.py:134:16:134:16 | SSA variable y | -| test.py:134:22:134:27 | ControlFlowNode for SOURCE | test.py:134:21:134:28 | ControlFlowNode for List [List element] | -| test.py:135:10:135:10 | ControlFlowNode for x [Set element] | test.py:135:10:135:16 | ControlFlowNode for Attribute() | -| test.py:139:9:139:16 | ControlFlowNode for Set [List element] | test.py:140:21:140:21 | ControlFlowNode for l [List element] | -| test.py:139:10:139:15 | ControlFlowNode for SOURCE | test.py:139:9:139:16 | ControlFlowNode for Set [List element] | -| test.py:140:9:140:22 | ControlFlowNode for SetComp [Set element] | test.py:141:10:141:10 | ControlFlowNode for x [Set element] | -| test.py:140:10:140:10 | ControlFlowNode for y | test.py:140:9:140:22 | ControlFlowNode for SetComp [Set element] | -| test.py:140:16:140:16 | SSA variable y | test.py:140:10:140:10 | ControlFlowNode for y | -| test.py:140:21:140:21 | ControlFlowNode for l [List element] | test.py:140:16:140:16 | SSA variable y | -| test.py:141:10:141:10 | ControlFlowNode for x [Set element] | test.py:141:10:141:16 | ControlFlowNode for Attribute() | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:183:23:183:28 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:188:25:188:30 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:199:34:199:39 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:336:11:336:16 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:340:11:340:16 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:344:16:344:21 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:365:28:365:33 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:442:12:442:17 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:449:28:449:33 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:499:9:499:14 | ControlFlowNode for SOURCE | +| test.py:20:1:20:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | +| test.py:20:10:20:17 | ControlFlowNode for Str | test.py:20:1:20:6 | GSSA Variable SOURCE | +| test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | +| test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | test.py:43:9:43:12 | ControlFlowNode for Subscript | +| test.py:43:9:43:12 | ControlFlowNode for Subscript | test.py:44:10:44:10 | ControlFlowNode for y | +| test.py:55:9:55:14 | ControlFlowNode for SOURCE | test.py:56:10:56:10 | ControlFlowNode for x | +| test.py:61:9:61:16 | ControlFlowNode for Str | test.py:62:10:62:10 | ControlFlowNode for x | +| test.py:66:9:66:17 | ControlFlowNode for Str | test.py:67:10:67:10 | ControlFlowNode for x | +| test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | test.py:72:10:72:10 | ControlFlowNode for x | +| test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | test.py:77:10:77:10 | ControlFlowNode for x | +| test.py:87:9:87:14 | ControlFlowNode for SOURCE | test.py:88:10:88:10 | ControlFlowNode for x | +| test.py:93:9:93:16 | ControlFlowNode for List [List element] | test.py:94:10:94:10 | ControlFlowNode for x [List element] | +| test.py:93:10:93:15 | ControlFlowNode for SOURCE | test.py:93:9:93:16 | ControlFlowNode for List [List element] | +| test.py:94:10:94:10 | ControlFlowNode for x [List element] | test.py:94:10:94:13 | ControlFlowNode for Subscript | +| test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | test.py:104:10:104:10 | ControlFlowNode for x [List element] | +| test.py:103:10:103:15 | ControlFlowNode for SOURCE | test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | +| test.py:104:10:104:10 | ControlFlowNode for x [List element] | test.py:104:10:104:13 | ControlFlowNode for Subscript | +| test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | test.py:109:10:109:10 | ControlFlowNode for x [List element] | +| test.py:108:10:108:10 | ControlFlowNode for y | test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | +| test.py:108:16:108:16 | SSA variable y | test.py:108:10:108:10 | ControlFlowNode for y | +| test.py:108:21:108:28 | ControlFlowNode for List [List element] | test.py:108:16:108:16 | SSA variable y | +| test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:108:21:108:28 | ControlFlowNode for List [List element] | +| test.py:109:10:109:10 | ControlFlowNode for x [List element] | test.py:109:10:109:13 | ControlFlowNode for Subscript | +| test.py:113:9:113:16 | ControlFlowNode for List [List element] | test.py:114:21:114:21 | ControlFlowNode for l [List element] | +| test.py:113:10:113:15 | ControlFlowNode for SOURCE | test.py:113:9:113:16 | ControlFlowNode for List [List element] | +| test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | test.py:115:10:115:10 | ControlFlowNode for x [List element] | +| test.py:114:10:114:10 | ControlFlowNode for y | test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | +| test.py:114:16:114:16 | SSA variable y | test.py:114:10:114:10 | ControlFlowNode for y | +| test.py:114:21:114:21 | ControlFlowNode for l [List element] | test.py:114:16:114:16 | SSA variable y | +| test.py:115:10:115:10 | ControlFlowNode for x [List element] | test.py:115:10:115:13 | ControlFlowNode for Subscript | +| test.py:125:9:125:16 | ControlFlowNode for Set [List element] | test.py:126:10:126:10 | ControlFlowNode for x [List element] | +| test.py:125:10:125:15 | ControlFlowNode for SOURCE | test.py:125:9:125:16 | ControlFlowNode for Set [List element] | +| test.py:126:10:126:10 | ControlFlowNode for x [List element] | test.py:126:10:126:16 | ControlFlowNode for Attribute() | +| test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | test.py:131:10:131:10 | ControlFlowNode for x [Set element] | +| test.py:130:10:130:15 | ControlFlowNode for SOURCE | test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | +| test.py:131:10:131:10 | ControlFlowNode for x [Set element] | test.py:131:10:131:16 | ControlFlowNode for Attribute() | +| test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | test.py:136:10:136:10 | ControlFlowNode for x [Set element] | +| test.py:135:10:135:10 | ControlFlowNode for y | test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | +| test.py:135:16:135:16 | SSA variable y | test.py:135:10:135:10 | ControlFlowNode for y | +| test.py:135:21:135:28 | ControlFlowNode for List [List element] | test.py:135:16:135:16 | SSA variable y | +| test.py:135:22:135:27 | ControlFlowNode for SOURCE | test.py:135:21:135:28 | ControlFlowNode for List [List element] | +| test.py:136:10:136:10 | ControlFlowNode for x [Set element] | test.py:136:10:136:16 | ControlFlowNode for Attribute() | +| test.py:140:9:140:16 | ControlFlowNode for Set [List element] | test.py:141:21:141:21 | ControlFlowNode for l [List element] | +| test.py:140:10:140:15 | ControlFlowNode for SOURCE | test.py:140:9:140:16 | ControlFlowNode for Set [List element] | +| test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | test.py:142:10:142:10 | ControlFlowNode for x [Set element] | +| test.py:141:10:141:10 | ControlFlowNode for y | test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | +| test.py:141:16:141:16 | SSA variable y | test.py:141:10:141:10 | ControlFlowNode for y | +| test.py:141:21:141:21 | ControlFlowNode for l [List element] | test.py:141:16:141:16 | SSA variable y | +| test.py:142:10:142:10 | ControlFlowNode for x [Set element] | test.py:142:10:142:16 | ControlFlowNode for Attribute() | | test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | | test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | | test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:153:10:153:15 | ControlFlowNode for Subscript | | test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | | test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | | test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:158:10:158:19 | ControlFlowNode for Attribute() | -| test.py:184:9:184:42 | ControlFlowNode for ListComp [List element] | test.py:185:10:185:10 | ControlFlowNode for x [List element] | -| test.py:184:10:184:10 | ControlFlowNode for y | test.py:184:9:184:42 | ControlFlowNode for ListComp [List element] | -| test.py:184:16:184:16 | SSA variable z [List element] | test.py:184:41:184:41 | ControlFlowNode for z [List element] | -| test.py:184:21:184:30 | ControlFlowNode for List [List element, List element] | test.py:184:16:184:16 | SSA variable z [List element] | -| test.py:184:22:184:29 | ControlFlowNode for List [List element] | test.py:184:21:184:30 | ControlFlowNode for List [List element, List element] | -| test.py:184:23:184:28 | ControlFlowNode for SOURCE | test.py:184:22:184:29 | ControlFlowNode for List [List element] | -| test.py:184:36:184:36 | SSA variable y | test.py:184:10:184:10 | ControlFlowNode for y | -| test.py:184:41:184:41 | ControlFlowNode for z [List element] | test.py:184:36:184:36 | SSA variable y | -| test.py:185:10:185:10 | ControlFlowNode for x [List element] | test.py:185:10:185:13 | ControlFlowNode for Subscript | -| test.py:189:9:189:68 | ControlFlowNode for ListComp [List element] | test.py:190:10:190:10 | ControlFlowNode for x [List element] | -| test.py:189:10:189:10 | ControlFlowNode for y | test.py:189:9:189:68 | ControlFlowNode for ListComp [List element] | -| test.py:189:16:189:16 | SSA variable v [List element, List element, ... (3)] | test.py:189:45:189:45 | ControlFlowNode for v [List element, List element, ... (3)] | -| test.py:189:21:189:34 | ControlFlowNode for List [List element, List element, ... (4)] | test.py:189:16:189:16 | SSA variable v [List element, List element, ... (3)] | -| test.py:189:22:189:33 | ControlFlowNode for List [List element, List element, ... (3)] | test.py:189:21:189:34 | ControlFlowNode for List [List element, List element, ... (4)] | -| test.py:189:23:189:32 | ControlFlowNode for List [List element, List element] | test.py:189:22:189:33 | ControlFlowNode for List [List element, List element, ... (3)] | -| test.py:189:24:189:31 | ControlFlowNode for List [List element] | test.py:189:23:189:32 | ControlFlowNode for List [List element, List element] | -| test.py:189:25:189:30 | ControlFlowNode for SOURCE | test.py:189:24:189:31 | ControlFlowNode for List [List element] | -| test.py:189:40:189:40 | SSA variable u [List element, List element] | test.py:189:56:189:56 | ControlFlowNode for u [List element, List element] | -| test.py:189:45:189:45 | ControlFlowNode for v [List element, List element, ... (3)] | test.py:189:40:189:40 | SSA variable u [List element, List element] | -| test.py:189:51:189:51 | SSA variable z [List element] | test.py:189:67:189:67 | ControlFlowNode for z [List element] | -| test.py:189:56:189:56 | ControlFlowNode for u [List element, List element] | test.py:189:51:189:51 | SSA variable z [List element] | -| test.py:189:62:189:62 | SSA variable y | test.py:189:10:189:10 | ControlFlowNode for y | -| test.py:189:67:189:67 | ControlFlowNode for z [List element] | test.py:189:62:189:62 | SSA variable y | -| test.py:190:10:190:10 | ControlFlowNode for x [List element] | test.py:190:10:190:13 | ControlFlowNode for Subscript | -| test.py:200:9:200:42 | ControlFlowNode for ListComp [List element] | test.py:201:10:201:10 | ControlFlowNode for x [List element] | -| test.py:200:10:200:10 | ControlFlowNode for y | test.py:200:9:200:42 | ControlFlowNode for ListComp [List element] | -| test.py:200:16:200:16 | SSA variable y | test.py:200:10:200:10 | ControlFlowNode for y | -| test.py:200:22:200:22 | ControlFlowNode for z | test.py:200:22:200:40 | ControlFlowNode for GeneratorExp [List element] | -| test.py:200:22:200:40 | ControlFlowNode for GeneratorExp [List element] | test.py:200:16:200:16 | SSA variable y | -| test.py:200:28:200:28 | SSA variable z | test.py:200:22:200:22 | ControlFlowNode for z | -| test.py:200:33:200:40 | ControlFlowNode for List [List element] | test.py:200:28:200:28 | SSA variable z | -| test.py:200:34:200:39 | ControlFlowNode for SOURCE | test.py:200:33:200:40 | ControlFlowNode for List [List element] | -| test.py:201:10:201:10 | ControlFlowNode for x [List element] | test.py:201:10:201:13 | ControlFlowNode for Subscript | -| test.py:344:11:344:16 | ControlFlowNode for SOURCE | test.py:344:11:344:17 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:344:11:344:17 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:344:10:344:21 | ControlFlowNode for Subscript | -| test.py:348:10:348:17 | ControlFlowNode for List [List element] | test.py:348:10:348:20 | ControlFlowNode for Subscript | -| test.py:348:11:348:16 | ControlFlowNode for SOURCE | test.py:348:10:348:17 | ControlFlowNode for List [List element] | -| test.py:352:10:352:22 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:352:10:352:27 | ControlFlowNode for Subscript | -| test.py:352:16:352:21 | ControlFlowNode for SOURCE | test.py:352:10:352:22 | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:375:28:375:33 | ControlFlowNode for SOURCE | test.py:375:10:375:34 | ControlFlowNode for second() | -| test.py:457:12:457:17 | ControlFlowNode for SOURCE | test.py:457:10:457:18 | ControlFlowNode for f() | -| test.py:462:28:462:33 | ControlFlowNode for SOURCE | test.py:462:10:462:34 | ControlFlowNode for second() | -| test.py:504:9:504:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for a | -| test.py:504:9:504:14 | ControlFlowNode for SOURCE | test.py:511:10:511:10 | ControlFlowNode for b | +| test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | test.py:184:10:184:10 | ControlFlowNode for x [List element] | +| test.py:183:10:183:10 | ControlFlowNode for y | test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | +| test.py:183:16:183:16 | SSA variable z [List element] | test.py:183:41:183:41 | ControlFlowNode for z [List element] | +| test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | test.py:183:16:183:16 | SSA variable z [List element] | +| test.py:183:22:183:29 | ControlFlowNode for List [List element] | test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | +| test.py:183:23:183:28 | ControlFlowNode for SOURCE | test.py:183:22:183:29 | ControlFlowNode for List [List element] | +| test.py:183:36:183:36 | SSA variable y | test.py:183:10:183:10 | ControlFlowNode for y | +| test.py:183:41:183:41 | ControlFlowNode for z [List element] | test.py:183:36:183:36 | SSA variable y | +| test.py:184:10:184:10 | ControlFlowNode for x [List element] | test.py:184:10:184:13 | ControlFlowNode for Subscript | +| test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | test.py:189:10:189:10 | ControlFlowNode for x [List element] | +| test.py:188:10:188:10 | ControlFlowNode for y | test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | +| test.py:188:16:188:16 | SSA variable v [List element, List element, ... (3)] | test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, ... (3)] | +| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, ... (4)] | test.py:188:16:188:16 | SSA variable v [List element, List element, ... (3)] | +| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, ... (3)] | test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, ... (4)] | +| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, ... (3)] | +| test.py:188:24:188:31 | ControlFlowNode for List [List element] | test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | +| test.py:188:25:188:30 | ControlFlowNode for SOURCE | test.py:188:24:188:31 | ControlFlowNode for List [List element] | +| test.py:188:40:188:40 | SSA variable u [List element, List element] | test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | +| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, ... (3)] | test.py:188:40:188:40 | SSA variable u [List element, List element] | +| test.py:188:51:188:51 | SSA variable z [List element] | test.py:188:67:188:67 | ControlFlowNode for z [List element] | +| test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | test.py:188:51:188:51 | SSA variable z [List element] | +| test.py:188:62:188:62 | SSA variable y | test.py:188:10:188:10 | ControlFlowNode for y | +| test.py:188:67:188:67 | ControlFlowNode for z [List element] | test.py:188:62:188:62 | SSA variable y | +| test.py:189:10:189:10 | ControlFlowNode for x [List element] | test.py:189:10:189:13 | ControlFlowNode for Subscript | +| test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | test.py:200:10:200:10 | ControlFlowNode for x [List element] | +| test.py:199:10:199:10 | ControlFlowNode for y | test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | +| test.py:199:16:199:16 | SSA variable y | test.py:199:10:199:10 | ControlFlowNode for y | +| test.py:199:22:199:22 | ControlFlowNode for z | test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | +| test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | test.py:199:16:199:16 | SSA variable y | +| test.py:199:28:199:28 | SSA variable z | test.py:199:22:199:22 | ControlFlowNode for z | +| test.py:199:33:199:40 | ControlFlowNode for List [List element] | test.py:199:28:199:28 | SSA variable z | +| test.py:199:34:199:39 | ControlFlowNode for SOURCE | test.py:199:33:199:40 | ControlFlowNode for List [List element] | +| test.py:200:10:200:10 | ControlFlowNode for x [List element] | test.py:200:10:200:13 | ControlFlowNode for Subscript | +| test.py:336:11:336:16 | ControlFlowNode for SOURCE | test.py:336:11:336:17 | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:336:11:336:17 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:336:10:336:21 | ControlFlowNode for Subscript | +| test.py:340:10:340:17 | ControlFlowNode for List [List element] | test.py:340:10:340:20 | ControlFlowNode for Subscript | +| test.py:340:11:340:16 | ControlFlowNode for SOURCE | test.py:340:10:340:17 | ControlFlowNode for List [List element] | +| test.py:344:10:344:22 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:344:10:344:27 | ControlFlowNode for Subscript | +| test.py:344:16:344:21 | ControlFlowNode for SOURCE | test.py:344:10:344:22 | ControlFlowNode for Dict [Dictionary element at key s] | +| test.py:365:28:365:33 | ControlFlowNode for SOURCE | test.py:365:10:365:34 | ControlFlowNode for second() | +| test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() | +| test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() | +| test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a | +| test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for b | nodes | datamodel.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module datamodel | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module datamodel | | datamodel.py:13:1:13:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | @@ -172,71 +172,71 @@ nodes | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] | | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module test | -| test.py:14:1:14:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | -| test.py:14:10:14:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| test.py:36:10:36:26 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:36:21:36:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:37:9:37:9 | ControlFlowNode for x [Tuple element at index 1] | semmle.label | ControlFlowNode for x [Tuple element at index 1] | -| test.py:37:9:37:12 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:38:10:38:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:50:9:50:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:51:10:51:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:57:9:57:16 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| test.py:58:10:58:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:62:9:62:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| test.py:63:10:63:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:67:9:67:10 | ControlFlowNode for IntegerLiteral | semmle.label | ControlFlowNode for IntegerLiteral | -| test.py:68:10:68:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:72:9:72:12 | ControlFlowNode for FloatLiteral | semmle.label | ControlFlowNode for FloatLiteral | -| test.py:73:10:73:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:84:10:84:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:85:10:85:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:91:9:91:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:91:10:91:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:92:10:92:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:92:10:92:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:101:9:101:37 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:101:10:101:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:102:10:102:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:102:10:102:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:106:9:106:29 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:106:10:106:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:106:16:106:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:106:21:106:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:106:22:106:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:107:10:107:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:107:10:107:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:111:9:111:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:111:10:111:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:112:9:112:22 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:112:10:112:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:112:16:112:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:112:21:112:21 | ControlFlowNode for l [List element] | semmle.label | ControlFlowNode for l [List element] | -| test.py:113:10:113:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:113:10:113:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:124:9:124:16 | ControlFlowNode for Set [List element] | semmle.label | ControlFlowNode for Set [List element] | -| test.py:124:10:124:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:125:10:125:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:125:10:125:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:129:9:129:37 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | -| test.py:129:10:129:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:130:10:130:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | -| test.py:130:10:130:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:134:9:134:29 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | -| test.py:134:10:134:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:134:16:134:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:134:21:134:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:134:22:134:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:135:10:135:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | -| test.py:135:10:135:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:139:9:139:16 | ControlFlowNode for Set [List element] | semmle.label | ControlFlowNode for Set [List element] | -| test.py:139:10:139:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:140:9:140:22 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | -| test.py:140:10:140:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:140:16:140:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:140:21:140:21 | ControlFlowNode for l [List element] | semmle.label | ControlFlowNode for l [List element] | -| test.py:141:10:141:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | -| test.py:141:10:141:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| test.py:20:1:20:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| test.py:20:10:20:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | +| test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | +| test.py:42:21:42:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | semmle.label | ControlFlowNode for x [Tuple element at index 1] | +| test.py:43:9:43:12 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:44:10:44:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:55:9:55:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:56:10:56:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:61:9:61:16 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | +| test.py:62:10:62:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:66:9:66:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | +| test.py:67:10:67:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | semmle.label | ControlFlowNode for IntegerLiteral | +| test.py:72:10:72:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | semmle.label | ControlFlowNode for FloatLiteral | +| test.py:77:10:77:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:87:9:87:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:88:10:88:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:93:9:93:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | +| test.py:93:10:93:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:94:10:94:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | +| test.py:94:10:94:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | +| test.py:103:10:103:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:104:10:104:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | +| test.py:104:10:104:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | +| test.py:108:10:108:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:108:16:108:16 | SSA variable y | semmle.label | SSA variable y | +| test.py:108:21:108:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | +| test.py:108:22:108:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:109:10:109:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | +| test.py:109:10:109:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:113:9:113:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | +| test.py:113:10:113:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | +| test.py:114:10:114:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:114:16:114:16 | SSA variable y | semmle.label | SSA variable y | +| test.py:114:21:114:21 | ControlFlowNode for l [List element] | semmle.label | ControlFlowNode for l [List element] | +| test.py:115:10:115:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | +| test.py:115:10:115:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:125:9:125:16 | ControlFlowNode for Set [List element] | semmle.label | ControlFlowNode for Set [List element] | +| test.py:125:10:125:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:126:10:126:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | +| test.py:126:10:126:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | +| test.py:130:10:130:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:131:10:131:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | +| test.py:131:10:131:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | +| test.py:135:10:135:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:135:16:135:16 | SSA variable y | semmle.label | SSA variable y | +| test.py:135:21:135:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | +| test.py:135:22:135:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:136:10:136:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | +| test.py:136:10:136:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| test.py:140:9:140:16 | ControlFlowNode for Set [List element] | semmle.label | ControlFlowNode for Set [List element] | +| test.py:140:10:140:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | +| test.py:141:10:141:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:141:16:141:16 | SSA variable y | semmle.label | SSA variable y | +| test.py:141:21:141:21 | ControlFlowNode for l [List element] | semmle.label | ControlFlowNode for l [List element] | +| test.py:142:10:142:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | +| test.py:142:10:142:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] | | test.py:152:15:152:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] | @@ -245,60 +245,60 @@ nodes | test.py:157:15:157:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] | | test.py:158:10:158:19 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:184:9:184:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:184:10:184:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:184:16:184:16 | SSA variable z [List element] | semmle.label | SSA variable z [List element] | -| test.py:184:21:184:30 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:184:22:184:29 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:184:23:184:28 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:184:36:184:36 | SSA variable y | semmle.label | SSA variable y | -| test.py:184:41:184:41 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] | -| test.py:185:10:185:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:185:10:185:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:189:9:189:68 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:189:10:189:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:189:16:189:16 | SSA variable v [List element, List element, ... (3)] | semmle.label | SSA variable v [List element, List element, ... (3)] | -| test.py:189:21:189:34 | ControlFlowNode for List [List element, List element, ... (4)] | semmle.label | ControlFlowNode for List [List element, List element, ... (4)] | -| test.py:189:22:189:33 | ControlFlowNode for List [List element, List element, ... (3)] | semmle.label | ControlFlowNode for List [List element, List element, ... (3)] | -| test.py:189:23:189:32 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:189:24:189:31 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:189:25:189:30 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:189:40:189:40 | SSA variable u [List element, List element] | semmle.label | SSA variable u [List element, List element] | -| test.py:189:45:189:45 | ControlFlowNode for v [List element, List element, ... (3)] | semmle.label | ControlFlowNode for v [List element, List element, ... (3)] | -| test.py:189:51:189:51 | SSA variable z [List element] | semmle.label | SSA variable z [List element] | -| test.py:189:56:189:56 | ControlFlowNode for u [List element, List element] | semmle.label | ControlFlowNode for u [List element, List element] | -| test.py:189:62:189:62 | SSA variable y | semmle.label | SSA variable y | -| test.py:189:67:189:67 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] | -| test.py:190:10:190:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:190:10:190:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:200:9:200:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:200:10:200:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:200:16:200:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:200:22:200:22 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | -| test.py:200:22:200:40 | ControlFlowNode for GeneratorExp [List element] | semmle.label | ControlFlowNode for GeneratorExp [List element] | -| test.py:200:28:200:28 | SSA variable z | semmle.label | SSA variable z | -| test.py:200:33:200:40 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:200:34:200:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:201:10:201:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:201:10:201:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:344:10:344:21 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:344:11:344:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:344:11:344:17 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:348:10:348:17 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:348:10:348:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:348:11:348:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:352:10:352:22 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:352:10:352:27 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:352:16:352:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:375:10:375:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:375:28:375:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:457:10:457:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | -| test.py:457:12:457:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:462:10:462:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:462:28:462:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:504:9:504:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:506:10:506:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:511:10:511:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | +| test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | +| test.py:183:10:183:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:183:16:183:16 | SSA variable z [List element] | semmle.label | SSA variable z [List element] | +| test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | +| test.py:183:22:183:29 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | +| test.py:183:23:183:28 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:183:36:183:36 | SSA variable y | semmle.label | SSA variable y | +| test.py:183:41:183:41 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] | +| test.py:184:10:184:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | +| test.py:184:10:184:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | +| test.py:188:10:188:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:188:16:188:16 | SSA variable v [List element, List element, ... (3)] | semmle.label | SSA variable v [List element, List element, ... (3)] | +| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, ... (4)] | semmle.label | ControlFlowNode for List [List element, List element, ... (4)] | +| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, ... (3)] | semmle.label | ControlFlowNode for List [List element, List element, ... (3)] | +| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | +| test.py:188:24:188:31 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | +| test.py:188:25:188:30 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:188:40:188:40 | SSA variable u [List element, List element] | semmle.label | SSA variable u [List element, List element] | +| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, ... (3)] | semmle.label | ControlFlowNode for v [List element, List element, ... (3)] | +| test.py:188:51:188:51 | SSA variable z [List element] | semmle.label | SSA variable z [List element] | +| test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | semmle.label | ControlFlowNode for u [List element, List element] | +| test.py:188:62:188:62 | SSA variable y | semmle.label | SSA variable y | +| test.py:188:67:188:67 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] | +| test.py:189:10:189:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | +| test.py:189:10:189:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | +| test.py:199:10:199:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | +| test.py:199:16:199:16 | SSA variable y | semmle.label | SSA variable y | +| test.py:199:22:199:22 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | +| test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | semmle.label | ControlFlowNode for GeneratorExp [List element] | +| test.py:199:28:199:28 | SSA variable z | semmle.label | SSA variable z | +| test.py:199:33:199:40 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | +| test.py:199:34:199:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:200:10:200:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | +| test.py:200:10:200:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:336:10:336:21 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:336:11:336:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:336:11:336:17 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | +| test.py:340:10:340:17 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | +| test.py:340:10:340:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:340:11:340:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:344:10:344:22 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] | +| test.py:344:10:344:27 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:344:16:344:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:365:10:365:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:365:28:365:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:442:10:442:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | +| test.py:442:12:442:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:449:10:449:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:449:28:449:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:499:9:499:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:501:10:501:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | +| test.py:506:10:506:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | #select | datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:38:6:38:17 | ControlFlowNode for f() | Flow found | | datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | Flow found | diff --git a/python/ql/test/experimental/dataflow/coverage/localFlow.expected b/python/ql/test/experimental/dataflow/coverage/localFlow.expected index 9610f505123..b9c7c01a63c 100644 --- a/python/ql/test/experimental/dataflow/coverage/localFlow.expected +++ b/python/ql/test/experimental/dataflow/coverage/localFlow.expected @@ -1,36 +1,36 @@ -| test.py:35:1:35:33 | GSSA Variable NONSOURCE | test.py:36:10:36:18 | ControlFlowNode for NONSOURCE | -| test.py:35:1:35:33 | GSSA Variable SINK | test.py:38:5:38:8 | ControlFlowNode for SINK | -| test.py:35:1:35:33 | GSSA Variable SOURCE | test.py:36:21:36:26 | ControlFlowNode for SOURCE | -| test.py:36:5:36:5 | SSA variable x | test.py:37:9:37:9 | ControlFlowNode for x | -| test.py:36:10:36:26 | ControlFlowNode for Tuple | test.py:36:5:36:5 | SSA variable x | -| test.py:37:5:37:5 | SSA variable y | test.py:38:5:38:11 | SSA variable y | -| test.py:37:5:37:5 | SSA variable y | test.py:38:10:38:10 | ControlFlowNode for y | -| test.py:37:9:37:12 | ControlFlowNode for Subscript | test.py:37:5:37:5 | SSA variable y | -| test.py:188:1:188:53 | GSSA Variable SINK | test.py:190:5:190:8 | ControlFlowNode for SINK | -| test.py:188:1:188:53 | GSSA Variable SOURCE | test.py:189:25:189:30 | ControlFlowNode for SOURCE | -| test.py:189:5:189:5 | SSA variable x | test.py:190:10:190:10 | ControlFlowNode for x | -| test.py:189:9:189:68 | ControlFlowNode for ListComp | test.py:189:5:189:5 | SSA variable x | -| test.py:189:9:189:68 | SSA variable u | test.py:189:9:189:68 | SSA variable u | -| test.py:189:9:189:68 | SSA variable u | test.py:189:9:189:68 | SSA variable u | -| test.py:189:9:189:68 | SSA variable u | test.py:189:9:189:68 | SSA variable u | -| test.py:189:9:189:68 | SSA variable v | test.py:189:9:189:68 | SSA variable v | -| test.py:189:9:189:68 | SSA variable y | test.py:189:9:189:68 | SSA variable y | -| test.py:189:9:189:68 | SSA variable y | test.py:189:9:189:68 | SSA variable y | -| test.py:189:9:189:68 | SSA variable y | test.py:189:9:189:68 | SSA variable y | -| test.py:189:9:189:68 | SSA variable y | test.py:189:9:189:68 | SSA variable y | -| test.py:189:9:189:68 | SSA variable y | test.py:189:9:189:68 | SSA variable y | -| test.py:189:9:189:68 | SSA variable y | test.py:189:9:189:68 | SSA variable y | -| test.py:189:9:189:68 | SSA variable y | test.py:189:9:189:68 | SSA variable y | -| test.py:189:9:189:68 | SSA variable z | test.py:189:9:189:68 | SSA variable z | -| test.py:189:9:189:68 | SSA variable z | test.py:189:9:189:68 | SSA variable z | -| test.py:189:9:189:68 | SSA variable z | test.py:189:9:189:68 | SSA variable z | -| test.py:189:9:189:68 | SSA variable z | test.py:189:9:189:68 | SSA variable z | -| test.py:189:9:189:68 | SSA variable z | test.py:189:9:189:68 | SSA variable z | -| test.py:189:16:189:16 | SSA variable v | test.py:189:9:189:68 | SSA variable v | -| test.py:189:16:189:16 | SSA variable v | test.py:189:45:189:45 | ControlFlowNode for v | -| test.py:189:40:189:40 | SSA variable u | test.py:189:9:189:68 | SSA variable u | -| test.py:189:40:189:40 | SSA variable u | test.py:189:56:189:56 | ControlFlowNode for u | -| test.py:189:51:189:51 | SSA variable z | test.py:189:9:189:68 | SSA variable z | -| test.py:189:51:189:51 | SSA variable z | test.py:189:67:189:67 | ControlFlowNode for z | -| test.py:189:62:189:62 | SSA variable y | test.py:189:9:189:68 | SSA variable y | -| test.py:189:62:189:62 | SSA variable y | test.py:189:10:189:10 | ControlFlowNode for y | +| test.py:41:1:41:33 | GSSA Variable NONSOURCE | test.py:42:10:42:18 | ControlFlowNode for NONSOURCE | +| test.py:41:1:41:33 | GSSA Variable SINK | test.py:44:5:44:8 | ControlFlowNode for SINK | +| test.py:41:1:41:33 | GSSA Variable SOURCE | test.py:42:21:42:26 | ControlFlowNode for SOURCE | +| test.py:42:5:42:5 | SSA variable x | test.py:43:9:43:9 | ControlFlowNode for x | +| test.py:42:10:42:26 | ControlFlowNode for Tuple | test.py:42:5:42:5 | SSA variable x | +| test.py:43:5:43:5 | SSA variable y | test.py:44:5:44:11 | SSA variable y | +| test.py:43:5:43:5 | SSA variable y | test.py:44:10:44:10 | ControlFlowNode for y | +| test.py:43:9:43:12 | ControlFlowNode for Subscript | test.py:43:5:43:5 | SSA variable y | +| test.py:187:1:187:53 | GSSA Variable SINK | test.py:189:5:189:8 | ControlFlowNode for SINK | +| test.py:187:1:187:53 | GSSA Variable SOURCE | test.py:188:25:188:30 | ControlFlowNode for SOURCE | +| test.py:188:5:188:5 | SSA variable x | test.py:189:10:189:10 | ControlFlowNode for x | +| test.py:188:9:188:68 | ControlFlowNode for ListComp | test.py:188:5:188:5 | SSA variable x | +| test.py:188:9:188:68 | SSA variable u | test.py:188:9:188:68 | SSA variable u | +| test.py:188:9:188:68 | SSA variable u | test.py:188:9:188:68 | SSA variable u | +| test.py:188:9:188:68 | SSA variable u | test.py:188:9:188:68 | SSA variable u | +| test.py:188:9:188:68 | SSA variable v | test.py:188:9:188:68 | SSA variable v | +| test.py:188:9:188:68 | SSA variable y | test.py:188:9:188:68 | SSA variable y | +| test.py:188:9:188:68 | SSA variable y | test.py:188:9:188:68 | SSA variable y | +| test.py:188:9:188:68 | SSA variable y | test.py:188:9:188:68 | SSA variable y | +| test.py:188:9:188:68 | SSA variable y | test.py:188:9:188:68 | SSA variable y | +| test.py:188:9:188:68 | SSA variable y | test.py:188:9:188:68 | SSA variable y | +| test.py:188:9:188:68 | SSA variable y | test.py:188:9:188:68 | SSA variable y | +| test.py:188:9:188:68 | SSA variable y | test.py:188:9:188:68 | SSA variable y | +| test.py:188:9:188:68 | SSA variable z | test.py:188:9:188:68 | SSA variable z | +| test.py:188:9:188:68 | SSA variable z | test.py:188:9:188:68 | SSA variable z | +| test.py:188:9:188:68 | SSA variable z | test.py:188:9:188:68 | SSA variable z | +| test.py:188:9:188:68 | SSA variable z | test.py:188:9:188:68 | SSA variable z | +| test.py:188:9:188:68 | SSA variable z | test.py:188:9:188:68 | SSA variable z | +| test.py:188:16:188:16 | SSA variable v | test.py:188:9:188:68 | SSA variable v | +| test.py:188:16:188:16 | SSA variable v | test.py:188:45:188:45 | ControlFlowNode for v | +| test.py:188:40:188:40 | SSA variable u | test.py:188:9:188:68 | SSA variable u | +| test.py:188:40:188:40 | SSA variable u | test.py:188:56:188:56 | ControlFlowNode for u | +| test.py:188:51:188:51 | SSA variable z | test.py:188:9:188:68 | SSA variable z | +| test.py:188:51:188:51 | SSA variable z | test.py:188:67:188:67 | ControlFlowNode for z | +| test.py:188:62:188:62 | SSA variable y | test.py:188:9:188:68 | SSA variable y | +| test.py:188:62:188:62 | SSA variable y | test.py:188:10:188:10 | ControlFlowNode for y | diff --git a/python/ql/test/experimental/dataflow/coverage/test.py b/python/ql/test/experimental/dataflow/coverage/test.py index 32e303e3652..e8569e11756 100644 --- a/python/ql/test/experimental/dataflow/coverage/test.py +++ b/python/ql/test/experimental/dataflow/coverage/test.py @@ -6,9 +6,15 @@ # # Functions whose name ends with "_with_local_flow" will also be tested for local flow. # -# All functions starting with "test_" should run and execute `print("OK")` one or more times. +# All functions starting with "test_" should run and execute `print("OK")` exactly once. # This can be checked by running validTest.py. +import sys +import os + +sys.path.append(os.path.dirname(os.path.dirname((__file__)))) +from testlib import * + # These are defined so that we can evaluate the test code. NONSOURCE = "not a source" SOURCE = "source" @@ -43,16 +49,14 @@ def test_tuple_negative(): y = x[0] SINK_F(y) + # 6.2.1. Identifiers (Names) - - def test_names(): x = SOURCE SINK(x) + # 6.2.2. Literals - - def test_string_literal(): x = "source" SINK(x) @@ -77,16 +81,14 @@ def test_imagnumber_literal(): x = 42j SINK(x) # Flow missing + # 6.2.3. Parenthesized forms - - def test_parenthesized_form(): - x = (SOURCE) + x = SOURCE SINK(x) + # 6.2.5. List displays - - def test_list_display(): x = [SOURCE] SINK(x[0]) @@ -114,12 +116,11 @@ def test_list_comprehension_inflow(): def test_nested_list_display(): - x = [* [SOURCE]] + x = [*[SOURCE]] SINK(x[0]) # Flow missing + # 6.2.6. Set displays - - def test_set_display(): x = {SOURCE} SINK(x.pop()) @@ -142,12 +143,11 @@ def test_set_comprehension_inflow(): def test_nested_set_display(): - x = {* {SOURCE}} + x = {*{SOURCE}} SINK(x.pop()) # Flow missing + # 6.2.7. Dictionary displays - - def test_dict_display(): x = {"s": SOURCE} SINK(x["s"]) @@ -169,17 +169,16 @@ def test_dict_comprehension_pop(): def test_nested_dict_display(): - x = {** {"s": SOURCE}} + x = {**{"s": SOURCE}} SINK(x["s"]) # Flow missing def test_nested_dict_display_pop(): - x = {** {"s": SOURCE}} + x = {**{"s": SOURCE}} SINK(x.pop("s")) # Flow missing + # Nested comprehensions - - def test_nested_comprehension(): x = [y for z in [[SOURCE]] for y in z] SINK(x[0]) @@ -200,16 +199,14 @@ def test_nested_comprehension_paren(): x = [y for y in (z for z in [SOURCE])] SINK(x[0]) + # 6.2.8. Generator expressions - - def test_generator(): x = (SOURCE for y in [NONSOURCE]) SINK([*x][0]) # Flow missing + # 6.2.9. Yield expressions - - def gen(x): yield x @@ -227,16 +224,14 @@ def test_yield_from(): g = gen_from(SOURCE) SINK(next(g)) # Flow missing + # a statement rather than an expression, but related to generators - - def test_for(): for x in gen(SOURCE): SINK(x) # Flow missing + # 6.2.9.1. Generator-iterator methods - - def test___next__(): g = gen(SOURCE) SINK(g.__next__()) # Flow missing @@ -266,21 +261,20 @@ def test_throw(): n = next(g) SINK(g.throw(TypeError)) # Flow missing + # no `test_close` as `close` involves no data flow # 6.2.9.3. Asynchronous generator functions - - async def agen(x): yield x + # 6.2.9.4. Asynchronous generator-iterator methods # helper to run async test functions - - def runa(a): import asyncio + asyncio.run(a) @@ -325,9 +319,8 @@ async def atest_athrow(): def test_athrow(): runa(atest_athrow()) + # 6.3.1. Attribute references - - class C: a = SOURCE @@ -335,11 +328,10 @@ class C: def test_attribute_reference(): SINK(C.a) # Flow missing + # overriding __getattr__ should be tested by the class coverage tests # 6.3.2. Subscriptions - - def test_subscription_tuple(): SINK((SOURCE,)[0]) @@ -351,9 +343,8 @@ def test_subscription_list(): def test_subscription_mapping(): SINK({"s": SOURCE}["s"]) + # overriding __getitem__ should be tested by the class coverage tests - - # 6.3.3. Slicings l = [SOURCE] @@ -362,11 +353,10 @@ def test_slicing(): s = l[0:1:1] SINK(s[0]) # Flow missing + # The grammar seems to allow `l[0:1:1, 0:1]`, but the interpreter does not like it # 6.3.4. Calls - - def second(a, b): return b @@ -406,28 +396,24 @@ def f_extra_keyword(a, **b): def test_call_extra_keyword(): SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) # Flow missing + # return the name of the first extra keyword argument - - def f_extra_keyword_flow(**a): return [*a][0] + # call the function with our source as the name of the keyword arguemnt - - def test_call_extra_keyword_flow(): SINK(f_extra_keyword_flow(**{SOURCE: None})) # Flow missing + # 6.12. Assignment expressions - - def test_assignment_expression(): x = NONSOURCE SINK(x := SOURCE) # Flow missing + # 6.13. Conditional expressions - - def test_conditional_true(): SINK(SOURCE if True else NONSOURCE) # Flow missing @@ -435,50 +421,59 @@ def test_conditional_true(): def test_conditional_false(): SINK(NONSOURCE if False else SOURCE) # Flow missing + # Condition is evaluated first, so x is SOURCE once chosen - - def test_conditional_evaluation_true(): x = NONSOURCE SINK(x if (SOURCE == (x := SOURCE)) else NONSOURCE) # Flow missing + # Condition is evaluated first, so x is SOURCE once chosen - - def test_conditional_evaluation_false(): x = NONSOURCE SINK(NONSOURCE if (NONSOURCE == (x := SOURCE)) else x) # Flow missing + # 6.14. Lambdas - - def test_lambda(): - def f(x): return x + def f(x): + return x + SINK(f(SOURCE)) def test_lambda_positional(): - def second(a, b): return b + def second(a, b): + return b + SINK(second(NONSOURCE, SOURCE)) def test_lambda_positional_negative(): - def second(a, b): return b + def second(a, b): + return b + SINK_F(second(SOURCE, NONSOURCE)) def test_lambda_keyword(): - def second(a, b): return b + def second(a, b): + return b + SINK(second(NONSOURCE, b=SOURCE)) # Flow missing def test_lambda_unpack_iterable(): - def second(a, b): return b + def second(a, b): + return b + SINK(second(NONSOURCE, *[SOURCE])) # Flow missing def test_lambda_unpack_mapping(): - def second(a, b): return b + def second(a, b): + return b + SINK(second(NONSOURCE, **{"b": SOURCE})) # Flow missing @@ -491,15 +486,15 @@ def test_lambda_extra_keyword(): f_extra_keyword = lambda a, **b: b["b"] SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) # Flow missing + # call the function with our source as the name of the keyword arguemnt - - def test_lambda_extra_keyword_flow(): # return the name of the first extra keyword argument f_extra_keyword_flow = lambda **a: [*a][0] SINK(f_extra_keyword_flow(**{SOURCE: None})) # Flow missing +@expects(4) def test_swap(): a = SOURCE b = NONSOURCE @@ -536,6 +531,7 @@ def test_deep_callgraph(): SINK(x) # Flow missing +@expects(2) def test_dynamic_tuple_creation_1(): tup = tuple() tup += (SOURCE,) @@ -545,6 +541,7 @@ def test_dynamic_tuple_creation_1(): SINK_F(tup[1]) +@expects(2) def test_dynamic_tuple_creation_2(): tup = () tup += (SOURCE,) @@ -554,6 +551,7 @@ def test_dynamic_tuple_creation_2(): SINK_F(tup[1]) +@expects(2) def test_dynamic_tuple_creation_3(): tup1 = (SOURCE,) tup2 = (NONSOURCE,) @@ -564,6 +562,7 @@ def test_dynamic_tuple_creation_3(): # Inspired by FP-report https://github.com/github/codeql/issues/4239 +@expects(2) def test_dynamic_tuple_creation_4(): tup = () for item in [SOURCE, NONSOURCE]: diff --git a/python/ql/test/experimental/dataflow/coverage/testlib.py b/python/ql/test/experimental/dataflow/coverage/testlib.py new file mode 100644 index 00000000000..a85c5f8c184 --- /dev/null +++ b/python/ql/test/experimental/dataflow/coverage/testlib.py @@ -0,0 +1,27 @@ +def expects(n): + def check_output(output): + lines = output.splitlines() + if all(s == "OK" for s in lines): + if len(lines) == n: + print("OK") + else: + print("Expected", n, "outputs but got", len(lines)) + else: + print(list(s for s in lines if s != "OK")) + + def wrap(f): + def wrapped(*args, **kwargs): + from io import StringIO + import sys + + capturer = StringIO() + old_stdout = sys.stdout + sys.stdout = capturer + f(*args, **kwargs) + sys.stdout = old_stdout + check_output(capturer.getvalue()) + + wrapped.__name__ = "[" + str(n) + "]" + f.__name__ + return wrapped + + return wrap diff --git a/python/ql/test/experimental/dataflow/coverage/validTest.py b/python/ql/test/experimental/dataflow/coverage/validTest.py index cd3ef2af1b7..b4ef99bc1ca 100644 --- a/python/ql/test/experimental/dataflow/coverage/validTest.py +++ b/python/ql/test/experimental/dataflow/coverage/validTest.py @@ -1,8 +1,9 @@ def check_output(outtext, f): - if outtext and all(s == "OK" for s in outtext.splitlines()): + if outtext == "OK\n": pass else: - raise RuntimeError("Function failed", outtext, f) + raise RuntimeError("Function failed", outtext, f.__name__) + def check_test_function(f): from io import StringIO @@ -15,6 +16,7 @@ def check_test_function(f): sys.stdout = old_stdout check_output(capturer.getvalue(), f) + def check_async_test_function(f): from io import StringIO import sys @@ -27,23 +29,27 @@ def check_async_test_function(f): sys.stdout = old_stdout check_output(capturer.getvalue(), f) + def check_tests_valid(testFile): import importlib + tests = importlib.import_module(testFile) for i in dir(tests): # print("Considering", i) if i.startswith("test_"): - item = getattr(tests,i) + item = getattr(tests, i) if callable(item): - print("Checking", testFile, item) + print("Checking", testFile, item.__name__) check_test_function(item) elif i.startswith("atest_"): - item = getattr(tests,i) + item = getattr(tests, i) if callable(item): - print("Checking", testFile, item) + print("Checking", testFile, item.__name__) check_async_test_function(item) -if __name__ == '__main__': + +if __name__ == "__main__": check_tests_valid("classes") check_tests_valid("test") + check_tests_valid("argumentPassing") From 8f2ef94b3e8f658c8aef3e2b8963277738257104 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 29 Sep 2020 09:40:48 +0200 Subject: [PATCH 008/166] Python: Hook up keyword arguments --- .../dataflow/internal/DataFlowPrivate.qll | 41 ++++-- .../dataflow/coverage/argumentPassing.py | 29 +++- .../coverage/argumentRouting1.expected | 22 +-- .../coverage/argumentRouting2.expected | 6 +- .../coverage/argumentRouting3.expected | 2 +- .../coverage/classesCallGraph.expected | 79 +++++------ .../dataflow/coverage/dataflow.expected | 134 ++++++++++-------- .../experimental/dataflow/coverage/test.py | 4 +- 8 files changed, 191 insertions(+), 126 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index fcc98e9d8ce..64c5c049ae1 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -261,6 +261,9 @@ abstract class DataFlowCallable extends TDataFlowCallable { /** Gets the name of this callable. */ abstract string getName(); + + /** Gets a callable value for this callable, if one exists. */ + abstract CallableValue getCallableValue(); } /** A class representing a callable value. */ @@ -278,6 +281,8 @@ class DataFlowCallableValue extends DataFlowCallable, TCallableValue { override NameNode getParameter(int n) { result = callable.getParameter(n) } override string getName() { result = callable.getName() } + + override CallableValue getCallableValue() { result = callable } } /** A class representing the scope in which a `ModuleVariableNode` appears. */ @@ -295,6 +300,8 @@ class DataFlowModuleScope extends DataFlowCallable, TModule { override NameNode getParameter(int n) { none() } override string getName() { result = mod.getName() } + + override CallableValue getCallableValue() { none() } } /** @@ -322,7 +329,10 @@ abstract class DataFlowCall extends TDataFlowCall { /** Get the callable to which this call goes. */ abstract DataFlowCallable getCallable(); - /** Get the specified argument to this call. */ + /** + * Gets the argument to this call that will be sent + * to the `n`th parameter of the callable. + */ abstract Node getArg(int n); /** Get the control flow node representing this call. */ @@ -330,6 +340,22 @@ abstract class DataFlowCall extends TDataFlowCall { /** Gets the enclosing callable of this call. */ abstract DataFlowCallable getEnclosingCallable(); + + /** Gets the location of this dataflow call. */ + Location getLocation() { result = this.getNode().getLocation() } +} + +ControlFlowNode getArg(CallNode call, CallableValue callable, int n) { + call = callable.getACall() and + ( + result = call.getArg(n) + or + exists(Function f, string argName | + f = callable.getScope() and + f.getArgName(n) = argName and + result = call.getArgByName(argName) + ) + ) } /** Represents a call to a callable (currently only callable values). */ @@ -344,7 +370,7 @@ class CallNodeCall extends DataFlowCall, TCallNode { override string toString() { result = call.toString() } - override Node getArg(int n) { result = TCfgNode(call.getArg(n)) } + override Node getArg(int n) { result = TCfgNode(getArg(call, callable.getCallableValue(), n)) } override ControlFlowNode getNode() { result = call } @@ -363,22 +389,19 @@ class ClassCall extends DataFlowCall, TClassCall { call = c.getACall() } + private CallableValue getCallableValue() { c.getScope().getInitMethod() = result.getScope() } + override string toString() { result = call.toString() } override Node getArg(int n) { - n > 0 and result = TCfgNode(call.getArg(n - 1)) + n > 0 and result = TCfgNode(getArg(call, this.getCallableValue(), n - 1)) or n = 0 and result = TSyntheticPreUpdateNode(TCfgNode(call)) } override ControlFlowNode getNode() { result = call } - override DataFlowCallable getCallable() { - exists(CallableValue callable | - result = TCallableValue(callable) and - c.getScope().getInitMethod() = callable.getScope() - ) - } + override DataFlowCallable getCallable() { result = TCallableValue(this.getCallableValue()) } override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() } } diff --git a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py index 8bd4f4344ef..34ae519efc9 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py +++ b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py @@ -8,6 +8,10 @@ arg = "source" arg1 = "source1" arg2 = "source2" arg3 = "source3" +arg4 = "source4" +arg5 = "source5" +arg6 = "source6" +arg7 = "source7" def SINK(x, expected=arg): @@ -29,6 +33,22 @@ def SINK3(x): SINK(x, expected=arg3) +def SINK4(x): + SINK(x, expected=arg4) + + +def SINK5(x): + SINK(x, expected=arg5) + + +def SINK6(x): + SINK(x, expected=arg6) + + +def SINK7(x): + SINK(x, expected=arg7) + + def argument_passing( a, b, @@ -43,12 +63,15 @@ def argument_passing( SINK1(a) SINK2(b) SINK3(c) - SINK(f) + SINK4(d) + SINK5(e) + SINK6(f) + SINK7(g["g"]) -@expects(4) +@expects(7) def test_argument_passing(): - argument_passing(arg1, arg2, arg3, f=arg) + argument_passing(arg1, *(arg2, arg3, arg4), e=arg5, **{"f": arg6, "g": arg7}) def with_pos_only(a, /, b): diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected index eba722029f7..bb05dbe40f8 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected @@ -1,11 +1,17 @@ -| argumentPassing.py:51:22:51:25 | ControlFlowNode for arg1 | argumentPassing.py:43:11:43:11 | ControlFlowNode for a | -| argumentPassing.py:61:19:61:22 | ControlFlowNode for arg1 | argumentPassing.py:55:11:55:11 | ControlFlowNode for a | -| argumentPassing.py:62:19:62:22 | ControlFlowNode for arg1 | argumentPassing.py:55:11:55:11 | ControlFlowNode for a | -| argumentPassing.py:63:19:63:22 | ControlFlowNode for arg1 | argumentPassing.py:55:11:55:11 | ControlFlowNode for a | -| argumentPassing.py:74:45:74:48 | ControlFlowNode for arg1 | argumentPassing.py:67:11:67:11 | ControlFlowNode for a | -| argumentPassing.py:75:27:75:30 | ControlFlowNode for arg1 | argumentPassing.py:67:11:67:11 | ControlFlowNode for a | -| argumentPassing.py:76:27:76:30 | ControlFlowNode for arg1 | argumentPassing.py:67:11:67:11 | ControlFlowNode for a | -| argumentPassing.py:88:28:88:31 | ControlFlowNode for arg1 | argumentPassing.py:80:11:80:11 | ControlFlowNode for a | +| argumentPassing.py:74:22:74:25 | ControlFlowNode for arg1 | argumentPassing.py:63:11:63:11 | ControlFlowNode for a | +| argumentPassing.py:84:19:84:22 | ControlFlowNode for arg1 | argumentPassing.py:78:11:78:11 | ControlFlowNode for a | +| argumentPassing.py:85:19:85:22 | ControlFlowNode for arg1 | argumentPassing.py:78:11:78:11 | ControlFlowNode for a | +| argumentPassing.py:86:19:86:22 | ControlFlowNode for arg1 | argumentPassing.py:78:11:78:11 | ControlFlowNode for a | +| argumentPassing.py:97:45:97:48 | ControlFlowNode for arg1 | argumentPassing.py:90:11:90:11 | ControlFlowNode for a | +| argumentPassing.py:98:27:98:30 | ControlFlowNode for arg1 | argumentPassing.py:90:11:90:11 | ControlFlowNode for a | +| argumentPassing.py:99:27:99:30 | ControlFlowNode for arg1 | argumentPassing.py:90:11:90:11 | ControlFlowNode for a | +| argumentPassing.py:111:28:111:31 | ControlFlowNode for arg1 | argumentPassing.py:103:11:103:11 | ControlFlowNode for a | +| argumentPassing.py:133:46:133:49 | ControlFlowNode for arg1 | argumentPassing.py:118:11:118:13 | ControlFlowNode for foo | +| argumentPassing.py:141:14:141:17 | ControlFlowNode for arg1 | argumentPassing.py:139:15:139:15 | ControlFlowNode for a | +| argumentPassing.py:148:19:148:22 | ControlFlowNode for arg1 | argumentPassing.py:146:15:146:15 | ControlFlowNode for a | +| argumentPassing.py:163:13:163:16 | ControlFlowNode for arg1 | argumentPassing.py:161:15:161:15 | ControlFlowNode for a | +| argumentPassing.py:170:16:170:19 | ControlFlowNode for arg1 | argumentPassing.py:168:15:168:15 | ControlFlowNode for a | +| argumentPassing.py:177:15:177:18 | ControlFlowNode for arg1 | argumentPassing.py:175:15:175:15 | ControlFlowNode for a | | classes.py:563:5:563:16 | SSA variable with_getitem | classes.py:557:15:557:18 | ControlFlowNode for self | | classes.py:578:5:578:16 | SSA variable with_setitem | classes.py:573:15:573:18 | ControlFlowNode for self | | classes.py:593:5:593:16 | SSA variable with_delitem | classes.py:588:15:588:18 | ControlFlowNode for self | diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected index 62ea005e68f..7bdd1f4ea16 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected @@ -1,5 +1,7 @@ -| argumentPassing.py:51:28:51:31 | ControlFlowNode for arg2 | argumentPassing.py:44:11:44:11 | ControlFlowNode for b | -| argumentPassing.py:61:25:61:28 | ControlFlowNode for arg2 | argumentPassing.py:56:11:56:11 | ControlFlowNode for b | +| argumentPassing.py:84:25:84:28 | ControlFlowNode for arg2 | argumentPassing.py:79:11:79:11 | ControlFlowNode for b | +| argumentPassing.py:85:27:85:30 | ControlFlowNode for arg2 | argumentPassing.py:79:11:79:11 | ControlFlowNode for b | +| argumentPassing.py:97:29:97:32 | ControlFlowNode for arg2 | argumentPassing.py:91:11:91:11 | ControlFlowNode for b | +| argumentPassing.py:112:30:112:33 | ControlFlowNode for arg2 | argumentPassing.py:104:11:104:11 | ControlFlowNode for b | | classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:556:15:556:17 | ControlFlowNode for key | | classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:572:15:572:17 | ControlFlowNode for key | | classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:587:15:587:17 | ControlFlowNode for key | diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected index 6ce32c993f9..f0c70c48ec9 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected @@ -1,2 +1,2 @@ -| argumentPassing.py:51:34:51:37 | ControlFlowNode for arg3 | argumentPassing.py:45:11:45:11 | ControlFlowNode for c | +| argumentPassing.py:97:37:97:40 | ControlFlowNode for arg3 | argumentPassing.py:92:11:92:11 | ControlFlowNode for c | | classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:571:15:571:19 | ControlFlowNode for value | diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected index cf688c17a49..7cf4ac4b6b3 100644 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected +++ b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected @@ -1,40 +1,39 @@ -| classes.py:41:16:41:35 | ControlFlowNode for Attribute() | classes.py:41:16:41:35 | ControlFlowNode for Attribute() | -| classes.py:58:17:58:27 | [pre objCreate] ControlFlowNode for With_init() | classes.py:52:18:52:21 | SSA variable self | -| classes.py:264:9:264:24 | ControlFlowNode for set() | classes.py:264:9:264:24 | ControlFlowNode for set() | -| classes.py:269:9:269:30 | ControlFlowNode for frozenset() | classes.py:269:9:269:30 | ControlFlowNode for frozenset() | -| classes.py:274:9:274:28 | ControlFlowNode for dict() | classes.py:274:9:274:28 | ControlFlowNode for dict() | -| classes.py:454:29:454:52 | ControlFlowNode for dict() | classes.py:454:29:454:52 | ControlFlowNode for dict() | -| classes.py:622:5:622:16 | ControlFlowNode for with_getitem | classes.py:612:21:612:24 | SSA variable self | -| classes.py:622:18:622:21 | ControlFlowNode for arg2 | classes.py:612:27:612:29 | SSA variable key | -| classes.py:640:5:640:16 | ControlFlowNode for with_setitem | classes.py:629:21:629:24 | SSA variable self | -| classes.py:640:18:640:21 | ControlFlowNode for arg2 | classes.py:629:27:629:29 | SSA variable key | -| classes.py:640:26:640:29 | ControlFlowNode for arg3 | classes.py:629:32:629:36 | SSA variable value | -| classes.py:656:9:656:20 | ControlFlowNode for with_delitem | classes.py:647:21:647:24 | SSA variable self | -| classes.py:656:22:656:25 | ControlFlowNode for arg2 | classes.py:647:27:647:29 | SSA variable key | -| classes.py:683:16:683:28 | ControlFlowNode for Attribute() | classes.py:683:16:683:28 | ControlFlowNode for Attribute() | -| classes.py:737:5:737:12 | ControlFlowNode for with_add | classes.py:727:17:727:20 | SSA variable self | -| classes.py:737:16:737:19 | ControlFlowNode for arg2 | classes.py:727:23:727:27 | SSA variable other | -| classes.py:754:5:754:12 | ControlFlowNode for with_sub | classes.py:744:17:744:20 | SSA variable self | -| classes.py:754:16:754:19 | ControlFlowNode for arg2 | classes.py:744:23:744:27 | SSA variable other | -| classes.py:771:5:771:12 | ControlFlowNode for with_mul | classes.py:761:17:761:20 | SSA variable self | -| classes.py:771:16:771:19 | ControlFlowNode for arg2 | classes.py:761:23:761:27 | SSA variable other | -| classes.py:788:5:788:15 | ControlFlowNode for with_matmul | classes.py:778:20:778:23 | SSA variable self | -| classes.py:788:19:788:22 | ControlFlowNode for arg2 | classes.py:778:26:778:30 | SSA variable other | -| classes.py:805:5:805:16 | ControlFlowNode for with_truediv | classes.py:795:21:795:24 | SSA variable self | -| classes.py:805:20:805:23 | ControlFlowNode for arg2 | classes.py:795:27:795:31 | SSA variable other | -| classes.py:822:5:822:17 | ControlFlowNode for with_floordiv | classes.py:812:22:812:25 | SSA variable self | -| classes.py:822:22:822:25 | ControlFlowNode for arg2 | classes.py:812:28:812:32 | SSA variable other | -| classes.py:839:5:839:12 | ControlFlowNode for with_mod | classes.py:829:17:829:20 | SSA variable self | -| classes.py:839:16:839:19 | ControlFlowNode for arg2 | classes.py:829:23:829:27 | SSA variable other | -| classes.py:879:5:879:12 | ControlFlowNode for with_pow | classes.py:863:17:863:20 | SSA variable self | -| classes.py:879:17:879:20 | ControlFlowNode for arg2 | classes.py:863:23:863:27 | SSA variable other | -| classes.py:896:5:896:15 | ControlFlowNode for with_lshift | classes.py:886:20:886:23 | SSA variable self | -| classes.py:896:20:896:23 | ControlFlowNode for arg2 | classes.py:886:26:886:30 | SSA variable other | -| classes.py:913:5:913:15 | ControlFlowNode for with_rshift | classes.py:903:20:903:23 | SSA variable self | -| classes.py:913:20:913:23 | ControlFlowNode for arg2 | classes.py:903:26:903:30 | SSA variable other | -| classes.py:930:5:930:12 | ControlFlowNode for with_and | classes.py:920:17:920:20 | SSA variable self | -| classes.py:930:16:930:19 | ControlFlowNode for arg2 | classes.py:920:23:920:27 | SSA variable other | -| classes.py:947:5:947:12 | ControlFlowNode for with_xor | classes.py:937:17:937:20 | SSA variable self | -| classes.py:947:16:947:19 | ControlFlowNode for arg2 | classes.py:937:23:937:27 | SSA variable other | -| classes.py:964:5:964:11 | ControlFlowNode for with_or | classes.py:954:16:954:19 | SSA variable self | -| classes.py:964:15:964:18 | ControlFlowNode for arg2 | classes.py:954:22:954:26 | SSA variable other | +| classes.py:45:16:45:35 | ControlFlowNode for Attribute() | classes.py:45:16:45:35 | ControlFlowNode for Attribute() | +| classes.py:60:17:60:27 | [pre objCreate] ControlFlowNode for With_init() | classes.py:54:18:54:21 | SSA variable self | +| classes.py:242:9:242:24 | ControlFlowNode for set() | classes.py:242:9:242:24 | ControlFlowNode for set() | +| classes.py:247:9:247:30 | ControlFlowNode for frozenset() | classes.py:247:9:247:30 | ControlFlowNode for frozenset() | +| classes.py:252:9:252:28 | ControlFlowNode for dict() | classes.py:252:9:252:28 | ControlFlowNode for dict() | +| classes.py:565:5:565:16 | ControlFlowNode for with_getitem | classes.py:555:21:555:24 | SSA variable self | +| classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:555:27:555:29 | SSA variable key | +| classes.py:581:5:581:16 | ControlFlowNode for with_setitem | classes.py:570:21:570:24 | SSA variable self | +| classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:570:27:570:29 | SSA variable key | +| classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:570:32:570:36 | SSA variable value | +| classes.py:595:9:595:20 | ControlFlowNode for with_delitem | classes.py:586:21:586:24 | SSA variable self | +| classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:586:27:586:29 | SSA variable key | +| classes.py:618:16:618:28 | ControlFlowNode for Attribute() | classes.py:618:16:618:28 | ControlFlowNode for Attribute() | +| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:657:17:657:20 | SSA variable self | +| classes.py:667:16:667:19 | ControlFlowNode for arg2 | classes.py:657:23:657:27 | SSA variable other | +| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:672:17:672:20 | SSA variable self | +| classes.py:682:16:682:19 | ControlFlowNode for arg2 | classes.py:672:23:672:27 | SSA variable other | +| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:687:17:687:20 | SSA variable self | +| classes.py:697:16:697:19 | ControlFlowNode for arg2 | classes.py:687:23:687:27 | SSA variable other | +| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:702:20:702:23 | SSA variable self | +| classes.py:712:19:712:22 | ControlFlowNode for arg2 | classes.py:702:26:702:30 | SSA variable other | +| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:717:21:717:24 | SSA variable self | +| classes.py:727:20:727:23 | ControlFlowNode for arg2 | classes.py:717:27:717:31 | SSA variable other | +| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:732:22:732:25 | SSA variable self | +| classes.py:742:22:742:25 | ControlFlowNode for arg2 | classes.py:732:28:732:32 | SSA variable other | +| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:747:17:747:20 | SSA variable self | +| classes.py:757:16:757:19 | ControlFlowNode for arg2 | classes.py:747:23:747:27 | SSA variable other | +| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:777:17:777:20 | SSA variable self | +| classes.py:793:17:793:20 | ControlFlowNode for arg2 | classes.py:777:23:777:27 | SSA variable other | +| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:798:20:798:23 | SSA variable self | +| classes.py:808:20:808:23 | ControlFlowNode for arg2 | classes.py:798:26:798:30 | SSA variable other | +| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:813:20:813:23 | SSA variable self | +| classes.py:823:20:823:23 | ControlFlowNode for arg2 | classes.py:813:26:813:30 | SSA variable other | +| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:828:17:828:20 | SSA variable self | +| classes.py:838:16:838:19 | ControlFlowNode for arg2 | classes.py:828:23:828:27 | SSA variable other | +| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:843:17:843:20 | SSA variable self | +| classes.py:853:16:853:19 | ControlFlowNode for arg2 | classes.py:843:23:843:27 | SSA variable other | +| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:858:16:858:19 | SSA variable self | +| classes.py:868:15:868:18 | ControlFlowNode for arg2 | classes.py:858:22:858:26 | SSA variable other | diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index 6eda1e4152f..11e1387d0c5 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -28,17 +28,17 @@ edges | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:36:21:36:26 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:50:9:50:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:84:10:84:15 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:91:10:91:15 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:101:10:101:15 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:106:22:106:27 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:111:10:111:15 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:124:10:124:15 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:129:10:129:15 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:134:22:134:27 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:139:10:139:15 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:42:21:42:26 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:55:9:55:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:9:87:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:93:10:93:15 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:103:10:103:15 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:108:22:108:27 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:113:10:113:15 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:125:10:125:15 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:130:10:130:15 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:135:22:135:27 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:140:10:140:15 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:152:15:152:20 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:157:15:157:20 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:183:23:183:28 | ControlFlowNode for SOURCE | @@ -48,8 +48,10 @@ edges | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:340:11:340:16 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:344:16:344:21 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:365:28:365:33 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:373:30:373:35 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:442:12:442:17 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:449:28:449:33 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:463:30:463:35 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:499:9:499:14 | ControlFlowNode for SOURCE | | test.py:20:1:20:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:20:1:20:6 | GSSA Variable SOURCE | @@ -147,8 +149,10 @@ edges | test.py:344:10:344:22 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:344:10:344:27 | ControlFlowNode for Subscript | | test.py:344:16:344:21 | ControlFlowNode for SOURCE | test.py:344:10:344:22 | ControlFlowNode for Dict [Dictionary element at key s] | | test.py:365:28:365:33 | ControlFlowNode for SOURCE | test.py:365:10:365:34 | ControlFlowNode for second() | +| test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() | | test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() | | test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() | +| test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for b | nodes @@ -292,10 +296,14 @@ nodes | test.py:344:16:344:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:365:10:365:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | | test.py:365:28:365:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:373:10:373:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:373:30:373:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:442:10:442:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | | test.py:442:12:442:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:449:10:449:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | | test.py:449:28:449:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:463:10:463:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:463:30:463:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:501:10:501:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | | test.py:506:10:506:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | @@ -324,55 +332,59 @@ nodes | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found | | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:13:10:13:17 | ControlFlowNode for Str | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | Flow found | | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | Flow found | -| test.py:38:10:38:10 | ControlFlowNode for y | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:38:10:38:10 | ControlFlowNode for y | Flow found | -| test.py:38:10:38:10 | ControlFlowNode for y | test.py:36:21:36:26 | ControlFlowNode for SOURCE | test.py:38:10:38:10 | ControlFlowNode for y | Flow found | -| test.py:51:10:51:10 | ControlFlowNode for x | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:51:10:51:10 | ControlFlowNode for x | Flow found | -| test.py:51:10:51:10 | ControlFlowNode for x | test.py:50:9:50:14 | ControlFlowNode for SOURCE | test.py:51:10:51:10 | ControlFlowNode for x | Flow found | -| test.py:58:10:58:10 | ControlFlowNode for x | test.py:57:9:57:16 | ControlFlowNode for Str | test.py:58:10:58:10 | ControlFlowNode for x | Flow found | -| test.py:63:10:63:10 | ControlFlowNode for x | test.py:62:9:62:17 | ControlFlowNode for Str | test.py:63:10:63:10 | ControlFlowNode for x | Flow found | -| test.py:68:10:68:10 | ControlFlowNode for x | test.py:67:9:67:10 | ControlFlowNode for IntegerLiteral | test.py:68:10:68:10 | ControlFlowNode for x | Flow found | -| test.py:73:10:73:10 | ControlFlowNode for x | test.py:72:9:72:12 | ControlFlowNode for FloatLiteral | test.py:73:10:73:10 | ControlFlowNode for x | Flow found | -| test.py:85:10:85:10 | ControlFlowNode for x | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:85:10:85:10 | ControlFlowNode for x | Flow found | -| test.py:85:10:85:10 | ControlFlowNode for x | test.py:84:10:84:15 | ControlFlowNode for SOURCE | test.py:85:10:85:10 | ControlFlowNode for x | Flow found | -| test.py:92:10:92:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:92:10:92:13 | ControlFlowNode for Subscript | Flow found | -| test.py:92:10:92:13 | ControlFlowNode for Subscript | test.py:91:10:91:15 | ControlFlowNode for SOURCE | test.py:92:10:92:13 | ControlFlowNode for Subscript | Flow found | -| test.py:102:10:102:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:102:10:102:13 | ControlFlowNode for Subscript | Flow found | -| test.py:102:10:102:13 | ControlFlowNode for Subscript | test.py:101:10:101:15 | ControlFlowNode for SOURCE | test.py:102:10:102:13 | ControlFlowNode for Subscript | Flow found | -| test.py:107:10:107:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:107:10:107:13 | ControlFlowNode for Subscript | Flow found | -| test.py:107:10:107:13 | ControlFlowNode for Subscript | test.py:106:22:106:27 | ControlFlowNode for SOURCE | test.py:107:10:107:13 | ControlFlowNode for Subscript | Flow found | -| test.py:113:10:113:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:113:10:113:13 | ControlFlowNode for Subscript | Flow found | -| test.py:113:10:113:13 | ControlFlowNode for Subscript | test.py:111:10:111:15 | ControlFlowNode for SOURCE | test.py:113:10:113:13 | ControlFlowNode for Subscript | Flow found | -| test.py:125:10:125:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:125:10:125:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:125:10:125:16 | ControlFlowNode for Attribute() | test.py:124:10:124:15 | ControlFlowNode for SOURCE | test.py:125:10:125:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:130:10:130:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:130:10:130:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:130:10:130:16 | ControlFlowNode for Attribute() | test.py:129:10:129:15 | ControlFlowNode for SOURCE | test.py:130:10:130:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:135:10:135:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:135:10:135:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:135:10:135:16 | ControlFlowNode for Attribute() | test.py:134:22:134:27 | ControlFlowNode for SOURCE | test.py:135:10:135:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:141:10:141:16 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:141:10:141:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:141:10:141:16 | ControlFlowNode for Attribute() | test.py:139:10:139:15 | ControlFlowNode for SOURCE | test.py:141:10:141:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:153:10:153:15 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:153:10:153:15 | ControlFlowNode for Subscript | Flow found | +| test.py:44:10:44:10 | ControlFlowNode for y | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:44:10:44:10 | ControlFlowNode for y | Flow found | +| test.py:44:10:44:10 | ControlFlowNode for y | test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for y | Flow found | +| test.py:56:10:56:10 | ControlFlowNode for x | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:56:10:56:10 | ControlFlowNode for x | Flow found | +| test.py:56:10:56:10 | ControlFlowNode for x | test.py:55:9:55:14 | ControlFlowNode for SOURCE | test.py:56:10:56:10 | ControlFlowNode for x | Flow found | +| test.py:62:10:62:10 | ControlFlowNode for x | test.py:61:9:61:16 | ControlFlowNode for Str | test.py:62:10:62:10 | ControlFlowNode for x | Flow found | +| test.py:67:10:67:10 | ControlFlowNode for x | test.py:66:9:66:17 | ControlFlowNode for Str | test.py:67:10:67:10 | ControlFlowNode for x | Flow found | +| test.py:72:10:72:10 | ControlFlowNode for x | test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | test.py:72:10:72:10 | ControlFlowNode for x | Flow found | +| test.py:77:10:77:10 | ControlFlowNode for x | test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | test.py:77:10:77:10 | ControlFlowNode for x | Flow found | +| test.py:88:10:88:10 | ControlFlowNode for x | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:88:10:88:10 | ControlFlowNode for x | Flow found | +| test.py:88:10:88:10 | ControlFlowNode for x | test.py:87:9:87:14 | ControlFlowNode for SOURCE | test.py:88:10:88:10 | ControlFlowNode for x | Flow found | +| test.py:94:10:94:13 | ControlFlowNode for Subscript | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:94:10:94:13 | ControlFlowNode for Subscript | Flow found | +| test.py:94:10:94:13 | ControlFlowNode for Subscript | test.py:93:10:93:15 | ControlFlowNode for SOURCE | test.py:94:10:94:13 | ControlFlowNode for Subscript | Flow found | +| test.py:104:10:104:13 | ControlFlowNode for Subscript | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:104:10:104:13 | ControlFlowNode for Subscript | Flow found | +| test.py:104:10:104:13 | ControlFlowNode for Subscript | test.py:103:10:103:15 | ControlFlowNode for SOURCE | test.py:104:10:104:13 | ControlFlowNode for Subscript | Flow found | +| test.py:109:10:109:13 | ControlFlowNode for Subscript | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:109:10:109:13 | ControlFlowNode for Subscript | Flow found | +| test.py:109:10:109:13 | ControlFlowNode for Subscript | test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:109:10:109:13 | ControlFlowNode for Subscript | Flow found | +| test.py:115:10:115:13 | ControlFlowNode for Subscript | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:115:10:115:13 | ControlFlowNode for Subscript | Flow found | +| test.py:115:10:115:13 | ControlFlowNode for Subscript | test.py:113:10:113:15 | ControlFlowNode for SOURCE | test.py:115:10:115:13 | ControlFlowNode for Subscript | Flow found | +| test.py:126:10:126:16 | ControlFlowNode for Attribute() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:126:10:126:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:126:10:126:16 | ControlFlowNode for Attribute() | test.py:125:10:125:15 | ControlFlowNode for SOURCE | test.py:126:10:126:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:131:10:131:16 | ControlFlowNode for Attribute() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:131:10:131:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:131:10:131:16 | ControlFlowNode for Attribute() | test.py:130:10:130:15 | ControlFlowNode for SOURCE | test.py:131:10:131:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:136:10:136:16 | ControlFlowNode for Attribute() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:136:10:136:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:136:10:136:16 | ControlFlowNode for Attribute() | test.py:135:22:135:27 | ControlFlowNode for SOURCE | test.py:136:10:136:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:142:10:142:16 | ControlFlowNode for Attribute() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:142:10:142:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:142:10:142:16 | ControlFlowNode for Attribute() | test.py:140:10:140:15 | ControlFlowNode for SOURCE | test.py:142:10:142:16 | ControlFlowNode for Attribute() | Flow found | +| test.py:153:10:153:15 | ControlFlowNode for Subscript | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:153:10:153:15 | ControlFlowNode for Subscript | Flow found | | test.py:153:10:153:15 | ControlFlowNode for Subscript | test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:153:10:153:15 | ControlFlowNode for Subscript | Flow found | -| test.py:158:10:158:19 | ControlFlowNode for Attribute() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:158:10:158:19 | ControlFlowNode for Attribute() | Flow found | +| test.py:158:10:158:19 | ControlFlowNode for Attribute() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:158:10:158:19 | ControlFlowNode for Attribute() | Flow found | | test.py:158:10:158:19 | ControlFlowNode for Attribute() | test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:158:10:158:19 | ControlFlowNode for Attribute() | Flow found | -| test.py:185:10:185:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:185:10:185:13 | ControlFlowNode for Subscript | Flow found | -| test.py:185:10:185:13 | ControlFlowNode for Subscript | test.py:184:23:184:28 | ControlFlowNode for SOURCE | test.py:185:10:185:13 | ControlFlowNode for Subscript | Flow found | -| test.py:190:10:190:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:190:10:190:13 | ControlFlowNode for Subscript | Flow found | -| test.py:190:10:190:13 | ControlFlowNode for Subscript | test.py:189:25:189:30 | ControlFlowNode for SOURCE | test.py:190:10:190:13 | ControlFlowNode for Subscript | Flow found | -| test.py:201:10:201:13 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:201:10:201:13 | ControlFlowNode for Subscript | Flow found | -| test.py:201:10:201:13 | ControlFlowNode for Subscript | test.py:200:34:200:39 | ControlFlowNode for SOURCE | test.py:201:10:201:13 | ControlFlowNode for Subscript | Flow found | -| test.py:344:10:344:21 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:344:10:344:21 | ControlFlowNode for Subscript | Flow found | -| test.py:344:10:344:21 | ControlFlowNode for Subscript | test.py:344:11:344:16 | ControlFlowNode for SOURCE | test.py:344:10:344:21 | ControlFlowNode for Subscript | Flow found | -| test.py:348:10:348:20 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:348:10:348:20 | ControlFlowNode for Subscript | Flow found | -| test.py:348:10:348:20 | ControlFlowNode for Subscript | test.py:348:11:348:16 | ControlFlowNode for SOURCE | test.py:348:10:348:20 | ControlFlowNode for Subscript | Flow found | -| test.py:352:10:352:27 | ControlFlowNode for Subscript | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:352:10:352:27 | ControlFlowNode for Subscript | Flow found | -| test.py:352:10:352:27 | ControlFlowNode for Subscript | test.py:352:16:352:21 | ControlFlowNode for SOURCE | test.py:352:10:352:27 | ControlFlowNode for Subscript | Flow found | -| test.py:375:10:375:34 | ControlFlowNode for second() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:375:10:375:34 | ControlFlowNode for second() | Flow found | -| test.py:375:10:375:34 | ControlFlowNode for second() | test.py:375:28:375:33 | ControlFlowNode for SOURCE | test.py:375:10:375:34 | ControlFlowNode for second() | Flow found | -| test.py:457:10:457:18 | ControlFlowNode for f() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:457:10:457:18 | ControlFlowNode for f() | Flow found | -| test.py:457:10:457:18 | ControlFlowNode for f() | test.py:457:12:457:17 | ControlFlowNode for SOURCE | test.py:457:10:457:18 | ControlFlowNode for f() | Flow found | -| test.py:462:10:462:34 | ControlFlowNode for second() | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:462:10:462:34 | ControlFlowNode for second() | Flow found | -| test.py:462:10:462:34 | ControlFlowNode for second() | test.py:462:28:462:33 | ControlFlowNode for SOURCE | test.py:462:10:462:34 | ControlFlowNode for second() | Flow found | -| test.py:506:10:506:10 | ControlFlowNode for a | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:506:10:506:10 | ControlFlowNode for a | Flow found | -| test.py:506:10:506:10 | ControlFlowNode for a | test.py:504:9:504:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for a | Flow found | -| test.py:511:10:511:10 | ControlFlowNode for b | test.py:14:10:14:17 | ControlFlowNode for Str | test.py:511:10:511:10 | ControlFlowNode for b | Flow found | -| test.py:511:10:511:10 | ControlFlowNode for b | test.py:504:9:504:14 | ControlFlowNode for SOURCE | test.py:511:10:511:10 | ControlFlowNode for b | Flow found | +| test.py:184:10:184:13 | ControlFlowNode for Subscript | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:184:10:184:13 | ControlFlowNode for Subscript | Flow found | +| test.py:184:10:184:13 | ControlFlowNode for Subscript | test.py:183:23:183:28 | ControlFlowNode for SOURCE | test.py:184:10:184:13 | ControlFlowNode for Subscript | Flow found | +| test.py:189:10:189:13 | ControlFlowNode for Subscript | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:189:10:189:13 | ControlFlowNode for Subscript | Flow found | +| test.py:189:10:189:13 | ControlFlowNode for Subscript | test.py:188:25:188:30 | ControlFlowNode for SOURCE | test.py:189:10:189:13 | ControlFlowNode for Subscript | Flow found | +| test.py:200:10:200:13 | ControlFlowNode for Subscript | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:200:10:200:13 | ControlFlowNode for Subscript | Flow found | +| test.py:200:10:200:13 | ControlFlowNode for Subscript | test.py:199:34:199:39 | ControlFlowNode for SOURCE | test.py:200:10:200:13 | ControlFlowNode for Subscript | Flow found | +| test.py:336:10:336:21 | ControlFlowNode for Subscript | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:336:10:336:21 | ControlFlowNode for Subscript | Flow found | +| test.py:336:10:336:21 | ControlFlowNode for Subscript | test.py:336:11:336:16 | ControlFlowNode for SOURCE | test.py:336:10:336:21 | ControlFlowNode for Subscript | Flow found | +| test.py:340:10:340:20 | ControlFlowNode for Subscript | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:340:10:340:20 | ControlFlowNode for Subscript | Flow found | +| test.py:340:10:340:20 | ControlFlowNode for Subscript | test.py:340:11:340:16 | ControlFlowNode for SOURCE | test.py:340:10:340:20 | ControlFlowNode for Subscript | Flow found | +| test.py:344:10:344:27 | ControlFlowNode for Subscript | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:344:10:344:27 | ControlFlowNode for Subscript | Flow found | +| test.py:344:10:344:27 | ControlFlowNode for Subscript | test.py:344:16:344:21 | ControlFlowNode for SOURCE | test.py:344:10:344:27 | ControlFlowNode for Subscript | Flow found | +| test.py:365:10:365:34 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:365:10:365:34 | ControlFlowNode for second() | Flow found | +| test.py:365:10:365:34 | ControlFlowNode for second() | test.py:365:28:365:33 | ControlFlowNode for SOURCE | test.py:365:10:365:34 | ControlFlowNode for second() | Flow found | +| test.py:373:10:373:36 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:373:10:373:36 | ControlFlowNode for second() | Flow found | +| test.py:373:10:373:36 | ControlFlowNode for second() | test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() | Flow found | +| test.py:442:10:442:18 | ControlFlowNode for f() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:442:10:442:18 | ControlFlowNode for f() | Flow found | +| test.py:442:10:442:18 | ControlFlowNode for f() | test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() | Flow found | +| test.py:449:10:449:34 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:449:10:449:34 | ControlFlowNode for second() | Flow found | +| test.py:449:10:449:34 | ControlFlowNode for second() | test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() | Flow found | +| test.py:463:10:463:36 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:463:10:463:36 | ControlFlowNode for second() | Flow found | +| test.py:463:10:463:36 | ControlFlowNode for second() | test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() | Flow found | +| test.py:501:10:501:10 | ControlFlowNode for a | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:501:10:501:10 | ControlFlowNode for a | Flow found | +| test.py:501:10:501:10 | ControlFlowNode for a | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a | Flow found | +| test.py:506:10:506:10 | ControlFlowNode for b | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:506:10:506:10 | ControlFlowNode for b | Flow found | +| test.py:506:10:506:10 | ControlFlowNode for b | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for b | Flow found | diff --git a/python/ql/test/experimental/dataflow/coverage/test.py b/python/ql/test/experimental/dataflow/coverage/test.py index e8569e11756..ebda5c698b5 100644 --- a/python/ql/test/experimental/dataflow/coverage/test.py +++ b/python/ql/test/experimental/dataflow/coverage/test.py @@ -370,7 +370,7 @@ def test_call_positional_negative(): def test_call_keyword(): - SINK(second(NONSOURCE, b=SOURCE)) # Flow missing + SINK(second(NONSOURCE, b=SOURCE)) def test_call_unpack_iterable(): @@ -460,7 +460,7 @@ def test_lambda_keyword(): def second(a, b): return b - SINK(second(NONSOURCE, b=SOURCE)) # Flow missing + SINK(second(NONSOURCE, b=SOURCE)) def test_lambda_unpack_iterable(): From 27af9bbae8edd85860d2661b707eeb9bc2c55af1 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 29 Sep 2020 13:14:45 +0200 Subject: [PATCH 009/166] Python: Support overflow positional arguments Currently ignoring starred arguments --- .../dataflow/internal/DataFlowPrivate.qll | 84 +++++++++++++++---- .../dataflow/internal/DataFlowPublic.qll | 12 ++- .../coverage/argumentRouting1.expected | 1 + .../dataflow/coverage/dataflow.expected | 16 ++++ .../experimental/dataflow/coverage/test.py | 4 +- 5 files changed, 95 insertions(+), 22 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 64c5c049ae1..3dd8469ddb1 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -235,6 +235,61 @@ private Node update(Node node) { // Global flow //-------- // +/** Computes routing of arguments to parameters */ +module ArgumentPassing { + NameNode getParameter(CallableValue callable, int n) { + // positional parameter + result = callable.getParameter(n) + or + // vararg + exists(Function f | + f = callable.getScope() and + n = f.getPositionalParameterCount() and + result = f.getVararg().getAFlowNode() + ) + } + + /** + * Gets the argument to `call` then is passed to the `n`th parameter of `callable`. + */ + Node getArg(CallNode call, CallableValue callable, int n) { + call = callable.getACall() and + ( + // positional argument + result = TCfgNode(call.getArg(n)) + or + // keyword argument + exists(Function f, string argName | + f = callable.getScope() and + f.getArgName(n) = argName and + result = TCfgNode(call.getArgByName(argName)) + ) + or + // vararg + exists(Function f | + f = callable.getScope() and + f.hasVarArg() and + n = f.getPositionalParameterCount() and + result = TPosOverflowNode(call, callable) + ) + ) + } + + ControlFlowNode getPositionalOverflowArg(CallNode call, CallableValue callable, int n) { + call = callable.getACall() and + exists(Function f, int posCount, int argNr | + f = callable.getScope() and + f.hasVarArg() and + posCount = f.getPositionalParameterCount() and + result = call.getArg(argNr) and + argNr >= posCount and + argNr = posCount + n + ) + } +} + +import ArgumentPassing + /** * IPA type for DataFlowCallable. * @@ -278,7 +333,7 @@ class DataFlowCallableValue extends DataFlowCallable, TCallableValue { override Scope getScope() { result = callable.getScope() } - override NameNode getParameter(int n) { result = callable.getParameter(n) } + override NameNode getParameter(int n) { result = getParameter(callable, n) } override string getName() { result = callable.getName() } @@ -345,19 +400,6 @@ abstract class DataFlowCall extends TDataFlowCall { Location getLocation() { result = this.getNode().getLocation() } } -ControlFlowNode getArg(CallNode call, CallableValue callable, int n) { - call = callable.getACall() and - ( - result = call.getArg(n) - or - exists(Function f, string argName | - f = callable.getScope() and - f.getArgName(n) = argName and - result = call.getArgByName(argName) - ) - ) -} - /** Represents a call to a callable (currently only callable values). */ class CallNodeCall extends DataFlowCall, TCallNode { CallNode call; @@ -370,7 +412,7 @@ class CallNodeCall extends DataFlowCall, TCallNode { override string toString() { result = call.toString() } - override Node getArg(int n) { result = TCfgNode(getArg(call, callable.getCallableValue(), n)) } + override Node getArg(int n) { result = getArg(call, callable.getCallableValue(), n) } override ControlFlowNode getNode() { result = call } @@ -394,7 +436,7 @@ class ClassCall extends DataFlowCall, TClassCall { override string toString() { result = call.toString() } override Node getArg(int n) { - n > 0 and result = TCfgNode(getArg(call, this.getCallableValue(), n - 1)) + n > 0 and result = getArg(call, this.getCallableValue(), n - 1) or n = 0 and result = TSyntheticPreUpdateNode(TCfgNode(call)) } @@ -550,6 +592,8 @@ predicate storeStep(Node nodeFrom, Content c, Node nodeTo) { comprehensionStoreStep(nodeFrom, c, nodeTo) or attributeStoreStep(nodeFrom, c, nodeTo) + or + posOverflowStoreStep(nodeFrom, c, nodeTo) } /** Data flows from an element of a list to the list. */ @@ -645,6 +689,14 @@ predicate attributeStoreStep(CfgNode nodeFrom, AttributeContent c, PostUpdateNod ) } +predicate posOverflowStoreStep(CfgNode nodeFrom, TupleElementContent c, Node nodeTo) { + exists(CallNode call, CallableValue callable, int n | + nodeFrom.asCfgNode() = getPositionalOverflowArg(call, callable, n) and + nodeTo = TPosOverflowNode(call, callable) and + c.getIndex() = n + ) +} + /** * Holds if data can flow from `nodeFrom` to `nodeTo` via a read of content `c`. */ diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index fb68fe3b1f1..5d3b94c3d5a 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -23,12 +23,16 @@ newtype TNode = TEssaNode(EssaVariable var) or /** A node corresponding to a control flow node. */ TCfgNode(DataFlowCfgNode node) or - /** A synthetic node representing the value of an object before a state change */ + /** A synthetic node representing the value of an object before a state change. */ TSyntheticPreUpdateNode(NeedsSyntheticPreUpdateNode post) or - /** A synthetic node representing the value of an object after a state change */ + /** A synthetic node representing the value of an object after a state change. */ TSyntheticPostUpdateNode(NeedsSyntheticPostUpdateNode pre) or - /** A node representing a global (module-level) variable in a specific module */ - TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m and v.escapes() } + /** A node representing a global (module-level) variable in a specific module. */ + TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m and v.escapes() } or + /** A node representing the overflow positional arguments to a call. */ + TPosOverflowNode(CallNode call, CallableValue callable) { + exists(getPositionalOverflowArg(call, callable, _)) + } /** * An element, viewed as a node in a data flow graph. Either an SSA variable diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected index bb05dbe40f8..1e12b6f5460 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected @@ -9,6 +9,7 @@ | argumentPassing.py:133:46:133:49 | ControlFlowNode for arg1 | argumentPassing.py:118:11:118:13 | ControlFlowNode for foo | | argumentPassing.py:141:14:141:17 | ControlFlowNode for arg1 | argumentPassing.py:139:15:139:15 | ControlFlowNode for a | | argumentPassing.py:148:19:148:22 | ControlFlowNode for arg1 | argumentPassing.py:146:15:146:15 | ControlFlowNode for a | +| argumentPassing.py:156:15:156:18 | ControlFlowNode for arg1 | argumentPassing.py:154:19:154:22 | ControlFlowNode for Subscript | | argumentPassing.py:163:13:163:16 | ControlFlowNode for arg1 | argumentPassing.py:161:15:161:15 | ControlFlowNode for a | | argumentPassing.py:170:16:170:19 | ControlFlowNode for arg1 | argumentPassing.py:168:15:168:15 | ControlFlowNode for a | | argumentPassing.py:177:15:177:18 | ControlFlowNode for arg1 | argumentPassing.py:175:15:175:15 | ControlFlowNode for a | diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index 11e1387d0c5..69cd4cc67d3 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -28,6 +28,8 @@ edges | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | +| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | +| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:42:21:42:26 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:55:9:55:14 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:9:87:14 | ControlFlowNode for SOURCE | @@ -49,9 +51,11 @@ edges | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:344:16:344:21 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:365:28:365:33 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:373:30:373:35 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:389:33:389:38 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:442:12:442:17 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:449:28:449:33 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:463:30:463:35 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:482:33:482:38 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:499:9:499:14 | ControlFlowNode for SOURCE | | test.py:20:1:20:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:20:1:20:6 | GSSA Variable SOURCE | @@ -150,9 +154,11 @@ edges | test.py:344:16:344:21 | ControlFlowNode for SOURCE | test.py:344:10:344:22 | ControlFlowNode for Dict [Dictionary element at key s] | | test.py:365:28:365:33 | ControlFlowNode for SOURCE | test.py:365:10:365:34 | ControlFlowNode for second() | | test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() | +| test.py:389:33:389:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | | test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() | | test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() | | test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() | +| test.py:482:33:482:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for b | nodes @@ -175,6 +181,8 @@ nodes | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] | | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] | | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] | +| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module test | | test.py:20:1:20:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | | test.py:20:10:20:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | @@ -298,12 +306,16 @@ nodes | test.py:365:28:365:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:373:10:373:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | | test.py:373:30:373:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | +| test.py:389:33:389:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:442:10:442:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | | test.py:442:12:442:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:449:10:449:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | | test.py:449:28:449:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:463:10:463:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | | test.py:463:30:463:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | +| test.py:482:33:482:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:501:10:501:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | | test.py:506:10:506:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | @@ -378,12 +390,16 @@ nodes | test.py:365:10:365:34 | ControlFlowNode for second() | test.py:365:28:365:33 | ControlFlowNode for SOURCE | test.py:365:10:365:34 | ControlFlowNode for second() | Flow found | | test.py:373:10:373:36 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:373:10:373:36 | ControlFlowNode for second() | Flow found | | test.py:373:10:373:36 | ControlFlowNode for second() | test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() | Flow found | +| test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | Flow found | +| test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | test.py:389:33:389:38 | ControlFlowNode for SOURCE | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | Flow found | | test.py:442:10:442:18 | ControlFlowNode for f() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:442:10:442:18 | ControlFlowNode for f() | Flow found | | test.py:442:10:442:18 | ControlFlowNode for f() | test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() | Flow found | | test.py:449:10:449:34 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:449:10:449:34 | ControlFlowNode for second() | Flow found | | test.py:449:10:449:34 | ControlFlowNode for second() | test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() | Flow found | | test.py:463:10:463:36 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:463:10:463:36 | ControlFlowNode for second() | Flow found | | test.py:463:10:463:36 | ControlFlowNode for second() | test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() | Flow found | +| test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | Flow found | +| test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | test.py:482:33:482:38 | ControlFlowNode for SOURCE | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | Flow found | | test.py:501:10:501:10 | ControlFlowNode for a | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:501:10:501:10 | ControlFlowNode for a | Flow found | | test.py:501:10:501:10 | ControlFlowNode for a | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a | Flow found | | test.py:506:10:506:10 | ControlFlowNode for b | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:506:10:506:10 | ControlFlowNode for b | Flow found | diff --git a/python/ql/test/experimental/dataflow/coverage/test.py b/python/ql/test/experimental/dataflow/coverage/test.py index ebda5c698b5..031646b9208 100644 --- a/python/ql/test/experimental/dataflow/coverage/test.py +++ b/python/ql/test/experimental/dataflow/coverage/test.py @@ -386,7 +386,7 @@ def f_extra_pos(a, *b): def test_call_extra_pos(): - SINK(f_extra_pos(NONSOURCE, SOURCE)) # Flow missing + SINK(f_extra_pos(NONSOURCE, SOURCE)) def f_extra_keyword(a, **b): @@ -479,7 +479,7 @@ def test_lambda_unpack_mapping(): def test_lambda_extra_pos(): f_extra_pos = lambda a, *b: b[0] - SINK(f_extra_pos(NONSOURCE, SOURCE)) # Flow missing + SINK(f_extra_pos(NONSOURCE, SOURCE)) def test_lambda_extra_keyword(): From e02cfbf6b0367a3957543695ced7f102556ff89d Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 29 Sep 2020 13:50:04 +0200 Subject: [PATCH 010/166] Python: Support keyword overflow arguments --- .../dataflow/internal/DataFlowPrivate.qll | 62 +++++++++++++++++-- .../dataflow/internal/DataFlowPublic.qll | 4 ++ .../coverage/argumentRouting1.expected | 1 + .../dataflow/coverage/dataflow.expected | 16 +++++ .../experimental/dataflow/coverage/test.py | 4 +- 5 files changed, 80 insertions(+), 7 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 3dd8469ddb1..5e44cbe221e 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -237,20 +237,34 @@ private Node update(Node node) { // /** Computes routing of arguments to parameters */ module ArgumentPassing { + /** + * Gets the `n`th parameter of `callable`. + * If the callable has a starred parameter, say `*tuple`, that is matched with `n=-1`. + * If the callable has a doubly starred parameter, say `**dict`, that is matched with `n=-2`. + * Note that, unlike other languages, we do _not_ use -1 for the position of `self` in Python, + * as it is an explicit parameter at position 0. + */ NameNode getParameter(CallableValue callable, int n) { // positional parameter result = callable.getParameter(n) or - // vararg + // starred parameter, `*tuple` exists(Function f | f = callable.getScope() and - n = f.getPositionalParameterCount() and + n = -1 and result = f.getVararg().getAFlowNode() ) + or + // doubly starred parameter, `**dict` + exists(Function f | + f = callable.getScope() and + n = -2 and + result = f.getKwarg().getAFlowNode() + ) } /** - * Gets the argument to `call` then is passed to the `n`th parameter of `callable`. + * Gets the argument to `call` that is passed to the `n`th parameter of `callable`. */ Node getArg(CallNode call, CallableValue callable, int n) { call = callable.getACall() and @@ -265,16 +279,25 @@ module ArgumentPassing { result = TCfgNode(call.getArgByName(argName)) ) or - // vararg + // argument -1 is a synthezised argument passed to the starred parameter exists(Function f | f = callable.getScope() and f.hasVarArg() and - n = f.getPositionalParameterCount() and + n = -1 and result = TPosOverflowNode(call, callable) ) + or + // argument -2 is a synthezised argument passed to the doubly starred parameter + exists(Function f | + f = callable.getScope() and + f.hasKwArg() and + n = -2 and + result = TKwOverflowNode(call, callable) + ) ) } + /** Gets the control flow node that is passed as the `n`th overflow positional argument. */ ControlFlowNode getPositionalOverflowArg(CallNode call, CallableValue callable, int n) { call = callable.getACall() and exists(Function f, int posCount, int argNr | @@ -286,6 +309,17 @@ module ArgumentPassing { argNr = posCount + n ) } + + /** Gets the control flow node that is passed as the overflow keyword argument with key `key`. */ + ControlFlowNode getKeywordOverflowArg(CallNode call, CallableValue callable, string key) { + call = callable.getACall() and + exists(Function f | + f = callable.getScope() and + f.hasKwArg() and + not exists(f.getArgByName(key)) and + result = call.getArgByName(key) + ) + } } import ArgumentPassing @@ -594,6 +628,8 @@ predicate storeStep(Node nodeFrom, Content c, Node nodeTo) { attributeStoreStep(nodeFrom, c, nodeTo) or posOverflowStoreStep(nodeFrom, c, nodeTo) + or + kwOverflowStoreStep(nodeFrom, c, nodeTo) } /** Data flows from an element of a list to the list. */ @@ -689,6 +725,10 @@ predicate attributeStoreStep(CfgNode nodeFrom, AttributeContent c, PostUpdateNod ) } +/** + * Holds if `nodeFrom` flows into the synthezised positional overflow argument (`nodeTo`) + * at the position indicated by `c`. + */ predicate posOverflowStoreStep(CfgNode nodeFrom, TupleElementContent c, Node nodeTo) { exists(CallNode call, CallableValue callable, int n | nodeFrom.asCfgNode() = getPositionalOverflowArg(call, callable, n) and @@ -697,6 +737,18 @@ predicate posOverflowStoreStep(CfgNode nodeFrom, TupleElementContent c, Node nod ) } +/** + * Holds if `nodeFrom` flows into the synthezised keyword overflow argument (`nodeTo`) + * at the key indicated by `c`. + */ +predicate kwOverflowStoreStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) { + exists(CallNode call, CallableValue callable, string key | + nodeFrom.asCfgNode() = getKeywordOverflowArg(call, callable, key) and + nodeTo = TKwOverflowNode(call, callable) and + c.getKey() = key + ) +} + /** * Holds if data can flow from `nodeFrom` to `nodeTo` via a read of content `c`. */ diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 5d3b94c3d5a..4120b43949a 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -32,6 +32,10 @@ newtype TNode = /** A node representing the overflow positional arguments to a call. */ TPosOverflowNode(CallNode call, CallableValue callable) { exists(getPositionalOverflowArg(call, callable, _)) + } or + /** A node representing the overflow keyword arguments to a call. */ + TKwOverflowNode(CallNode call, CallableValue callable) { + exists(getKeywordOverflowArg(call, callable, _)) } /** diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected index 1e12b6f5460..0f920206d75 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected @@ -13,6 +13,7 @@ | argumentPassing.py:163:13:163:16 | ControlFlowNode for arg1 | argumentPassing.py:161:15:161:15 | ControlFlowNode for a | | argumentPassing.py:170:16:170:19 | ControlFlowNode for arg1 | argumentPassing.py:168:15:168:15 | ControlFlowNode for a | | argumentPassing.py:177:15:177:18 | ControlFlowNode for arg1 | argumentPassing.py:175:15:175:15 | ControlFlowNode for a | +| argumentPassing.py:184:23:184:26 | ControlFlowNode for arg1 | argumentPassing.py:182:15:182:20 | ControlFlowNode for Subscript | | classes.py:563:5:563:16 | SSA variable with_getitem | classes.py:557:15:557:18 | ControlFlowNode for self | | classes.py:578:5:578:16 | SSA variable with_setitem | classes.py:573:15:573:18 | ControlFlowNode for self | | classes.py:593:5:593:16 | SSA variable with_delitem | classes.py:588:15:588:18 | ControlFlowNode for self | diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index 69cd4cc67d3..b73078427ee 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -28,6 +28,8 @@ edges | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | +| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | +| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:42:21:42:26 | ControlFlowNode for SOURCE | @@ -52,10 +54,12 @@ edges | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:365:28:365:33 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:373:30:373:35 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:389:33:389:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:397:39:397:44 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:442:12:442:17 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:449:28:449:33 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:463:30:463:35 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:482:33:482:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:487:39:487:44 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:499:9:499:14 | ControlFlowNode for SOURCE | | test.py:20:1:20:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:20:1:20:6 | GSSA Variable SOURCE | @@ -155,10 +159,12 @@ edges | test.py:365:28:365:33 | ControlFlowNode for SOURCE | test.py:365:10:365:34 | ControlFlowNode for second() | | test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() | | test.py:389:33:389:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | +| test.py:397:39:397:44 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Dictionary element at key b] | | test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() | | test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() | | test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() | | test.py:482:33:482:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | +| test.py:487:39:487:44 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Dictionary element at key b] | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for b | nodes @@ -181,6 +187,8 @@ nodes | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] | | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] | | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | semmle.label | Data flow node [Dictionary element at key b] | +| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | semmle.label | Data flow node [Dictionary element at key b] | | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] | | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module test | @@ -308,6 +316,8 @@ nodes | test.py:373:30:373:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | | test.py:389:33:389:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | +| test.py:397:39:397:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:442:10:442:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | | test.py:442:12:442:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:449:10:449:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | @@ -316,6 +326,8 @@ nodes | test.py:463:30:463:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | | test.py:482:33:482:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | +| test.py:487:39:487:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:501:10:501:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | | test.py:506:10:506:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | @@ -392,6 +404,8 @@ nodes | test.py:373:10:373:36 | ControlFlowNode for second() | test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() | Flow found | | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | Flow found | | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | test.py:389:33:389:38 | ControlFlowNode for SOURCE | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | Flow found | +| test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | Flow found | +| test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | test.py:397:39:397:44 | ControlFlowNode for SOURCE | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | Flow found | | test.py:442:10:442:18 | ControlFlowNode for f() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:442:10:442:18 | ControlFlowNode for f() | Flow found | | test.py:442:10:442:18 | ControlFlowNode for f() | test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() | Flow found | | test.py:449:10:449:34 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:449:10:449:34 | ControlFlowNode for second() | Flow found | @@ -400,6 +414,8 @@ nodes | test.py:463:10:463:36 | ControlFlowNode for second() | test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() | Flow found | | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | Flow found | | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | test.py:482:33:482:38 | ControlFlowNode for SOURCE | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | Flow found | +| test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | Flow found | +| test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | test.py:487:39:487:44 | ControlFlowNode for SOURCE | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | Flow found | | test.py:501:10:501:10 | ControlFlowNode for a | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:501:10:501:10 | ControlFlowNode for a | Flow found | | test.py:501:10:501:10 | ControlFlowNode for a | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a | Flow found | | test.py:506:10:506:10 | ControlFlowNode for b | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:506:10:506:10 | ControlFlowNode for b | Flow found | diff --git a/python/ql/test/experimental/dataflow/coverage/test.py b/python/ql/test/experimental/dataflow/coverage/test.py index 031646b9208..70483b918ed 100644 --- a/python/ql/test/experimental/dataflow/coverage/test.py +++ b/python/ql/test/experimental/dataflow/coverage/test.py @@ -394,7 +394,7 @@ def f_extra_keyword(a, **b): def test_call_extra_keyword(): - SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) # Flow missing + SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) # return the name of the first extra keyword argument @@ -484,7 +484,7 @@ def test_lambda_extra_pos(): def test_lambda_extra_keyword(): f_extra_keyword = lambda a, **b: b["b"] - SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) # Flow missing + SINK(f_extra_keyword(NONSOURCE, b=SOURCE)) # call the function with our source as the name of the keyword arguemnt From 30d048f9d4196d62201efff38514093b62566a82 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Tue, 29 Sep 2020 15:36:38 +0200 Subject: [PATCH 011/166] Python: Support unpacking of keyword arguments. --- .../dataflow/internal/DataFlowPrivate.qll | 37 +++++++++++++++++++ .../dataflow/internal/DataFlowPublic.qll | 4 ++ .../experimental/dataflow/coverage/test.py | 4 +- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 5e44cbe221e..e2e220b6b87 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -294,6 +294,12 @@ module ArgumentPassing { n = -2 and result = TKwOverflowNode(call, callable) ) + or + // argument unpacked from dict + exists(string name | + call_unpacks(call, callable, name, n) and + result = TKwUnpacked(call, callable, name) + ) ) } @@ -320,6 +326,22 @@ module ArgumentPassing { result = call.getArgByName(key) ) } + + /** + * Holds if `call` unpacks a dictionary argument in order to pass it via `name`. + * It will then be passed to the `n`th parameter of `callable`. + */ + predicate call_unpacks(CallNode call, CallableValue callable, string name, int n) { + exists(Function f | + f = callable.getScope() and + not exists(call.getArg(n)) and // no positional arguement available + name = f.getArgName(n) and + not exists(call.getArgByName(name)) and // no keyword argument available + n >= 0 and + n < f.getPositionalParameterCount() + f.getKeywordOnlyParameterCount() and + exists(call.getNode().getKwargs()) // dict argument available + ) + } } import ArgumentPassing @@ -760,6 +782,8 @@ predicate readStep(Node nodeFrom, Content c, Node nodeTo) { comprehensionReadStep(nodeFrom, c, nodeTo) or attributeReadStep(nodeFrom, c, nodeTo) + or + kwUnpackReadStep(nodeFrom, c, nodeTo) } /** Data flows from a sequence to a subscript of the sequence. */ @@ -864,6 +888,19 @@ predicate attributeReadStep(CfgNode nodeFrom, AttributeContent c, CfgNode nodeTo ) } +/** + * Holds if `nodeFrom` is a dictionary argument being unpacked and `nodeTo` is the + * synthezised unpacked argument with the name indicated by `c`. + */ +predicate kwUnpackReadStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) { + exists(CallNode call, CallableValue callable, string name, int n | + call_unpacks(call, callable, name, n) and + nodeFrom.asCfgNode() = call.getNode().getKwargs().getAFlowNode() and + nodeTo = TKwUnpacked(call, callable, name) and + name = c.getKey() + ) +} + /** * Holds if values stored inside content `c` are cleared at node `n`. For example, * any value stored inside `f` is cleared at the pre-update node associated with `x` diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 4120b43949a..bc7d82a519b 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -36,6 +36,10 @@ newtype TNode = /** A node representing the overflow keyword arguments to a call. */ TKwOverflowNode(CallNode call, CallableValue callable) { exists(getKeywordOverflowArg(call, callable, _)) + } or + /** A node representing an unpacked element of a dictionary argument. */ + TKwUnpacked(CallNode call, CallableValue callable, string name) { + call_unpacks(call, callable, name, _) } /** diff --git a/python/ql/test/experimental/dataflow/coverage/test.py b/python/ql/test/experimental/dataflow/coverage/test.py index 70483b918ed..be47df8373a 100644 --- a/python/ql/test/experimental/dataflow/coverage/test.py +++ b/python/ql/test/experimental/dataflow/coverage/test.py @@ -378,7 +378,7 @@ def test_call_unpack_iterable(): def test_call_unpack_mapping(): - SINK(second(NONSOURCE, **{"b": SOURCE})) # Flow missing + SINK(second(NONSOURCE, **{"b": SOURCE})) def f_extra_pos(a, *b): @@ -474,7 +474,7 @@ def test_lambda_unpack_mapping(): def second(a, b): return b - SINK(second(NONSOURCE, **{"b": SOURCE})) # Flow missing + SINK(second(NONSOURCE, **{"b": SOURCE})) def test_lambda_extra_pos(): From 00966bba0d10670ab0857ad085f7134d7a99bc23 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 30 Sep 2020 13:11:23 +0200 Subject: [PATCH 012/166] Python: update test expectations --- .../coverage/argumentRouting2.expected | 1 + .../coverage/argumentRouting3.expected | 3 +++ .../dataflow/coverage/dataflow.expected | 20 +++++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected index 7bdd1f4ea16..03a300e512f 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected @@ -2,6 +2,7 @@ | argumentPassing.py:85:27:85:30 | ControlFlowNode for arg2 | argumentPassing.py:79:11:79:11 | ControlFlowNode for b | | argumentPassing.py:97:29:97:32 | ControlFlowNode for arg2 | argumentPassing.py:91:11:91:11 | ControlFlowNode for b | | argumentPassing.py:112:30:112:33 | ControlFlowNode for arg2 | argumentPassing.py:104:11:104:11 | ControlFlowNode for b | +| argumentPassing.py:133:36:133:39 | ControlFlowNode for arg2 | argumentPassing.py:123:11:123:13 | ControlFlowNode for bar | | classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:556:15:556:17 | ControlFlowNode for key | | classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:572:15:572:17 | ControlFlowNode for key | | classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:587:15:587:17 | ControlFlowNode for key | diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected index f0c70c48ec9..867809b437a 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected @@ -1,2 +1,5 @@ | argumentPassing.py:97:37:97:40 | ControlFlowNode for arg3 | argumentPassing.py:92:11:92:11 | ControlFlowNode for c | +| argumentPassing.py:98:43:98:46 | ControlFlowNode for arg3 | argumentPassing.py:92:11:92:11 | ControlFlowNode for c | +| argumentPassing.py:99:41:99:44 | ControlFlowNode for arg3 | argumentPassing.py:92:11:92:11 | ControlFlowNode for c | +| argumentPassing.py:113:36:113:39 | ControlFlowNode for arg3 | argumentPassing.py:105:11:105:11 | ControlFlowNode for c | | classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:571:15:571:19 | ControlFlowNode for value | diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index b73078427ee..c217e6e21c1 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -28,6 +28,8 @@ edges | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | +| file://:0:0:0:0 | Data flow node | test.py:381:10:381:43 | ControlFlowNode for second() | +| file://:0:0:0:0 | Data flow node | test.py:477:10:477:43 | ControlFlowNode for second() | | file://:0:0:0:0 | Data flow node [Dictionary element at key b] | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | | file://:0:0:0:0 | Data flow node [Dictionary element at key b] | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | @@ -53,11 +55,13 @@ edges | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:344:16:344:21 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:365:28:365:33 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:373:30:373:35 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:381:36:381:41 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:389:33:389:38 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:397:39:397:44 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:442:12:442:17 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:449:28:449:33 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:463:30:463:35 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:477:36:477:41 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:482:33:482:38 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:487:39:487:44 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:499:9:499:14 | ControlFlowNode for SOURCE | @@ -158,11 +162,15 @@ edges | test.py:344:16:344:21 | ControlFlowNode for SOURCE | test.py:344:10:344:22 | ControlFlowNode for Dict [Dictionary element at key s] | | test.py:365:28:365:33 | ControlFlowNode for SOURCE | test.py:365:10:365:34 | ControlFlowNode for second() | | test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() | +| test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | file://:0:0:0:0 | Data flow node | +| test.py:381:36:381:41 | ControlFlowNode for SOURCE | test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | | test.py:389:33:389:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | | test.py:397:39:397:44 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Dictionary element at key b] | | test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() | | test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() | | test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() | +| test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | file://:0:0:0:0 | Data flow node | +| test.py:477:36:477:41 | ControlFlowNode for SOURCE | test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | | test.py:482:33:482:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | | test.py:487:39:487:44 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Dictionary element at key b] | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a | @@ -187,6 +195,8 @@ nodes | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] | | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] | | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| file://:0:0:0:0 | Data flow node | semmle.label | Data flow node | +| file://:0:0:0:0 | Data flow node | semmle.label | Data flow node | | file://:0:0:0:0 | Data flow node [Dictionary element at key b] | semmle.label | Data flow node [Dictionary element at key b] | | file://:0:0:0:0 | Data flow node [Dictionary element at key b] | semmle.label | Data flow node [Dictionary element at key b] | | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] | @@ -314,6 +324,9 @@ nodes | test.py:365:28:365:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:373:10:373:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | | test.py:373:30:373:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:381:10:381:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | +| test.py:381:36:381:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | | test.py:389:33:389:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | @@ -324,6 +337,9 @@ nodes | test.py:449:28:449:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:463:10:463:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | | test.py:463:30:463:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:477:10:477:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | +| test.py:477:36:477:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | | test.py:482:33:482:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | @@ -402,6 +418,8 @@ nodes | test.py:365:10:365:34 | ControlFlowNode for second() | test.py:365:28:365:33 | ControlFlowNode for SOURCE | test.py:365:10:365:34 | ControlFlowNode for second() | Flow found | | test.py:373:10:373:36 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:373:10:373:36 | ControlFlowNode for second() | Flow found | | test.py:373:10:373:36 | ControlFlowNode for second() | test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() | Flow found | +| test.py:381:10:381:43 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:381:10:381:43 | ControlFlowNode for second() | Flow found | +| test.py:381:10:381:43 | ControlFlowNode for second() | test.py:381:36:381:41 | ControlFlowNode for SOURCE | test.py:381:10:381:43 | ControlFlowNode for second() | Flow found | | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | Flow found | | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | test.py:389:33:389:38 | ControlFlowNode for SOURCE | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | Flow found | | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | Flow found | @@ -412,6 +430,8 @@ nodes | test.py:449:10:449:34 | ControlFlowNode for second() | test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() | Flow found | | test.py:463:10:463:36 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:463:10:463:36 | ControlFlowNode for second() | Flow found | | test.py:463:10:463:36 | ControlFlowNode for second() | test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() | Flow found | +| test.py:477:10:477:43 | ControlFlowNode for second() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:477:10:477:43 | ControlFlowNode for second() | Flow found | +| test.py:477:10:477:43 | ControlFlowNode for second() | test.py:477:36:477:41 | ControlFlowNode for SOURCE | test.py:477:10:477:43 | ControlFlowNode for second() | Flow found | | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | Flow found | | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | test.py:482:33:482:38 | ControlFlowNode for SOURCE | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | Flow found | | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | test.py:20:10:20:17 | ControlFlowNode for Str | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | Flow found | From 4ae422ce1633d98887f6f32a28467d10998892e0 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 30 Sep 2020 15:28:29 +0200 Subject: [PATCH 013/166] Python: Add test for extraneous overflow arguments --- .../test/experimental/dataflow/coverage/argumentPassing.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py index 34ae519efc9..6f21f40111a 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py +++ b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py @@ -121,6 +121,10 @@ def grab_foo_bar_baz(foo, **kwargs): def grab_bar_baz(bar, **kwargs): SINK2(bar) + try: + SINK2(kwargs["bar"]) + except: + print("OK") grab_baz(**kwargs) @@ -128,7 +132,7 @@ def grab_baz(baz): SINK3(baz) -@expects(3) +@expects(4) def test_grab(): grab_foo_bar_baz(baz=arg3, bar=arg2, foo=arg1) From b0ed7af897a95883437127eaa64bd10687589506 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 30 Sep 2020 15:54:12 +0200 Subject: [PATCH 014/166] Python: Approximate **arg -> **param --- .../dataflow/internal/DataFlowPrivate.qll | 13 +++++++++---- .../dataflow/internal/DataFlowPublic.qll | 6 +++++- .../dataflow/coverage/argumentPassing.py | 2 +- .../dataflow/coverage/argumentRouting1.expected | 16 ++++++++-------- .../dataflow/coverage/argumentRouting2.expected | 3 ++- .../dataflow/coverage/argumentRouting3.expected | 1 + 6 files changed, 26 insertions(+), 15 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index e2e220b6b87..f33a844a936 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -279,7 +279,7 @@ module ArgumentPassing { result = TCfgNode(call.getArgByName(argName)) ) or - // argument -1 is a synthezised argument passed to the starred parameter + // a synthezised argument passed to the starred parameter (at position -1) exists(Function f | f = callable.getScope() and f.hasVarArg() and @@ -287,7 +287,7 @@ module ArgumentPassing { result = TPosOverflowNode(call, callable) ) or - // argument -2 is a synthezised argument passed to the doubly starred parameter + // a synthezised argument passed to the doubly starred parameter (at position -2) exists(Function f | f = callable.getScope() and f.hasKwArg() and @@ -300,6 +300,11 @@ module ArgumentPassing { call_unpacks(call, callable, name, n) and result = TKwUnpacked(call, callable, name) ) + or + // Dict argument is passed to the doubly starred parameter (at position -2). + // This is an overaaproximation, not removing unpacked arguments. + n = -2 and + result = TCfgNode(call.getNode().getKwargs().getAFlowNode()) ) } @@ -332,6 +337,7 @@ module ArgumentPassing { * It will then be passed to the `n`th parameter of `callable`. */ predicate call_unpacks(CallNode call, CallableValue callable, string name, int n) { + call = callable.getACall() and exists(Function f | f = callable.getScope() and not exists(call.getArg(n)) and // no positional arguement available @@ -893,8 +899,7 @@ predicate attributeReadStep(CfgNode nodeFrom, AttributeContent c, CfgNode nodeTo * synthezised unpacked argument with the name indicated by `c`. */ predicate kwUnpackReadStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) { - exists(CallNode call, CallableValue callable, string name, int n | - call_unpacks(call, callable, name, n) and + exists(CallNode call, CallableValue callable, string name | nodeFrom.asCfgNode() = call.getNode().getKwargs().getAFlowNode() and nodeTo = TKwUnpacked(call, callable, name) and name = c.getKey() diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index bc7d82a519b..f6f98ec7e71 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -37,7 +37,11 @@ newtype TNode = TKwOverflowNode(CallNode call, CallableValue callable) { exists(getKeywordOverflowArg(call, callable, _)) } or - /** A node representing an unpacked element of a dictionary argument. */ + /** + * A node representing an unpacked element of a dictionary argument. + * That is, `call` contains argument `**{"foo": bar}` which is passed + * to parameter `foo` of `callable. + */ TKwUnpacked(CallNode call, CallableValue callable, string name) { call_unpacks(call, callable, name, _) } diff --git a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py index 6f21f40111a..594163912f9 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py +++ b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py @@ -122,7 +122,7 @@ def grab_foo_bar_baz(foo, **kwargs): def grab_bar_baz(bar, **kwargs): SINK2(bar) try: - SINK2(kwargs["bar"]) + SINK2(kwargs["bar"]) # FP except: print("OK") grab_baz(**kwargs) diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected index 0f920206d75..9c3b9abc8e3 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected @@ -6,14 +6,14 @@ | argumentPassing.py:98:27:98:30 | ControlFlowNode for arg1 | argumentPassing.py:90:11:90:11 | ControlFlowNode for a | | argumentPassing.py:99:27:99:30 | ControlFlowNode for arg1 | argumentPassing.py:90:11:90:11 | ControlFlowNode for a | | argumentPassing.py:111:28:111:31 | ControlFlowNode for arg1 | argumentPassing.py:103:11:103:11 | ControlFlowNode for a | -| argumentPassing.py:133:46:133:49 | ControlFlowNode for arg1 | argumentPassing.py:118:11:118:13 | ControlFlowNode for foo | -| argumentPassing.py:141:14:141:17 | ControlFlowNode for arg1 | argumentPassing.py:139:15:139:15 | ControlFlowNode for a | -| argumentPassing.py:148:19:148:22 | ControlFlowNode for arg1 | argumentPassing.py:146:15:146:15 | ControlFlowNode for a | -| argumentPassing.py:156:15:156:18 | ControlFlowNode for arg1 | argumentPassing.py:154:19:154:22 | ControlFlowNode for Subscript | -| argumentPassing.py:163:13:163:16 | ControlFlowNode for arg1 | argumentPassing.py:161:15:161:15 | ControlFlowNode for a | -| argumentPassing.py:170:16:170:19 | ControlFlowNode for arg1 | argumentPassing.py:168:15:168:15 | ControlFlowNode for a | -| argumentPassing.py:177:15:177:18 | ControlFlowNode for arg1 | argumentPassing.py:175:15:175:15 | ControlFlowNode for a | -| argumentPassing.py:184:23:184:26 | ControlFlowNode for arg1 | argumentPassing.py:182:15:182:20 | ControlFlowNode for Subscript | +| argumentPassing.py:137:46:137:49 | ControlFlowNode for arg1 | argumentPassing.py:118:11:118:13 | ControlFlowNode for foo | +| argumentPassing.py:145:14:145:17 | ControlFlowNode for arg1 | argumentPassing.py:143:15:143:15 | ControlFlowNode for a | +| argumentPassing.py:152:19:152:22 | ControlFlowNode for arg1 | argumentPassing.py:150:15:150:15 | ControlFlowNode for a | +| argumentPassing.py:160:15:160:18 | ControlFlowNode for arg1 | argumentPassing.py:158:19:158:22 | ControlFlowNode for Subscript | +| argumentPassing.py:167:13:167:16 | ControlFlowNode for arg1 | argumentPassing.py:165:15:165:15 | ControlFlowNode for a | +| argumentPassing.py:174:16:174:19 | ControlFlowNode for arg1 | argumentPassing.py:172:15:172:15 | ControlFlowNode for a | +| argumentPassing.py:181:15:181:18 | ControlFlowNode for arg1 | argumentPassing.py:179:15:179:15 | ControlFlowNode for a | +| argumentPassing.py:188:23:188:26 | ControlFlowNode for arg1 | argumentPassing.py:186:15:186:20 | ControlFlowNode for Subscript | | classes.py:563:5:563:16 | SSA variable with_getitem | classes.py:557:15:557:18 | ControlFlowNode for self | | classes.py:578:5:578:16 | SSA variable with_setitem | classes.py:573:15:573:18 | ControlFlowNode for self | | classes.py:593:5:593:16 | SSA variable with_delitem | classes.py:588:15:588:18 | ControlFlowNode for self | diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected index 03a300e512f..fa6aa4c96cb 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected @@ -2,7 +2,8 @@ | argumentPassing.py:85:27:85:30 | ControlFlowNode for arg2 | argumentPassing.py:79:11:79:11 | ControlFlowNode for b | | argumentPassing.py:97:29:97:32 | ControlFlowNode for arg2 | argumentPassing.py:91:11:91:11 | ControlFlowNode for b | | argumentPassing.py:112:30:112:33 | ControlFlowNode for arg2 | argumentPassing.py:104:11:104:11 | ControlFlowNode for b | -| argumentPassing.py:133:36:133:39 | ControlFlowNode for arg2 | argumentPassing.py:123:11:123:13 | ControlFlowNode for bar | +| argumentPassing.py:137:36:137:39 | ControlFlowNode for arg2 | argumentPassing.py:123:11:123:13 | ControlFlowNode for bar | +| argumentPassing.py:137:36:137:39 | ControlFlowNode for arg2 | argumentPassing.py:125:15:125:27 | ControlFlowNode for Subscript | | classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:556:15:556:17 | ControlFlowNode for key | | classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:572:15:572:17 | ControlFlowNode for key | | classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:587:15:587:17 | ControlFlowNode for key | diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected index 867809b437a..8e9ad3cf180 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected @@ -2,4 +2,5 @@ | argumentPassing.py:98:43:98:46 | ControlFlowNode for arg3 | argumentPassing.py:92:11:92:11 | ControlFlowNode for c | | argumentPassing.py:99:41:99:44 | ControlFlowNode for arg3 | argumentPassing.py:92:11:92:11 | ControlFlowNode for c | | argumentPassing.py:113:36:113:39 | ControlFlowNode for arg3 | argumentPassing.py:105:11:105:11 | ControlFlowNode for c | +| argumentPassing.py:137:26:137:29 | ControlFlowNode for arg3 | argumentPassing.py:132:11:132:13 | ControlFlowNode for baz | | classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:571:15:571:19 | ControlFlowNode for value | From 29a162bc9cfe30b2476e93bbe0d6276574039443 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 30 Sep 2020 23:55:02 +0200 Subject: [PATCH 015/166] Python: Proper flow **arg -> **param --- .../dataflow/internal/DataFlowPrivate.qll | 23 ++++++++++++------ .../dataflow/internal/DataFlowPublic.qll | 23 ++++++++++++++++++ .../dataflow/coverage/argumentPassing.py | 2 +- .../coverage/argumentRouting2.expected | 1 - .../dataflow/coverage/dataflow.expected | 24 +++++++++---------- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index f33a844a936..d9c67c6d04e 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -170,6 +170,13 @@ module EssaFlow { nodeTo.(EssaNode).getVar() = p.getVariable() and nodeFrom.(EssaNode).getVar() = p.getAnInput() ) + or + // Overflow keyword argument + exists(CallNode call, CallableValue callable | + call = callable.getACall() and + nodeTo = TKwOverflowNode(call, callable) and + nodeFrom.asCfgNode() = call.getNode().getKwargs().getAFlowNode() + ) } predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) { @@ -300,11 +307,6 @@ module ArgumentPassing { call_unpacks(call, callable, name, n) and result = TKwUnpacked(call, callable, name) ) - or - // Dict argument is passed to the doubly starred parameter (at position -2). - // This is an overaaproximation, not removing unpacked arguments. - n = -2 and - result = TCfgNode(call.getNode().getKwargs().getAFlowNode()) ) } @@ -342,7 +344,8 @@ module ArgumentPassing { f = callable.getScope() and not exists(call.getArg(n)) and // no positional arguement available name = f.getArgName(n) and - not exists(call.getArgByName(name)) and // no keyword argument available + // not exists(call.getArgByName(name)) and // only matches keyword arguments not preceded by ** + not call.getNode().getANamedArg().(Keyword).getArg() = name and // no keyword argument available n >= 0 and n < f.getPositionalParameterCount() + f.getKeywordOnlyParameterCount() and exists(call.getNode().getKwargs()) // dict argument available @@ -912,7 +915,13 @@ predicate kwUnpackReadStep(CfgNode nodeFrom, DictionaryElementContent c, Node no * in `x.f = newValue`. */ cached -predicate clearsContent(Node n, Content c) { none() } +predicate clearsContent(Node n, Content c) { + exists(CallNode call, CallableValue callable, string name | + call_unpacks(call, callable, name, _) and + n = TKwOverflowNode(call, callable) and + c.(DictionaryElementContent).getKey() = name + ) +} //-------- // Fancy context-sensitive guards diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index f6f98ec7e71..27bbbfa9e9b 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -36,6 +36,9 @@ newtype TNode = /** A node representing the overflow keyword arguments to a call. */ TKwOverflowNode(CallNode call, CallableValue callable) { exists(getKeywordOverflowArg(call, callable, _)) + or + exists(call.getNode().getKwargs()) and + callable.getScope().hasKwArg() } or /** * A node representing an unpacked element of a dictionary argument. @@ -252,6 +255,26 @@ class ModuleVariableNode extends Node, TModuleVariableNode { override Location getLocation() { result = mod.getLocation() } } +class PosOverflowNode extends Node, TPosOverflowNode { + CallNode call; + + PosOverflowNode() { this = TPosOverflowNode(call, _) } + + override string toString() { result = "PosOverflowNode for " + call.getNode().toString() } + + override Location getLocation() { result = call.getLocation() } +} + +class KwOverflowNode extends Node, TKwOverflowNode { + CallNode call; + + KwOverflowNode() { this = TKwOverflowNode(call, _) } + + override string toString() { result = "KwOverflowNode for " + call.getNode().toString() } + + override Location getLocation() { result = call.getLocation() } +} + /** * A node that controls whether other nodes are evaluated. */ diff --git a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py index 594163912f9..6f21f40111a 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py +++ b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py @@ -122,7 +122,7 @@ def grab_foo_bar_baz(foo, **kwargs): def grab_bar_baz(bar, **kwargs): SINK2(bar) try: - SINK2(kwargs["bar"]) # FP + SINK2(kwargs["bar"]) except: print("OK") grab_baz(**kwargs) diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected index fa6aa4c96cb..b0b6950f996 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected @@ -3,7 +3,6 @@ | argumentPassing.py:97:29:97:32 | ControlFlowNode for arg2 | argumentPassing.py:91:11:91:11 | ControlFlowNode for b | | argumentPassing.py:112:30:112:33 | ControlFlowNode for arg2 | argumentPassing.py:104:11:104:11 | ControlFlowNode for b | | argumentPassing.py:137:36:137:39 | ControlFlowNode for arg2 | argumentPassing.py:123:11:123:13 | ControlFlowNode for bar | -| argumentPassing.py:137:36:137:39 | ControlFlowNode for arg2 | argumentPassing.py:125:15:125:27 | ControlFlowNode for Subscript | | classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:556:15:556:17 | ControlFlowNode for key | | classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:572:15:572:17 | ControlFlowNode for key | | classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:587:15:587:17 | ControlFlowNode for key | diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index c217e6e21c1..d073ddcf2f0 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -30,10 +30,6 @@ edges | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | | file://:0:0:0:0 | Data flow node | test.py:381:10:381:43 | ControlFlowNode for second() | | file://:0:0:0:0 | Data flow node | test.py:477:10:477:43 | ControlFlowNode for second() | -| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | -| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | -| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | -| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:42:21:42:26 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:55:9:55:14 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:9:87:14 | ControlFlowNode for SOURCE | @@ -164,15 +160,19 @@ edges | test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() | | test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | file://:0:0:0:0 | Data flow node | | test.py:381:36:381:41 | ControlFlowNode for SOURCE | test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:389:33:389:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | -| test.py:397:39:397:44 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Dictionary element at key b] | +| test.py:389:10:389:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | +| test.py:389:33:389:38 | ControlFlowNode for SOURCE | test.py:389:10:389:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | +| test.py:397:10:397:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | +| test.py:397:39:397:44 | ControlFlowNode for SOURCE | test.py:397:10:397:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | | test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() | | test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() | | test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() | | test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | file://:0:0:0:0 | Data flow node | | test.py:477:36:477:41 | ControlFlowNode for SOURCE | test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:482:33:482:38 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Tuple element at index 0] | -| test.py:487:39:487:44 | ControlFlowNode for SOURCE | file://:0:0:0:0 | Data flow node [Dictionary element at key b] | +| test.py:482:10:482:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | +| test.py:482:33:482:38 | ControlFlowNode for SOURCE | test.py:482:10:482:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | +| test.py:487:10:487:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | +| test.py:487:39:487:44 | ControlFlowNode for SOURCE | test.py:487:10:487:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:501:10:501:10 | ControlFlowNode for a | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | test.py:506:10:506:10 | ControlFlowNode for b | nodes @@ -197,10 +197,6 @@ nodes | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | file://:0:0:0:0 | Data flow node | semmle.label | Data flow node | | file://:0:0:0:0 | Data flow node | semmle.label | Data flow node | -| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | semmle.label | Data flow node [Dictionary element at key b] | -| file://:0:0:0:0 | Data flow node [Dictionary element at key b] | semmle.label | Data flow node [Dictionary element at key b] | -| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] | -| file://:0:0:0:0 | Data flow node [Tuple element at index 0] | semmle.label | Data flow node [Tuple element at index 0] | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module test | | test.py:20:1:20:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | | test.py:20:10:20:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | @@ -328,8 +324,10 @@ nodes | test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | | test.py:381:36:381:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | +| test.py:389:10:389:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | | test.py:389:33:389:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:397:10:397:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | +| test.py:397:10:397:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | | test.py:397:39:397:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:442:10:442:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | | test.py:442:12:442:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | @@ -341,8 +339,10 @@ nodes | test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | | test.py:477:36:477:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | +| test.py:482:10:482:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | | test.py:482:33:482:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:487:10:487:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | +| test.py:487:10:487:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | | test.py:487:39:487:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:499:9:499:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:501:10:501:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | From b092df48a55c8a1f7585b4c9a103aa6427b9aff1 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 1 Oct 2020 10:15:19 +0200 Subject: [PATCH 016/166] Python: Location and toString for KwUnpacked --- .../experimental/dataflow/internal/DataFlowPublic.qll | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 27bbbfa9e9b..f04c104cb74 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -275,6 +275,17 @@ class KwOverflowNode extends Node, TKwOverflowNode { override Location getLocation() { result = call.getLocation() } } +class KwUnpacked extends Node, TKwUnpacked { + CallNode call; + string name; + + KwUnpacked() { this = TKwUnpacked(call, _, name) } + + override string toString() { result = "KwUnpacked " + name } + + override Location getLocation() { result = call.getLocation() } +} + /** * A node that controls whether other nodes are evaluated. */ From db23dad6ec9d0c53220eff2874d5fa89758ab358 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 1 Oct 2020 12:33:42 +0200 Subject: [PATCH 017/166] Python: Allow callables to connect to calls freely --- .../dataflow/internal/DataFlowPrivate.qll | 33 ++++++++++--------- .../dataflow/coverage/dataflow.expected | 12 +++---- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index d9c67c6d04e..72cf9eda218 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -270,11 +270,18 @@ module ArgumentPassing { ) } + predicate connects(CallNode call, CallableValue callable) { + exists(DataFlowCall c | + call = c.getNode() and + callable = c.getCallable().getCallableValue() + ) + } + /** * Gets the argument to `call` that is passed to the `n`th parameter of `callable`. */ Node getArg(CallNode call, CallableValue callable, int n) { - call = callable.getACall() and + connects(call, callable) and ( // positional argument result = TCfgNode(call.getArg(n)) @@ -287,20 +294,14 @@ module ArgumentPassing { ) or // a synthezised argument passed to the starred parameter (at position -1) - exists(Function f | - f = callable.getScope() and - f.hasVarArg() and - n = -1 and - result = TPosOverflowNode(call, callable) - ) + callable.getScope().hasVarArg() and + n = -1 and + result = TPosOverflowNode(call, callable) or // a synthezised argument passed to the doubly starred parameter (at position -2) - exists(Function f | - f = callable.getScope() and - f.hasKwArg() and - n = -2 and - result = TKwOverflowNode(call, callable) - ) + callable.getScope().hasKwArg() and + n = -2 and + result = TKwOverflowNode(call, callable) or // argument unpacked from dict exists(string name | @@ -312,7 +313,7 @@ module ArgumentPassing { /** Gets the control flow node that is passed as the `n`th overflow positional argument. */ ControlFlowNode getPositionalOverflowArg(CallNode call, CallableValue callable, int n) { - call = callable.getACall() and + connects(call, callable) and exists(Function f, int posCount, int argNr | f = callable.getScope() and f.hasVarArg() and @@ -325,7 +326,7 @@ module ArgumentPassing { /** Gets the control flow node that is passed as the overflow keyword argument with key `key`. */ ControlFlowNode getKeywordOverflowArg(CallNode call, CallableValue callable, string key) { - call = callable.getACall() and + connects(call, callable) and exists(Function f | f = callable.getScope() and f.hasKwArg() and @@ -339,7 +340,7 @@ module ArgumentPassing { * It will then be passed to the `n`th parameter of `callable`. */ predicate call_unpacks(CallNode call, CallableValue callable, string name, int n) { - call = callable.getACall() and + connects(call, callable) and exists(Function f | f = callable.getScope() and not exists(call.getArg(n)) and // no positional arguement available diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index d073ddcf2f0..ca72388cc20 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -28,8 +28,6 @@ edges | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | -| file://:0:0:0:0 | Data flow node | test.py:381:10:381:43 | ControlFlowNode for second() | -| file://:0:0:0:0 | Data flow node | test.py:477:10:477:43 | ControlFlowNode for second() | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:42:21:42:26 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:55:9:55:14 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:9:87:14 | ControlFlowNode for SOURCE | @@ -158,7 +156,8 @@ edges | test.py:344:16:344:21 | ControlFlowNode for SOURCE | test.py:344:10:344:22 | ControlFlowNode for Dict [Dictionary element at key s] | | test.py:365:28:365:33 | ControlFlowNode for SOURCE | test.py:365:10:365:34 | ControlFlowNode for second() | | test.py:373:30:373:35 | ControlFlowNode for SOURCE | test.py:373:10:373:36 | ControlFlowNode for second() | -| test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | file://:0:0:0:0 | Data flow node | +| test.py:381:10:381:43 | KwUnpacked b | test.py:381:10:381:43 | ControlFlowNode for second() | +| test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:381:10:381:43 | KwUnpacked b | | test.py:381:36:381:41 | ControlFlowNode for SOURCE | test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | | test.py:389:10:389:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | | test.py:389:33:389:38 | ControlFlowNode for SOURCE | test.py:389:10:389:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | @@ -167,7 +166,8 @@ edges | test.py:442:12:442:17 | ControlFlowNode for SOURCE | test.py:442:10:442:18 | ControlFlowNode for f() | | test.py:449:28:449:33 | ControlFlowNode for SOURCE | test.py:449:10:449:34 | ControlFlowNode for second() | | test.py:463:30:463:35 | ControlFlowNode for SOURCE | test.py:463:10:463:36 | ControlFlowNode for second() | -| test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | file://:0:0:0:0 | Data flow node | +| test.py:477:10:477:43 | KwUnpacked b | test.py:477:10:477:43 | ControlFlowNode for second() | +| test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:477:10:477:43 | KwUnpacked b | | test.py:477:36:477:41 | ControlFlowNode for SOURCE | test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | | test.py:482:10:482:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | | test.py:482:33:482:38 | ControlFlowNode for SOURCE | test.py:482:10:482:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | @@ -195,8 +195,6 @@ nodes | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] | | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] | | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| file://:0:0:0:0 | Data flow node | semmle.label | Data flow node | -| file://:0:0:0:0 | Data flow node | semmle.label | Data flow node | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module test | | test.py:20:1:20:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | | test.py:20:10:20:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | @@ -321,6 +319,7 @@ nodes | test.py:373:10:373:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | | test.py:373:30:373:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:381:10:381:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:381:10:381:43 | KwUnpacked b | semmle.label | KwUnpacked b | | test.py:381:30:381:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | | test.py:381:36:381:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:389:10:389:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | @@ -336,6 +335,7 @@ nodes | test.py:463:10:463:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | | test.py:463:30:463:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:477:10:477:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | +| test.py:477:10:477:43 | KwUnpacked b | semmle.label | KwUnpacked b | | test.py:477:30:477:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | | test.py:477:36:477:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:482:10:482:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | From 2187389da108753ff3c69d5cd3d2c1e42c6e2ac4 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 1 Oct 2020 12:48:38 +0200 Subject: [PATCH 018/166] Python: Show constructor keyword arg problem Also make tests runnable --- .../dataflow/fieldflow/dataflow.expected | 81 +++++++++++-------- .../experimental/dataflow/fieldflow/test.py | 32 ++++++-- 2 files changed, 74 insertions(+), 39 deletions(-) diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected index 76e77ef58b4..158ce21d86d 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected @@ -18,19 +18,25 @@ edges | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | | examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | examples.py:42:6:42:12 | ControlFlowNode for Attribute | | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | -| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | -| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | -| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | ControlFlowNode for x | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | -| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | -| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | -| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:46:19:46:24 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:51:9:51:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:62:17:62:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:78:33:78:38 | ControlFlowNode for SOURCE | +| test.py:3:1:3:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | +| test.py:3:10:3:17 | ControlFlowNode for Str | test.py:3:1:3:6 | GSSA Variable SOURCE | +| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:18 | ControlFlowNode for Attribute | +| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:55:17:55:17 | ControlFlowNode for x | test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | +| test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | +| test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:58:10:58:18 | ControlFlowNode for Attribute | +| test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | test.py:63:10:63:16 | ControlFlowNode for Attribute | +| test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | nodes | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] | | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | @@ -49,23 +55,26 @@ nodes | examples.py:42:6:42:12 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] | -| test.py:29:19:29:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] | -| test.py:30:10:30:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:34:9:34:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute [Attribute foo] | -| test.py:38:17:38:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | -| test.py:41:10:41:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | -| test.py:46:10:46:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | -| test.py:56:33:56:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module test | +| test.py:3:1:3:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | +| test.py:3:10:3:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | +| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:46:19:46:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] | +| test.py:47:10:47:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:51:9:51:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute [Attribute foo] | +| test.py:55:17:55:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | +| test.py:58:10:58:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:62:17:62:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | +| test.py:63:10:63:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | +| test.py:78:33:78:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | #select | examples.py:28:6:28:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:28:6:28:14 | ControlFlowNode for Attribute | Flow found | | examples.py:38:6:38:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:38:6:38:14 | ControlFlowNode for Attribute | Flow found | @@ -77,7 +86,11 @@ nodes | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | Flow found | | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | Flow found | | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | Flow found | -| test.py:30:10:30:18 | ControlFlowNode for Attribute | test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:30:10:30:18 | ControlFlowNode for Attribute | Flow found | -| test.py:41:10:41:18 | ControlFlowNode for Attribute | test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:41:10:41:18 | ControlFlowNode for Attribute | Flow found | -| test.py:46:10:46:16 | ControlFlowNode for Attribute | test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:46:10:46:16 | ControlFlowNode for Attribute | Flow found | -| test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | Flow found | +| test.py:47:10:47:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:47:10:47:18 | ControlFlowNode for Attribute | Flow found | +| test.py:47:10:47:18 | ControlFlowNode for Attribute | test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:47:10:47:18 | ControlFlowNode for Attribute | Flow found | +| test.py:58:10:58:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:58:10:58:18 | ControlFlowNode for Attribute | Flow found | +| test.py:58:10:58:18 | ControlFlowNode for Attribute | test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:58:10:58:18 | ControlFlowNode for Attribute | Flow found | +| test.py:63:10:63:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:63:10:63:16 | ControlFlowNode for Attribute | Flow found | +| test.py:63:10:63:16 | ControlFlowNode for Attribute | test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:63:10:63:16 | ControlFlowNode for Attribute | Flow found | +| test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | Flow found | +| test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | Flow found | diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py index a747db14e24..68992851597 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/test.py +++ b/python/ql/test/experimental/dataflow/fieldflow/test.py @@ -1,16 +1,33 @@ -from python.ql.test.experimental.dataflow.testDefinitions import * +# These are defined so that we can evaluate the test code. +NONSOURCE = "not a source" +SOURCE = "source" + + +def is_source(x): + return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j + + +def SINK(x): + if is_source(x): + print("OK") + else: + print("Unexpected flow", x) + + +def SINK_F(x): + if is_source(x): + print("Unexpected flow", x) + else: + print("OK") + # Preamble - - class MyObj(object): - def __init__(self, foo): self.foo = foo class NestedObj(object): - def __init__(self): self.obj = MyObj("OK") @@ -46,6 +63,11 @@ def test_example3(): SINK(obj.foo) +def test_example3_kw(): + obj = MyObj(foo=SOURCE) + SINK(obj.foo) # Flow not found + + def fields_with_local_flow(x): obj = MyObj(x) a = obj.foo From 5326125b70c0860434a5dd2b28a380a9b154bf9c Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 1 Oct 2020 15:28:26 +0200 Subject: [PATCH 019/166] Python: Handle positional construtor arguments --- .../dataflow/internal/DataFlowPrivate.qll | 21 +- .../dataflow/internal/DataFlowPublic.qll | 2 +- .../dataflow/fieldflow/allLocalFlow.expected | 178 +-- .../dataflow/fieldflow/dataflow.expected | 10 + .../dataflow/fieldflow/globalStep.expected | 1105 +++++++++++------ .../dataflow/fieldflow/localFlow.expected | 14 +- .../dataflow/fieldflow/postupdates.expected | 66 +- .../experimental/dataflow/fieldflow/test.py | 2 +- 8 files changed, 899 insertions(+), 499 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 72cf9eda218..8e7223ea5aa 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -279,12 +279,16 @@ module ArgumentPassing { /** * Gets the argument to `call` that is passed to the `n`th parameter of `callable`. + * If it is a positional argument, it must appear at position `argNr`. + * `argNr` will differ from `n` for method- or class calls, where the first parameter + * is `self` and the first positional arguemnt is passed to the second positional parameter. */ - Node getArg(CallNode call, CallableValue callable, int n) { + Node getArg(CallNode call, int argNr, CallableValue callable, int n) { connects(call, callable) and + n - argNr in [0, 1] and // constrain for now to limit the size of the predicate; we only use it to insert one argument (self). ( // positional argument - result = TCfgNode(call.getArg(n)) + result = TCfgNode(call.getArg(argNr)) or // keyword argument exists(Function f, string argName | @@ -305,7 +309,7 @@ module ArgumentPassing { or // argument unpacked from dict exists(string name | - call_unpacks(call, callable, name, n) and + call_unpacks(call, argNr, callable, name, n) and result = TKwUnpacked(call, callable, name) ) ) @@ -339,11 +343,12 @@ module ArgumentPassing { * Holds if `call` unpacks a dictionary argument in order to pass it via `name`. * It will then be passed to the `n`th parameter of `callable`. */ - predicate call_unpacks(CallNode call, CallableValue callable, string name, int n) { + predicate call_unpacks(CallNode call, int argNr, CallableValue callable, string name, int n) { connects(call, callable) and + n - argNr in [0, 1] and exists(Function f | f = callable.getScope() and - not exists(call.getArg(n)) and // no positional arguement available + not exists(call.getArg(argNr)) and // no positional arguement available name = f.getArgName(n) and // not exists(call.getArgByName(name)) and // only matches keyword arguments not preceded by ** not call.getNode().getANamedArg().(Keyword).getArg() = name and // no keyword argument available @@ -478,7 +483,7 @@ class CallNodeCall extends DataFlowCall, TCallNode { override string toString() { result = call.toString() } - override Node getArg(int n) { result = getArg(call, callable.getCallableValue(), n) } + override Node getArg(int n) { result = getArg(call, n, callable.getCallableValue(), n) } override ControlFlowNode getNode() { result = call } @@ -502,7 +507,7 @@ class ClassCall extends DataFlowCall, TClassCall { override string toString() { result = call.toString() } override Node getArg(int n) { - n > 0 and result = getArg(call, this.getCallableValue(), n - 1) + n > 0 and result = getArg(call, n - 1, this.getCallableValue(), n) or n = 0 and result = TSyntheticPreUpdateNode(TCfgNode(call)) } @@ -918,7 +923,7 @@ predicate kwUnpackReadStep(CfgNode nodeFrom, DictionaryElementContent c, Node no cached predicate clearsContent(Node n, Content c) { exists(CallNode call, CallableValue callable, string name | - call_unpacks(call, callable, name, _) and + call_unpacks(call, _, callable, name, _) and n = TKwOverflowNode(call, callable) and c.(DictionaryElementContent).getKey() = name ) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index f04c104cb74..54f77163512 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -46,7 +46,7 @@ newtype TNode = * to parameter `foo` of `callable. */ TKwUnpacked(CallNode call, CallableValue callable, string name) { - call_unpacks(call, callable, name, _) + call_unpacks(call, _, callable, name, _) } /** diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected index 0f33cbc6bda..afd2d9ee803 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected @@ -74,74 +74,110 @@ | examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | | examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | | examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | -| test.py:0:0:0:0 | GSSA Variable SINK | test.py:1:1:1:66 | GSSA Variable SINK | -| test.py:0:0:0:0 | GSSA Variable SINK_F | test.py:1:1:1:66 | GSSA Variable SINK_F | -| test.py:0:0:0:0 | GSSA Variable SOURCE | test.py:1:1:1:66 | GSSA Variable SOURCE | -| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:1:1:1:66 | GSSA Variable __name__ | -| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:1:1:1:66 | GSSA Variable __package__ | -| test.py:0:0:0:0 | GSSA Variable object | test.py:1:1:1:66 | GSSA Variable object | -| test.py:0:0:0:0 | GSSA Variable object | test.py:6:13:6:18 | ControlFlowNode for object | -| test.py:0:0:0:0 | SSA variable $ | test.py:1:1:1:66 | SSA variable $ | -| test.py:0:0:0:0 | SSA variable * | test.py:1:1:1:66 | SSA variable * | -| test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj | -| test.py:6:13:6:18 | ControlFlowNode for object | test.py:12:17:12:22 | ControlFlowNode for object | -| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ | -| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | -| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | -| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | -| test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj | -| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ | -| test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj | -| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | -| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | -| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj | -| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self | -| test.py:21:1:21:19 | ControlFlowNode for FunctionExpr | test.py:21:5:21:10 | GSSA Variable setFoo | -| test.py:21:1:21:19 | GSSA Variable SINK_F | test.py:22:5:22:10 | ControlFlowNode for SINK_F | -| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | -| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | -| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:26:1:26:20 | ControlFlowNode for FunctionExpr | test.py:26:5:26:17 | GSSA Variable test_example1 | -| test.py:26:1:26:20 | GSSA Variable MyObj | test.py:27:13:27:17 | ControlFlowNode for MyObj | -| test.py:26:1:26:20 | GSSA Variable SINK | test.py:30:5:30:8 | ControlFlowNode for SINK | -| test.py:26:1:26:20 | GSSA Variable SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE | -| test.py:26:1:26:20 | GSSA Variable setFoo | test.py:29:5:29:10 | ControlFlowNode for setFoo | -| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:33:1:33:20 | ControlFlowNode for FunctionExpr | test.py:33:5:33:17 | GSSA Variable test_example2 | -| test.py:33:1:33:20 | GSSA Variable NestedObj | test.py:36:9:36:17 | ControlFlowNode for NestedObj | -| test.py:33:1:33:20 | GSSA Variable SINK | test.py:41:5:41:8 | ControlFlowNode for SINK | -| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:34:9:34:14 | ControlFlowNode for SOURCE | -| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x | -| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x | -| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | -| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:44:1:44:20 | ControlFlowNode for FunctionExpr | test.py:44:5:44:17 | GSSA Variable test_example3 | -| test.py:44:1:44:20 | GSSA Variable MyObj | test.py:45:11:45:15 | ControlFlowNode for MyObj | -| test.py:44:1:44:20 | GSSA Variable SINK | test.py:46:5:46:8 | ControlFlowNode for SINK | -| test.py:44:1:44:20 | GSSA Variable SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | -| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow | -| test.py:49:1:49:30 | GSSA Variable MyObj | test.py:50:11:50:15 | ControlFlowNode for MyObj | -| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | -| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | -| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | -| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | -| test.py:55:1:55:18 | ControlFlowNode for FunctionExpr | test.py:55:5:55:15 | GSSA Variable test_fields | -| test.py:55:1:55:18 | GSSA Variable SINK | test.py:56:5:56:8 | ControlFlowNode for SINK | -| test.py:55:1:55:18 | GSSA Variable SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE | -| test.py:55:1:55:18 | GSSA Variable fields_with_local_flow | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow | +| test.py:2:13:2:26 | ControlFlowNode for Str | test.py:2:1:2:9 | GSSA Variable NONSOURCE | +| test.py:3:10:3:17 | ControlFlowNode for Str | test.py:3:1:3:6 | GSSA Variable SOURCE | +| test.py:6:1:6:17 | ControlFlowNode for FunctionExpr | test.py:6:5:6:13 | GSSA Variable is_source | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:12:7:12 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:29:7:29 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:29:7:29 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:47:7:47 | SSA variable x | +| test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:7:58:7:58 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:71:7:71 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:10:1:10:12 | ControlFlowNode for FunctionExpr | test.py:10:5:10:8 | GSSA Variable SINK | +| test.py:10:1:10:12 | GSSA Variable is_source | test.py:11:8:11:16 | ControlFlowNode for is_source | +| test.py:10:10:10:10 | SSA variable x | test.py:11:18:11:18 | ControlFlowNode for x | +| test.py:10:10:10:10 | SSA variable x | test.py:12:9:12:13 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:14:9:14:13 | SSA variable x | +| test.py:11:18:11:18 | ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:12:9:12:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:14:9:14:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:17:1:17:14 | ControlFlowNode for FunctionExpr | test.py:17:5:17:10 | GSSA Variable SINK_F | +| test.py:17:1:17:14 | GSSA Variable is_source | test.py:18:8:18:16 | ControlFlowNode for is_source | +| test.py:17:12:17:12 | SSA variable x | test.py:18:18:18:18 | ControlFlowNode for x | +| test.py:17:12:17:12 | SSA variable x | test.py:19:9:19:13 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:21:9:21:13 | SSA variable x | +| test.py:18:18:18:18 | ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:19:9:19:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:21:9:21:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:25:1:25:20 | ControlFlowNode for ClassExpr | test.py:25:7:25:11 | GSSA Variable MyObj | +| test.py:25:13:25:18 | ControlFlowNode for object | test.py:30:17:30:22 | ControlFlowNode for object | +| test.py:26:5:26:28 | ControlFlowNode for FunctionExpr | test.py:26:9:26:16 | SSA variable __init__ | +| test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:12 | ControlFlowNode for self | +| test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:16 | SSA variable self | +| test.py:26:24:26:26 | SSA variable foo | test.py:27:20:27:22 | ControlFlowNode for foo | +| test.py:30:1:30:24 | ControlFlowNode for ClassExpr | test.py:30:7:30:15 | GSSA Variable NestedObj | +| test.py:31:5:31:23 | ControlFlowNode for FunctionExpr | test.py:31:9:31:16 | SSA variable __init__ | +| test.py:31:5:31:23 | GSSA Variable MyObj | test.py:32:20:32:24 | ControlFlowNode for MyObj | +| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:12 | ControlFlowNode for self | +| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:16 | SSA variable self | +| test.py:34:5:34:21 | ControlFlowNode for FunctionExpr | test.py:34:9:34:14 | SSA variable getObj | +| test.py:34:16:34:19 | SSA variable self | test.py:35:16:35:19 | ControlFlowNode for self | +| test.py:38:1:38:19 | ControlFlowNode for FunctionExpr | test.py:38:5:38:10 | GSSA Variable setFoo | +| test.py:38:1:38:19 | GSSA Variable SINK_F | test.py:39:5:39:10 | ControlFlowNode for SINK_F | +| test.py:38:12:38:14 | SSA variable obj | test.py:39:12:39:14 | ControlFlowNode for obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:11 | SSA variable obj | +| test.py:38:17:38:17 | SSA variable x | test.py:40:15:40:15 | ControlFlowNode for x | +| test.py:39:12:39:14 | ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:43:1:43:20 | ControlFlowNode for FunctionExpr | test.py:43:5:43:17 | GSSA Variable test_example1 | +| test.py:43:1:43:20 | GSSA Variable MyObj | test.py:44:13:44:17 | ControlFlowNode for MyObj | +| test.py:43:1:43:20 | GSSA Variable SINK | test.py:47:5:47:8 | ControlFlowNode for SINK | +| test.py:43:1:43:20 | GSSA Variable SOURCE | test.py:46:19:46:24 | ControlFlowNode for SOURCE | +| test.py:43:1:43:20 | GSSA Variable setFoo | test.py:46:5:46:10 | ControlFlowNode for setFoo | +| test.py:44:5:44:9 | SSA variable myobj | test.py:46:5:46:25 | SSA variable myobj | +| test.py:44:5:44:9 | SSA variable myobj | test.py:46:12:46:16 | ControlFlowNode for myobj | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:44:5:44:9 | SSA variable myobj | +| test.py:46:12:46:16 | ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | +| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | +| test.py:50:1:50:20 | ControlFlowNode for FunctionExpr | test.py:50:5:50:17 | GSSA Variable test_example2 | +| test.py:50:1:50:20 | GSSA Variable NestedObj | test.py:53:9:53:17 | ControlFlowNode for NestedObj | +| test.py:50:1:50:20 | GSSA Variable SINK | test.py:58:5:58:8 | ControlFlowNode for SINK | +| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:9:51:14 | ControlFlowNode for SOURCE | +| test.py:51:5:51:5 | SSA variable x | test.py:55:17:55:17 | ControlFlowNode for x | +| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:51:5:51:5 | SSA variable x | +| test.py:53:5:53:5 | SSA variable a | test.py:55:5:55:5 | ControlFlowNode for a | +| test.py:53:5:53:5 | SSA variable a | test.py:56:5:56:14 | SSA variable a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:5:53:5 | SSA variable a | +| test.py:55:5:55:5 | ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | +| test.py:55:17:55:17 | ControlFlowNode for x | test.py:56:22:56:22 | ControlFlowNode for x | +| test.py:56:5:56:5 | ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:56:5:56:5 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:61:1:61:20 | ControlFlowNode for FunctionExpr | test.py:61:5:61:17 | GSSA Variable test_example3 | +| test.py:61:1:61:20 | GSSA Variable MyObj | test.py:62:11:62:15 | ControlFlowNode for MyObj | +| test.py:61:1:61:20 | GSSA Variable SINK | test.py:63:5:63:8 | ControlFlowNode for SINK | +| test.py:61:1:61:20 | GSSA Variable SOURCE | test.py:62:17:62:22 | ControlFlowNode for SOURCE | +| test.py:62:5:62:7 | SSA variable obj | test.py:63:10:63:12 | ControlFlowNode for obj | +| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:62:5:62:7 | SSA variable obj | +| test.py:66:1:66:23 | ControlFlowNode for FunctionExpr | test.py:66:5:66:20 | GSSA Variable test_example3_kw | +| test.py:66:1:66:23 | GSSA Variable MyObj | test.py:67:11:67:15 | ControlFlowNode for MyObj | +| test.py:66:1:66:23 | GSSA Variable SINK | test.py:68:5:68:8 | ControlFlowNode for SINK | +| test.py:66:1:66:23 | GSSA Variable SOURCE | test.py:67:21:67:26 | ControlFlowNode for SOURCE | +| test.py:67:5:67:7 | SSA variable obj | test.py:68:10:68:12 | ControlFlowNode for obj | +| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:67:5:67:7 | SSA variable obj | +| test.py:71:1:71:30 | ControlFlowNode for FunctionExpr | test.py:71:5:71:26 | GSSA Variable fields_with_local_flow | +| test.py:71:1:71:30 | GSSA Variable MyObj | test.py:72:11:72:15 | ControlFlowNode for MyObj | +| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | +| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | +| test.py:72:5:72:7 | SSA variable obj | test.py:73:9:73:11 | ControlFlowNode for obj | +| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:72:5:72:7 | SSA variable obj | +| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | +| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | +| test.py:77:1:77:18 | ControlFlowNode for FunctionExpr | test.py:77:5:77:15 | GSSA Variable test_fields | +| test.py:77:1:77:18 | GSSA Variable SINK | test.py:78:5:78:8 | ControlFlowNode for SINK | +| test.py:77:1:77:18 | GSSA Variable SOURCE | test.py:78:33:78:38 | ControlFlowNode for SOURCE | +| test.py:77:1:77:18 | GSSA Variable fields_with_local_flow | test.py:78:10:78:31 | ControlFlowNode for fields_with_local_flow | diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected index 158ce21d86d..dc4fba7f10a 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected @@ -21,6 +21,7 @@ edges | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:46:19:46:24 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:51:9:51:14 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:62:17:62:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:67:21:67:26 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:78:33:78:38 | ControlFlowNode for SOURCE | | test.py:3:1:3:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:3:1:3:6 | GSSA Variable SOURCE | @@ -36,6 +37,9 @@ edges | test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | | test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | test.py:63:10:63:16 | ControlFlowNode for Attribute | +| test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:67:21:67:26 | ControlFlowNode for SOURCE | test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | test.py:68:10:68:16 | ControlFlowNode for Attribute | | test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | nodes | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] | @@ -73,6 +77,10 @@ nodes | test.py:62:17:62:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | | test.py:63:10:63:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:67:21:67:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | +| test.py:68:10:68:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | | test.py:78:33:78:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | #select @@ -92,5 +100,7 @@ nodes | test.py:58:10:58:18 | ControlFlowNode for Attribute | test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:58:10:58:18 | ControlFlowNode for Attribute | Flow found | | test.py:63:10:63:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:63:10:63:16 | ControlFlowNode for Attribute | Flow found | | test.py:63:10:63:16 | ControlFlowNode for Attribute | test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:63:10:63:16 | ControlFlowNode for Attribute | Flow found | +| test.py:68:10:68:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:68:10:68:16 | ControlFlowNode for Attribute | Flow found | +| test.py:68:10:68:16 | ControlFlowNode for Attribute | test.py:67:21:67:26 | ControlFlowNode for SOURCE | test.py:68:10:68:16 | ControlFlowNode for Attribute | Flow found | | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | Flow found | | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | Flow found | diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected index ec077fa4b64..3d95a26fd8b 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected @@ -419,388 +419,723 @@ | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:45:28:45:28 | SSA variable x | | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | -| test.py:0:0:0:0 | GSSA Variable SINK | test.py:1:1:1:66 | GSSA Variable SINK | -| test.py:0:0:0:0 | GSSA Variable SINK | test.py:1:1:1:66 | GSSA Variable SINK | -| test.py:0:0:0:0 | GSSA Variable SINK_F | test.py:1:1:1:66 | GSSA Variable SINK_F | -| test.py:0:0:0:0 | GSSA Variable SINK_F | test.py:1:1:1:66 | GSSA Variable SINK_F | -| test.py:0:0:0:0 | GSSA Variable SOURCE | test.py:1:1:1:66 | GSSA Variable SOURCE | -| test.py:0:0:0:0 | GSSA Variable SOURCE | test.py:1:1:1:66 | GSSA Variable SOURCE | -| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:1:1:1:66 | GSSA Variable __name__ | -| test.py:0:0:0:0 | GSSA Variable __name__ | test.py:1:1:1:66 | GSSA Variable __name__ | -| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:1:1:1:66 | GSSA Variable __package__ | -| test.py:0:0:0:0 | GSSA Variable __package__ | test.py:1:1:1:66 | GSSA Variable __package__ | -| test.py:0:0:0:0 | GSSA Variable object | test.py:1:1:1:66 | GSSA Variable object | -| test.py:0:0:0:0 | GSSA Variable object | test.py:1:1:1:66 | GSSA Variable object | -| test.py:0:0:0:0 | GSSA Variable object | test.py:6:13:6:18 | ControlFlowNode for object | -| test.py:0:0:0:0 | GSSA Variable object | test.py:6:13:6:18 | ControlFlowNode for object | -| test.py:0:0:0:0 | GSSA Variable object | test.py:12:17:12:22 | ControlFlowNode for object | -| test.py:0:0:0:0 | GSSA Variable object | test.py:12:17:12:22 | ControlFlowNode for object | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:15:20:15:24 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:15:20:15:24 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:27:13:27:17 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:27:13:27:17 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:45:11:45:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:45:11:45:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:50:11:50:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:50:11:50:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:36:9:36:17 | ControlFlowNode for NestedObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:36:9:36:17 | ControlFlowNode for NestedObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:30:5:30:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:30:5:30:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:41:5:41:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:41:5:41:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:46:5:46:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:46:5:46:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:56:5:56:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:56:5:56:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:22:5:22:10 | ControlFlowNode for SINK_F | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:22:5:22:10 | ControlFlowNode for SINK_F | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:29:19:29:24 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:29:19:29:24 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:34:9:34:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:34:9:34:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:45:17:45:22 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:45:17:45:22 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:56:33:56:38 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:56:33:56:38 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:29:5:29:10 | ControlFlowNode for setFoo | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:29:5:29:10 | ControlFlowNode for setFoo | -| test.py:0:0:0:0 | SSA variable $ | test.py:1:1:1:66 | SSA variable $ | -| test.py:0:0:0:0 | SSA variable $ | test.py:1:1:1:66 | SSA variable $ | -| test.py:0:0:0:0 | SSA variable * | test.py:1:1:1:66 | SSA variable * | -| test.py:0:0:0:0 | SSA variable * | test.py:1:1:1:66 | SSA variable * | -| test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj | -| test.py:6:1:6:20 | ControlFlowNode for ClassExpr | test.py:6:7:6:11 | GSSA Variable MyObj | -| test.py:6:7:6:11 | GSSA Variable MyObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | -| test.py:6:7:6:11 | GSSA Variable MyObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | -| test.py:6:13:6:18 | ControlFlowNode for object | test.py:12:17:12:22 | ControlFlowNode for object | -| test.py:6:13:6:18 | ControlFlowNode for object | test.py:12:17:12:22 | ControlFlowNode for object | -| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ | -| test.py:8:5:8:28 | ControlFlowNode for FunctionExpr | test.py:8:9:8:16 | SSA variable __init__ | -| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | -| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | -| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | -| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:12 | ControlFlowNode for self | -| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | -| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | -| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | -| test.py:8:18:8:21 | SSA variable self | test.py:9:9:9:16 | SSA variable self | -| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | -| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | -| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | -| test.py:8:24:8:26 | SSA variable foo | test.py:9:20:9:22 | ControlFlowNode for foo | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:15:20:15:30 | ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:27:13:27:23 | ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:45:11:45:23 | ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:50:11:50:18 | ControlFlowNode for MyObj() | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:15:20:15:30 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | -| test.py:9:20:9:22 | ControlFlowNode for foo | test.py:9:9:9:12 | [post store] ControlFlowNode for self [Attribute foo] | -| test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj | -| test.py:12:1:12:24 | ControlFlowNode for ClassExpr | test.py:12:7:12:15 | GSSA Variable NestedObj | -| test.py:12:7:12:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | -| test.py:12:7:12:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | -| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ | -| test.py:14:5:14:23 | ControlFlowNode for FunctionExpr | test.py:14:9:14:16 | SSA variable __init__ | -| test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj | -| test.py:14:5:14:23 | GSSA Variable MyObj | test.py:15:20:15:24 | ControlFlowNode for MyObj | -| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | -| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | -| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | -| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:12 | ControlFlowNode for self | -| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | -| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | -| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | -| test.py:14:18:14:21 | SSA variable self | test.py:15:9:15:16 | SSA variable self | -| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | -| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:36:9:36:19 | ControlFlowNode for NestedObj() | -| test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | -| test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | -| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj] | -| test.py:15:20:15:30 | ControlFlowNode for MyObj() [Attribute foo] | test.py:15:9:15:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | -| test.py:15:20:15:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:15:20:15:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | -| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | -| test.py:15:26:15:29 | ControlFlowNode for Str | test.py:15:20:15:30 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj | -| test.py:17:5:17:21 | ControlFlowNode for FunctionExpr | test.py:17:9:17:14 | SSA variable getObj | -| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self | -| test.py:17:16:17:19 | SSA variable self | test.py:18:16:18:19 | ControlFlowNode for self | -| test.py:21:1:21:19 | ControlFlowNode for FunctionExpr | test.py:21:5:21:10 | GSSA Variable setFoo | -| test.py:21:1:21:19 | ControlFlowNode for FunctionExpr | test.py:21:5:21:10 | GSSA Variable setFoo | -| test.py:21:1:21:19 | GSSA Variable SINK_F | test.py:22:5:22:10 | ControlFlowNode for SINK_F | -| test.py:21:1:21:19 | GSSA Variable SINK_F | test.py:22:5:22:10 | ControlFlowNode for SINK_F | -| test.py:21:5:21:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | -| test.py:21:5:21:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | -| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:22:12:22:14 | ControlFlowNode for obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | -| test.py:21:12:21:14 | SSA variable obj | test.py:23:5:23:11 | SSA variable obj | -| test.py:21:12:21:14 | SSA variable obj [Attribute foo] | test.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | -| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | -| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | -| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | -| test.py:21:17:21:17 | SSA variable x | test.py:23:15:23:15 | ControlFlowNode for x | -| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:22:12:22:14 | ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | test.py:22:12:22:18 | ControlFlowNode for Attribute | -| test.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | test.py:22:12:22:18 | ControlFlowNode for Attribute | -| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | -| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | -| test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | -| test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | -| test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | -| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | -| test.py:23:15:23:15 | ControlFlowNode for x | test.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | -| test.py:26:1:26:20 | ControlFlowNode for FunctionExpr | test.py:26:5:26:17 | GSSA Variable test_example1 | -| test.py:26:1:26:20 | ControlFlowNode for FunctionExpr | test.py:26:5:26:17 | GSSA Variable test_example1 | -| test.py:26:1:26:20 | GSSA Variable MyObj | test.py:27:13:27:17 | ControlFlowNode for MyObj | -| test.py:26:1:26:20 | GSSA Variable MyObj | test.py:27:13:27:17 | ControlFlowNode for MyObj | -| test.py:26:1:26:20 | GSSA Variable SINK | test.py:30:5:30:8 | ControlFlowNode for SINK | -| test.py:26:1:26:20 | GSSA Variable SINK | test.py:30:5:30:8 | ControlFlowNode for SINK | -| test.py:26:1:26:20 | GSSA Variable SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE | -| test.py:26:1:26:20 | GSSA Variable SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE | -| test.py:26:1:26:20 | GSSA Variable setFoo | test.py:29:5:29:10 | ControlFlowNode for setFoo | -| test.py:26:1:26:20 | GSSA Variable setFoo | test.py:29:5:29:10 | ControlFlowNode for setFoo | -| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:5:27:9 | SSA variable myobj | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:5:27:9 | SSA variable myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:5:27:9 | SSA variable myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | -| test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:5:27:9 | SSA variable myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:5:29:25 | SSA variable myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:27:5:27:9 | SSA variable myobj [Attribute foo] | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:27:13:27:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:27:13:27:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | -| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:8:24:8:26 | SSA variable foo | -| test.py:27:19:27:22 | ControlFlowNode for Str | test.py:27:13:27:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj | -| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:21:12:21:14 | SSA variable obj | -| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:29:12:29:16 | ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | test.py:21:12:21:14 | SSA variable obj [Attribute foo] | -| test.py:29:12:29:16 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x | -| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:21:17:21:17 | SSA variable x | -| test.py:29:19:29:24 | ControlFlowNode for SOURCE | test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | -| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | -| test.py:30:10:30:14 | ControlFlowNode for myobj [Attribute foo] | test.py:30:10:30:18 | ControlFlowNode for Attribute | -| test.py:33:1:33:20 | ControlFlowNode for FunctionExpr | test.py:33:5:33:17 | GSSA Variable test_example2 | -| test.py:33:1:33:20 | ControlFlowNode for FunctionExpr | test.py:33:5:33:17 | GSSA Variable test_example2 | -| test.py:33:1:33:20 | GSSA Variable NestedObj | test.py:36:9:36:17 | ControlFlowNode for NestedObj | -| test.py:33:1:33:20 | GSSA Variable NestedObj | test.py:36:9:36:17 | ControlFlowNode for NestedObj | -| test.py:33:1:33:20 | GSSA Variable SINK | test.py:41:5:41:8 | ControlFlowNode for SINK | -| test.py:33:1:33:20 | GSSA Variable SINK | test.py:41:5:41:8 | ControlFlowNode for SINK | -| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:34:5:34:5 | SSA variable x | -| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:34:5:34:5 | SSA variable x | -| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:34:9:34:14 | ControlFlowNode for SOURCE | -| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:34:9:34:14 | ControlFlowNode for SOURCE | -| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:38:17:38:17 | ControlFlowNode for x | -| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:38:17:38:17 | ControlFlowNode for x | -| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:39:22:39:22 | ControlFlowNode for x | -| test.py:33:1:33:20 | GSSA Variable SOURCE | test.py:39:22:39:22 | ControlFlowNode for x | -| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x | -| test.py:34:5:34:5 | SSA variable x | test.py:38:17:38:17 | ControlFlowNode for x | -| test.py:34:5:34:5 | SSA variable x | test.py:39:22:39:22 | ControlFlowNode for x | -| test.py:34:5:34:5 | SSA variable x | test.py:39:22:39:22 | ControlFlowNode for x | -| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x | -| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:34:5:34:5 | SSA variable x | -| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | ControlFlowNode for x | -| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | ControlFlowNode for x | -| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:39:22:39:22 | ControlFlowNode for x | -| test.py:34:9:34:14 | ControlFlowNode for SOURCE | test.py:39:22:39:22 | ControlFlowNode for x | -| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:5:36:5 | SSA variable a | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | -| test.py:36:5:36:5 | SSA variable a | test.py:39:5:39:14 | SSA variable a | -| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:5:36:5 | SSA variable a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | -| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | -| test.py:36:5:36:5 | SSA variable a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:5:36:5 | SSA variable a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:39:5:39:14 | SSA variable a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:36:5:36:5 | SSA variable a [Attribute obj, Attribute foo] | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:36:5:36:5 | SSA variable a [Attribute obj] | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | -| test.py:36:9:36:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self | -| test.py:36:9:36:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:14:18:14:21 | SSA variable self | -| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:38:5:38:5 | ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:38:5:38:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:38:5:38:9 | ControlFlowNode for Attribute | -| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:38:5:38:9 | ControlFlowNode for Attribute | -| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | -| test.py:38:5:38:5 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | -| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj] | -| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:38:5:38:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:38:17:38:17 | ControlFlowNode for x | test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | -| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | -| test.py:38:17:38:17 | ControlFlowNode for x | test.py:39:22:39:22 | ControlFlowNode for x | -| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:39:5:39:5 | ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:39:5:39:5 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | -| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | -| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:14 | ControlFlowNode for Attribute | -| test.py:41:10:41:10 | ControlFlowNode for a [Attribute obj] | test.py:41:10:41:14 | ControlFlowNode for Attribute | -| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | -| test.py:41:10:41:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:41:10:41:18 | ControlFlowNode for Attribute | -| test.py:44:1:44:20 | ControlFlowNode for FunctionExpr | test.py:44:5:44:17 | GSSA Variable test_example3 | -| test.py:44:1:44:20 | ControlFlowNode for FunctionExpr | test.py:44:5:44:17 | GSSA Variable test_example3 | -| test.py:44:1:44:20 | GSSA Variable MyObj | test.py:45:11:45:15 | ControlFlowNode for MyObj | -| test.py:44:1:44:20 | GSSA Variable MyObj | test.py:45:11:45:15 | ControlFlowNode for MyObj | -| test.py:44:1:44:20 | GSSA Variable SINK | test.py:46:5:46:8 | ControlFlowNode for SINK | -| test.py:44:1:44:20 | GSSA Variable SINK | test.py:46:5:46:8 | ControlFlowNode for SINK | -| test.py:44:1:44:20 | GSSA Variable SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | -| test.py:44:1:44:20 | GSSA Variable SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | -| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:5:45:7 | SSA variable obj | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:5:45:7 | SSA variable obj [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:5:45:7 | SSA variable obj | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:45:5:45:7 | SSA variable obj [Attribute foo] | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:45:11:45:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:45:11:45:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:8:24:8:26 | SSA variable foo | -| test.py:45:17:45:22 | ControlFlowNode for SOURCE | test.py:45:11:45:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | -| test.py:46:10:46:12 | ControlFlowNode for obj [Attribute foo] | test.py:46:10:46:16 | ControlFlowNode for Attribute | -| test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow | -| test.py:49:1:49:30 | ControlFlowNode for FunctionExpr | test.py:49:5:49:26 | GSSA Variable fields_with_local_flow | -| test.py:49:1:49:30 | GSSA Variable MyObj | test.py:50:11:50:15 | ControlFlowNode for MyObj | -| test.py:49:1:49:30 | GSSA Variable MyObj | test.py:50:11:50:15 | ControlFlowNode for MyObj | -| test.py:49:5:49:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | -| test.py:49:5:49:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | -| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | -| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | -| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | -| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | -| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | -| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | -| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | -| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | -| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:50:5:50:7 | SSA variable obj [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:50:5:50:7 | SSA variable obj [Attribute foo] | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:50:11:50:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:50:11:50:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:8:18:8:21 | SSA variable self | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:8:24:8:26 | SSA variable foo | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:50:17:50:17 | ControlFlowNode for x | test.py:50:11:50:18 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | -| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | -| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | -| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | -| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | -| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | -| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | -| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | -| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | -| test.py:51:9:51:11 | ControlFlowNode for obj [Attribute foo] | test.py:51:9:51:15 | ControlFlowNode for Attribute | -| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | -| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | -| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | -| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | -| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a | -| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a | -| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a | -| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:52:12:52:12 | ControlFlowNode for a | -| test.py:52:12:52:12 | ControlFlowNode for a | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:52:12:52:12 | ControlFlowNode for a | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:55:1:55:18 | ControlFlowNode for FunctionExpr | test.py:55:5:55:15 | GSSA Variable test_fields | -| test.py:55:1:55:18 | ControlFlowNode for FunctionExpr | test.py:55:5:55:15 | GSSA Variable test_fields | -| test.py:55:1:55:18 | GSSA Variable SINK | test.py:56:5:56:8 | ControlFlowNode for SINK | -| test.py:55:1:55:18 | GSSA Variable SINK | test.py:56:5:56:8 | ControlFlowNode for SINK | -| test.py:55:1:55:18 | GSSA Variable SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE | -| test.py:55:1:55:18 | GSSA Variable SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE | -| test.py:55:1:55:18 | GSSA Variable fields_with_local_flow | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow | -| test.py:55:1:55:18 | GSSA Variable fields_with_local_flow | test.py:56:10:56:31 | ControlFlowNode for fields_with_local_flow | -| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x | -| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:49:28:49:28 | SSA variable x | -| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:56:33:56:38 | ControlFlowNode for SOURCE | test.py:56:10:56:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:32:20:32:24 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:32:20:32:24 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:44:13:44:17 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:44:13:44:17 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:62:11:62:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:62:11:62:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:67:11:67:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:67:11:67:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:72:11:72:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:72:11:72:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:53:9:53:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:53:9:53:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:47:5:47:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:47:5:47:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:58:5:58:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:58:5:58:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:63:5:63:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:63:5:63:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:68:5:68:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:68:5:68:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:78:5:78:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:78:5:78:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:39:5:39:10 | ControlFlowNode for SINK_F | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:39:5:39:10 | ControlFlowNode for SINK_F | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:46:19:46:24 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:46:19:46:24 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:51:9:51:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:51:9:51:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:62:17:62:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:62:17:62:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:67:21:67:26 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:67:21:67:26 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:78:33:78:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:78:33:78:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:78:10:78:31 | ControlFlowNode for fields_with_local_flow | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:78:10:78:31 | ControlFlowNode for fields_with_local_flow | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:11:8:11:16 | ControlFlowNode for is_source | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:11:8:11:16 | ControlFlowNode for is_source | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:18:8:18:16 | ControlFlowNode for is_source | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:18:8:18:16 | ControlFlowNode for is_source | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:12:9:12:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:12:9:12:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:14:9:14:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:14:9:14:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:19:9:19:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:19:9:19:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:21:9:21:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:21:9:21:13 | ControlFlowNode for print | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:46:5:46:10 | ControlFlowNode for setFoo | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:46:5:46:10 | ControlFlowNode for setFoo | +| test.py:2:13:2:26 | ControlFlowNode for Str | test.py:2:1:2:9 | GSSA Variable NONSOURCE | +| test.py:2:13:2:26 | ControlFlowNode for Str | test.py:2:1:2:9 | GSSA Variable NONSOURCE | +| test.py:3:1:3:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | +| test.py:3:1:3:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | +| test.py:3:10:3:17 | ControlFlowNode for Str | test.py:3:1:3:6 | GSSA Variable SOURCE | +| test.py:3:10:3:17 | ControlFlowNode for Str | test.py:3:1:3:6 | GSSA Variable SOURCE | +| test.py:6:1:6:17 | ControlFlowNode for FunctionExpr | test.py:6:5:6:13 | GSSA Variable is_source | +| test.py:6:1:6:17 | ControlFlowNode for FunctionExpr | test.py:6:5:6:13 | GSSA Variable is_source | +| test.py:6:5:6:13 | GSSA Variable is_source | test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | +| test.py:6:5:6:13 | GSSA Variable is_source | test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:12:7:12 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:12:7:12 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:12:7:12 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:12:7:12 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:29:7:29 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:29:7:29 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:29:7:29 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:29:7:29 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:29:7:29 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:29:7:29 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:29:7:29 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:29:7:29 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:47:7:47 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:47:7:47 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:47:7:47 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:47:7:47 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:6:15:6:15 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:5:7:78 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:29:7:29 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:29:7:29 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:29:7:29 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:29:7:29 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:12:7:12 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:12:7:78 | ControlFlowNode for BoolExpr | test.py:11:8:11:19 | ControlFlowNode for is_source() | +| test.py:7:12:7:78 | ControlFlowNode for BoolExpr | test.py:11:8:11:19 | ControlFlowNode for is_source() | +| test.py:7:12:7:78 | ControlFlowNode for BoolExpr | test.py:18:8:18:19 | ControlFlowNode for is_source() | +| test.py:7:12:7:78 | ControlFlowNode for BoolExpr | test.py:18:8:18:19 | ControlFlowNode for is_source() | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:47:7:47 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:29:7:29 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:47:7:47 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:47:7:47 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:47:7:47 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:47:7:47 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:29:7:29 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:58:7:58 | ControlFlowNode for x | +| test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:47:7:47 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:58:7:58 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:47:7:47 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:58:7:58 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:58:7:58 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:58:7:58 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:58:7:58 | ControlFlowNode for x | test.py:7:71:7:71 | ControlFlowNode for x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:58:7:58 | SSA variable x | test.py:7:71:7:71 | SSA variable x | +| test.py:7:71:7:71 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:71:7:71 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:71:7:71 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:7:71:7:71 | SSA variable x | test.py:7:5:7:78 | SSA variable x | +| test.py:10:1:10:12 | ControlFlowNode for FunctionExpr | test.py:10:5:10:8 | GSSA Variable SINK | +| test.py:10:1:10:12 | ControlFlowNode for FunctionExpr | test.py:10:5:10:8 | GSSA Variable SINK | +| test.py:10:1:10:12 | GSSA Variable is_source | test.py:11:8:11:16 | ControlFlowNode for is_source | +| test.py:10:1:10:12 | GSSA Variable is_source | test.py:11:8:11:16 | ControlFlowNode for is_source | +| test.py:10:5:10:8 | GSSA Variable SINK | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | +| test.py:10:5:10:8 | GSSA Variable SINK | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | +| test.py:10:10:10:10 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:11:18:11:18 | ControlFlowNode for x | +| test.py:10:10:10:10 | SSA variable x | test.py:11:18:11:18 | ControlFlowNode for x | +| test.py:10:10:10:10 | SSA variable x | test.py:11:18:11:18 | ControlFlowNode for x | +| test.py:10:10:10:10 | SSA variable x | test.py:11:18:11:18 | ControlFlowNode for x | +| test.py:10:10:10:10 | SSA variable x | test.py:12:9:12:13 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:12:9:12:13 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:12:9:12:13 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:12:9:12:13 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:14:9:14:13 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:14:9:14:13 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:14:9:14:13 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:14:9:14:13 | SSA variable x | +| test.py:10:10:10:10 | SSA variable x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:10:10:10:10 | SSA variable x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:10:10:10:10 | SSA variable x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:10:10:10:10 | SSA variable x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:11:18:11:18 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | +| test.py:11:18:11:18 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | +| test.py:11:18:11:18 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | +| test.py:11:18:11:18 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | +| test.py:11:18:11:18 | ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:11:18:11:18 | ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:11:18:11:18 | ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:11:18:11:18 | ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:47:10:47:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:47:10:47:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:58:10:58:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:58:10:58:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:63:10:63:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:63:10:63:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:68:10:68:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:68:10:68:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:78:10:78:39 | [post arg] ControlFlowNode for fields_with_local_flow() | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:78:10:78:39 | [post arg] ControlFlowNode for fields_with_local_flow() | +| test.py:12:9:12:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:12:9:12:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:12:9:12:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:12:9:12:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:14:9:14:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:14:9:14:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:14:9:14:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:14:9:14:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | +| test.py:17:1:17:14 | ControlFlowNode for FunctionExpr | test.py:17:5:17:10 | GSSA Variable SINK_F | +| test.py:17:1:17:14 | ControlFlowNode for FunctionExpr | test.py:17:5:17:10 | GSSA Variable SINK_F | +| test.py:17:1:17:14 | GSSA Variable is_source | test.py:18:8:18:16 | ControlFlowNode for is_source | +| test.py:17:1:17:14 | GSSA Variable is_source | test.py:18:8:18:16 | ControlFlowNode for is_source | +| test.py:17:5:17:10 | GSSA Variable SINK_F | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | +| test.py:17:5:17:10 | GSSA Variable SINK_F | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | +| test.py:17:12:17:12 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:18:18:18:18 | ControlFlowNode for x | +| test.py:17:12:17:12 | SSA variable x | test.py:18:18:18:18 | ControlFlowNode for x | +| test.py:17:12:17:12 | SSA variable x | test.py:18:18:18:18 | ControlFlowNode for x | +| test.py:17:12:17:12 | SSA variable x | test.py:18:18:18:18 | ControlFlowNode for x | +| test.py:17:12:17:12 | SSA variable x | test.py:19:9:19:13 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:19:9:19:13 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:19:9:19:13 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:19:9:19:13 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:17:12:17:12 | SSA variable x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:17:12:17:12 | SSA variable x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:17:12:17:12 | SSA variable x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:17:12:17:12 | SSA variable x | test.py:21:9:21:13 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:21:9:21:13 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:21:9:21:13 | SSA variable x | +| test.py:17:12:17:12 | SSA variable x | test.py:21:9:21:13 | SSA variable x | +| test.py:18:18:18:18 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | +| test.py:18:18:18:18 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | +| test.py:18:18:18:18 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | +| test.py:18:18:18:18 | ControlFlowNode for x | test.py:6:15:6:15 | SSA variable x | +| test.py:18:18:18:18 | ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:18:18:18:18 | ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:18:18:18:18 | ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:18:18:18:18 | ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | +| test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | +| test.py:19:9:19:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:19:9:19:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:19:9:19:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:19:9:19:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:19:34:19:34 | [post arg] ControlFlowNode for x | test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | +| test.py:19:34:19:34 | [post arg] ControlFlowNode for x | test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | +| test.py:21:9:21:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:21:9:21:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:21:9:21:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:21:9:21:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | +| test.py:25:1:25:20 | ControlFlowNode for ClassExpr | test.py:25:7:25:11 | GSSA Variable MyObj | +| test.py:25:1:25:20 | ControlFlowNode for ClassExpr | test.py:25:7:25:11 | GSSA Variable MyObj | +| test.py:25:7:25:11 | GSSA Variable MyObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | +| test.py:25:7:25:11 | GSSA Variable MyObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | +| test.py:25:13:25:18 | ControlFlowNode for object | test.py:30:17:30:22 | ControlFlowNode for object | +| test.py:25:13:25:18 | ControlFlowNode for object | test.py:30:17:30:22 | ControlFlowNode for object | +| test.py:26:5:26:28 | ControlFlowNode for FunctionExpr | test.py:26:9:26:16 | SSA variable __init__ | +| test.py:26:5:26:28 | ControlFlowNode for FunctionExpr | test.py:26:9:26:16 | SSA variable __init__ | +| test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:12 | ControlFlowNode for self | +| test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:12 | ControlFlowNode for self | +| test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:12 | ControlFlowNode for self | +| test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:12 | ControlFlowNode for self | +| test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:16 | SSA variable self | +| test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:16 | SSA variable self | +| test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:16 | SSA variable self | +| test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:16 | SSA variable self | +| test.py:26:24:26:26 | SSA variable foo | test.py:27:20:27:22 | ControlFlowNode for foo | +| test.py:26:24:26:26 | SSA variable foo | test.py:27:20:27:22 | ControlFlowNode for foo | +| test.py:26:24:26:26 | SSA variable foo | test.py:27:20:27:22 | ControlFlowNode for foo | +| test.py:26:24:26:26 | SSA variable foo | test.py:27:20:27:22 | ControlFlowNode for foo | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:32:20:32:30 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:32:20:32:30 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:44:13:44:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:44:13:44:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:62:11:62:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:62:11:62:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:67:11:67:27 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:67:11:67:27 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:72:11:72:18 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:72:11:72:18 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:32:20:32:30 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:44:13:44:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:20:27:22 | ControlFlowNode for foo | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | +| test.py:27:20:27:22 | ControlFlowNode for foo | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | +| test.py:30:1:30:24 | ControlFlowNode for ClassExpr | test.py:30:7:30:15 | GSSA Variable NestedObj | +| test.py:30:1:30:24 | ControlFlowNode for ClassExpr | test.py:30:7:30:15 | GSSA Variable NestedObj | +| test.py:30:7:30:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | +| test.py:30:7:30:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | +| test.py:31:5:31:23 | ControlFlowNode for FunctionExpr | test.py:31:9:31:16 | SSA variable __init__ | +| test.py:31:5:31:23 | ControlFlowNode for FunctionExpr | test.py:31:9:31:16 | SSA variable __init__ | +| test.py:31:5:31:23 | GSSA Variable MyObj | test.py:32:20:32:24 | ControlFlowNode for MyObj | +| test.py:31:5:31:23 | GSSA Variable MyObj | test.py:32:20:32:24 | ControlFlowNode for MyObj | +| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:12 | ControlFlowNode for self | +| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:12 | ControlFlowNode for self | +| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:12 | ControlFlowNode for self | +| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:12 | ControlFlowNode for self | +| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:16 | SSA variable self | +| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:16 | SSA variable self | +| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:16 | SSA variable self | +| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:16 | SSA variable self | +| test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | +| test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | +| test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | +| test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | +| test.py:32:20:32:30 | ControlFlowNode for MyObj() | test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj] | +| test.py:32:20:32:30 | ControlFlowNode for MyObj() [Attribute foo] | test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | +| test.py:32:20:32:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:32:20:32:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:32:26:32:29 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | +| test.py:32:26:32:29 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | +| test.py:32:26:32:29 | ControlFlowNode for Str | test.py:32:20:32:30 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:34:5:34:21 | ControlFlowNode for FunctionExpr | test.py:34:9:34:14 | SSA variable getObj | +| test.py:34:5:34:21 | ControlFlowNode for FunctionExpr | test.py:34:9:34:14 | SSA variable getObj | +| test.py:34:16:34:19 | SSA variable self | test.py:35:16:35:19 | ControlFlowNode for self | +| test.py:34:16:34:19 | SSA variable self | test.py:35:16:35:19 | ControlFlowNode for self | +| test.py:35:16:35:23 | ControlFlowNode for Attribute | test.py:56:5:56:14 | ControlFlowNode for Attribute() | +| test.py:35:16:35:23 | ControlFlowNode for Attribute | test.py:56:5:56:14 | ControlFlowNode for Attribute() | +| test.py:38:1:38:19 | ControlFlowNode for FunctionExpr | test.py:38:5:38:10 | GSSA Variable setFoo | +| test.py:38:1:38:19 | ControlFlowNode for FunctionExpr | test.py:38:5:38:10 | GSSA Variable setFoo | +| test.py:38:1:38:19 | GSSA Variable SINK_F | test.py:39:5:39:10 | ControlFlowNode for SINK_F | +| test.py:38:1:38:19 | GSSA Variable SINK_F | test.py:39:5:39:10 | ControlFlowNode for SINK_F | +| test.py:38:5:38:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | +| test.py:38:5:38:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | +| test.py:38:12:38:14 | SSA variable obj | test.py:39:12:39:14 | ControlFlowNode for obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:39:12:39:14 | ControlFlowNode for obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:39:12:39:14 | ControlFlowNode for obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:39:12:39:14 | ControlFlowNode for obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:11 | SSA variable obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:11 | SSA variable obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:11 | SSA variable obj | +| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:11 | SSA variable obj | +| test.py:38:12:38:14 | SSA variable obj [Attribute foo] | test.py:39:12:39:14 | ControlFlowNode for obj [Attribute foo] | +| test.py:38:17:38:17 | SSA variable x | test.py:40:15:40:15 | ControlFlowNode for x | +| test.py:38:17:38:17 | SSA variable x | test.py:40:15:40:15 | ControlFlowNode for x | +| test.py:38:17:38:17 | SSA variable x | test.py:40:15:40:15 | ControlFlowNode for x | +| test.py:38:17:38:17 | SSA variable x | test.py:40:15:40:15 | ControlFlowNode for x | +| test.py:39:12:39:14 | ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:39:12:39:14 | ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:39:12:39:14 | ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:39:12:39:14 | ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:39:12:39:14 | ControlFlowNode for obj [Attribute foo] | test.py:39:12:39:18 | ControlFlowNode for Attribute | +| test.py:39:12:39:14 | ControlFlowNode for obj [Attribute foo] | test.py:39:12:39:18 | ControlFlowNode for Attribute | +| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | +| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | +| test.py:39:12:39:14 | [post read] ControlFlowNode for obj [Attribute foo] | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:39:12:39:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | +| test.py:39:12:39:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | +| test.py:39:12:39:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | +| test.py:39:12:39:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | +| test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | test.py:39:12:39:14 | [post read] ControlFlowNode for obj [Attribute foo] | +| test.py:40:5:40:7 | [post store] ControlFlowNode for obj | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | +| test.py:40:5:40:7 | [post store] ControlFlowNode for obj | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | +| test.py:40:5:40:7 | [post store] ControlFlowNode for obj [Attribute foo] | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:40:15:40:15 | ControlFlowNode for x | test.py:40:5:40:7 | [post store] ControlFlowNode for obj [Attribute foo] | +| test.py:40:15:40:15 | ControlFlowNode for x | test.py:40:5:40:7 | [post store] ControlFlowNode for obj [Attribute foo] | +| test.py:43:1:43:20 | ControlFlowNode for FunctionExpr | test.py:43:5:43:17 | GSSA Variable test_example1 | +| test.py:43:1:43:20 | ControlFlowNode for FunctionExpr | test.py:43:5:43:17 | GSSA Variable test_example1 | +| test.py:43:1:43:20 | GSSA Variable MyObj | test.py:44:13:44:17 | ControlFlowNode for MyObj | +| test.py:43:1:43:20 | GSSA Variable MyObj | test.py:44:13:44:17 | ControlFlowNode for MyObj | +| test.py:43:1:43:20 | GSSA Variable SINK | test.py:47:5:47:8 | ControlFlowNode for SINK | +| test.py:43:1:43:20 | GSSA Variable SINK | test.py:47:5:47:8 | ControlFlowNode for SINK | +| test.py:43:1:43:20 | GSSA Variable SOURCE | test.py:46:19:46:24 | ControlFlowNode for SOURCE | +| test.py:43:1:43:20 | GSSA Variable SOURCE | test.py:46:19:46:24 | ControlFlowNode for SOURCE | +| test.py:43:1:43:20 | GSSA Variable setFoo | test.py:46:5:46:10 | ControlFlowNode for setFoo | +| test.py:43:1:43:20 | GSSA Variable setFoo | test.py:46:5:46:10 | ControlFlowNode for setFoo | +| test.py:44:5:44:9 | SSA variable myobj | test.py:46:5:46:25 | SSA variable myobj | +| test.py:44:5:44:9 | SSA variable myobj | test.py:46:5:46:25 | SSA variable myobj | +| test.py:44:5:44:9 | SSA variable myobj | test.py:46:12:46:16 | ControlFlowNode for myobj | +| test.py:44:5:44:9 | SSA variable myobj | test.py:46:12:46:16 | ControlFlowNode for myobj | +| test.py:44:5:44:9 | SSA variable myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | +| test.py:44:5:44:9 | SSA variable myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | +| test.py:44:5:44:9 | SSA variable myobj [Attribute foo] | test.py:46:12:46:16 | ControlFlowNode for myobj [Attribute foo] | +| test.py:44:5:44:9 | SSA variable myobj [Attribute foo] | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:44:5:44:9 | SSA variable myobj | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:44:5:44:9 | SSA variable myobj | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:46:5:46:25 | SSA variable myobj | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:46:5:46:25 | SSA variable myobj | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:46:12:46:16 | ControlFlowNode for myobj | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:46:12:46:16 | ControlFlowNode for myobj | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:47:10:47:14 | ControlFlowNode for myobj | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:47:10:47:14 | ControlFlowNode for myobj | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:44:5:44:9 | SSA variable myobj [Attribute foo] | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:46:12:46:16 | ControlFlowNode for myobj [Attribute foo] | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:44:13:44:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:44:13:44:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:44:19:44:22 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | +| test.py:44:19:44:22 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | +| test.py:44:19:44:22 | ControlFlowNode for Str | test.py:44:13:44:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:46:12:46:16 | ControlFlowNode for myobj | test.py:38:12:38:14 | SSA variable obj | +| test.py:46:12:46:16 | ControlFlowNode for myobj | test.py:38:12:38:14 | SSA variable obj | +| test.py:46:12:46:16 | ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | +| test.py:46:12:46:16 | ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | +| test.py:46:12:46:16 | ControlFlowNode for myobj [Attribute foo] | test.py:38:12:38:14 | SSA variable obj [Attribute foo] | +| test.py:46:12:46:16 | ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | +| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | +| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | SSA variable x | +| test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | SSA variable x | +| test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:18 | ControlFlowNode for Attribute | +| test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:18 | ControlFlowNode for Attribute | +| test.py:47:10:47:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:47:10:47:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:50:1:50:20 | ControlFlowNode for FunctionExpr | test.py:50:5:50:17 | GSSA Variable test_example2 | +| test.py:50:1:50:20 | ControlFlowNode for FunctionExpr | test.py:50:5:50:17 | GSSA Variable test_example2 | +| test.py:50:1:50:20 | GSSA Variable NestedObj | test.py:53:9:53:17 | ControlFlowNode for NestedObj | +| test.py:50:1:50:20 | GSSA Variable NestedObj | test.py:53:9:53:17 | ControlFlowNode for NestedObj | +| test.py:50:1:50:20 | GSSA Variable SINK | test.py:58:5:58:8 | ControlFlowNode for SINK | +| test.py:50:1:50:20 | GSSA Variable SINK | test.py:58:5:58:8 | ControlFlowNode for SINK | +| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:5:51:5 | SSA variable x | +| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:5:51:5 | SSA variable x | +| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:9:51:14 | ControlFlowNode for SOURCE | +| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:9:51:14 | ControlFlowNode for SOURCE | +| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | +| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | +| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:56:22:56:22 | ControlFlowNode for x | +| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:56:22:56:22 | ControlFlowNode for x | +| test.py:51:5:51:5 | SSA variable x | test.py:55:17:55:17 | ControlFlowNode for x | +| test.py:51:5:51:5 | SSA variable x | test.py:55:17:55:17 | ControlFlowNode for x | +| test.py:51:5:51:5 | SSA variable x | test.py:56:22:56:22 | ControlFlowNode for x | +| test.py:51:5:51:5 | SSA variable x | test.py:56:22:56:22 | ControlFlowNode for x | +| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:51:5:51:5 | SSA variable x | +| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:51:5:51:5 | SSA variable x | +| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | +| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | +| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:56:22:56:22 | ControlFlowNode for x | +| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:56:22:56:22 | ControlFlowNode for x | +| test.py:53:5:53:5 | SSA variable a | test.py:55:5:55:5 | ControlFlowNode for a | +| test.py:53:5:53:5 | SSA variable a | test.py:55:5:55:5 | ControlFlowNode for a | +| test.py:53:5:53:5 | SSA variable a | test.py:56:5:56:5 | ControlFlowNode for a | +| test.py:53:5:53:5 | SSA variable a | test.py:56:5:56:5 | ControlFlowNode for a | +| test.py:53:5:53:5 | SSA variable a | test.py:56:5:56:14 | SSA variable a | +| test.py:53:5:53:5 | SSA variable a | test.py:56:5:56:14 | SSA variable a | +| test.py:53:5:53:5 | SSA variable a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:53:5:53:5 | SSA variable a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:53:5:53:5 | SSA variable a [Attribute obj] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | +| test.py:53:5:53:5 | SSA variable a [Attribute obj] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj] | +| test.py:53:5:53:5 | SSA variable a [Attribute obj] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:5:53:5 | SSA variable a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:5:53:5 | SSA variable a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:55:5:55:5 | ControlFlowNode for a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:55:5:55:5 | ControlFlowNode for a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:56:5:56:5 | ControlFlowNode for a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:56:5:56:5 | ControlFlowNode for a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:56:5:56:14 | SSA variable a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:56:5:56:14 | SSA variable a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:53:5:53:5 | SSA variable a [Attribute obj] | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj] | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | +| test.py:53:9:53:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:31:18:31:21 | SSA variable self | +| test.py:53:9:53:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:31:18:31:21 | SSA variable self | +| test.py:55:5:55:5 | ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | +| test.py:55:5:55:5 | ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | +| test.py:55:5:55:5 | ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:55:5:55:5 | ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:55:5:55:9 | ControlFlowNode for Attribute | +| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:55:5:55:9 | ControlFlowNode for Attribute | +| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj] | +| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj] | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | +| test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute | test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj] | +| test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:55:17:55:17 | ControlFlowNode for x | test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | +| test.py:55:17:55:17 | ControlFlowNode for x | test.py:56:22:56:22 | ControlFlowNode for x | +| test.py:55:17:55:17 | ControlFlowNode for x | test.py:56:22:56:22 | ControlFlowNode for x | +| test.py:56:5:56:5 | ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:56:5:56:5 | ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | +| test.py:56:5:56:5 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:56:5:56:5 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | +| test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | test.py:58:10:58:14 | ControlFlowNode for Attribute | +| test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | test.py:58:10:58:14 | ControlFlowNode for Attribute | +| test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:58:10:58:18 | ControlFlowNode for Attribute | +| test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:58:10:58:18 | ControlFlowNode for Attribute | +| test.py:58:10:58:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:58:10:58:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:61:1:61:20 | ControlFlowNode for FunctionExpr | test.py:61:5:61:17 | GSSA Variable test_example3 | +| test.py:61:1:61:20 | ControlFlowNode for FunctionExpr | test.py:61:5:61:17 | GSSA Variable test_example3 | +| test.py:61:1:61:20 | GSSA Variable MyObj | test.py:62:11:62:15 | ControlFlowNode for MyObj | +| test.py:61:1:61:20 | GSSA Variable MyObj | test.py:62:11:62:15 | ControlFlowNode for MyObj | +| test.py:61:1:61:20 | GSSA Variable SINK | test.py:63:5:63:8 | ControlFlowNode for SINK | +| test.py:61:1:61:20 | GSSA Variable SINK | test.py:63:5:63:8 | ControlFlowNode for SINK | +| test.py:61:1:61:20 | GSSA Variable SOURCE | test.py:62:17:62:22 | ControlFlowNode for SOURCE | +| test.py:61:1:61:20 | GSSA Variable SOURCE | test.py:62:17:62:22 | ControlFlowNode for SOURCE | +| test.py:62:5:62:7 | SSA variable obj | test.py:63:10:63:12 | ControlFlowNode for obj | +| test.py:62:5:62:7 | SSA variable obj | test.py:63:10:63:12 | ControlFlowNode for obj | +| test.py:62:5:62:7 | SSA variable obj [Attribute foo] | test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:62:5:62:7 | SSA variable obj | +| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:62:5:62:7 | SSA variable obj | +| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:63:10:63:12 | ControlFlowNode for obj | +| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:63:10:63:12 | ControlFlowNode for obj | +| test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:62:5:62:7 | SSA variable obj [Attribute foo] | +| test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:62:11:62:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:62:11:62:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | test.py:63:10:63:16 | ControlFlowNode for Attribute | +| test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | test.py:63:10:63:16 | ControlFlowNode for Attribute | +| test.py:63:10:63:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:63:10:63:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:66:1:66:23 | ControlFlowNode for FunctionExpr | test.py:66:5:66:20 | GSSA Variable test_example3_kw | +| test.py:66:1:66:23 | ControlFlowNode for FunctionExpr | test.py:66:5:66:20 | GSSA Variable test_example3_kw | +| test.py:66:1:66:23 | GSSA Variable MyObj | test.py:67:11:67:15 | ControlFlowNode for MyObj | +| test.py:66:1:66:23 | GSSA Variable MyObj | test.py:67:11:67:15 | ControlFlowNode for MyObj | +| test.py:66:1:66:23 | GSSA Variable SINK | test.py:68:5:68:8 | ControlFlowNode for SINK | +| test.py:66:1:66:23 | GSSA Variable SINK | test.py:68:5:68:8 | ControlFlowNode for SINK | +| test.py:66:1:66:23 | GSSA Variable SOURCE | test.py:67:21:67:26 | ControlFlowNode for SOURCE | +| test.py:66:1:66:23 | GSSA Variable SOURCE | test.py:67:21:67:26 | ControlFlowNode for SOURCE | +| test.py:67:5:67:7 | SSA variable obj | test.py:68:10:68:12 | ControlFlowNode for obj | +| test.py:67:5:67:7 | SSA variable obj | test.py:68:10:68:12 | ControlFlowNode for obj | +| test.py:67:5:67:7 | SSA variable obj [Attribute foo] | test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:67:5:67:7 | SSA variable obj | +| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:67:5:67:7 | SSA variable obj | +| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:68:10:68:12 | ControlFlowNode for obj | +| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:68:10:68:12 | ControlFlowNode for obj | +| test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:67:5:67:7 | SSA variable obj [Attribute foo] | +| test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:67:11:67:27 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:67:11:67:27 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:67:21:67:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:67:21:67:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:67:21:67:26 | ControlFlowNode for SOURCE | test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | test.py:68:10:68:16 | ControlFlowNode for Attribute | +| test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | test.py:68:10:68:16 | ControlFlowNode for Attribute | +| test.py:68:10:68:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:68:10:68:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:71:1:71:30 | ControlFlowNode for FunctionExpr | test.py:71:5:71:26 | GSSA Variable fields_with_local_flow | +| test.py:71:1:71:30 | ControlFlowNode for FunctionExpr | test.py:71:5:71:26 | GSSA Variable fields_with_local_flow | +| test.py:71:1:71:30 | GSSA Variable MyObj | test.py:72:11:72:15 | ControlFlowNode for MyObj | +| test.py:71:1:71:30 | GSSA Variable MyObj | test.py:72:11:72:15 | ControlFlowNode for MyObj | +| test.py:71:5:71:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | +| test.py:71:5:71:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | +| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | +| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | +| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | +| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | +| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | +| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | +| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | +| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | +| test.py:72:5:72:7 | SSA variable obj | test.py:73:9:73:11 | ControlFlowNode for obj | +| test.py:72:5:72:7 | SSA variable obj | test.py:73:9:73:11 | ControlFlowNode for obj | +| test.py:72:5:72:7 | SSA variable obj [Attribute foo] | test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:72:5:72:7 | SSA variable obj [Attribute foo] | test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:72:5:72:7 | SSA variable obj | +| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:72:5:72:7 | SSA variable obj | +| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:73:9:73:11 | ControlFlowNode for obj | +| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:73:9:73:11 | ControlFlowNode for obj | +| test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:72:5:72:7 | SSA variable obj [Attribute foo] | +| test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:72:5:72:7 | SSA variable obj [Attribute foo] | +| test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:72:11:72:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:72:11:72:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:72:17:72:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:72:17:72:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:72:17:72:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:72:17:72:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:72:17:72:17 | ControlFlowNode for x | test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:72:17:72:17 | ControlFlowNode for x | test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:72:17:72:17 | [post arg] ControlFlowNode for x | test.py:78:33:78:38 | [post arg] ControlFlowNode for SOURCE | +| test.py:72:17:72:17 | [post arg] ControlFlowNode for x | test.py:78:33:78:38 | [post arg] ControlFlowNode for SOURCE | +| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | +| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | +| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | +| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | +| test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | test.py:73:9:73:15 | ControlFlowNode for Attribute | +| test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | test.py:73:9:73:15 | ControlFlowNode for Attribute | +| test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | test.py:73:9:73:15 | ControlFlowNode for Attribute | +| test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | test.py:73:9:73:15 | ControlFlowNode for Attribute | +| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | +| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | +| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | +| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | +| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:74:12:74:12 | ControlFlowNode for a | +| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:74:12:74:12 | ControlFlowNode for a | +| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:74:12:74:12 | ControlFlowNode for a | +| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:74:12:74:12 | ControlFlowNode for a | +| test.py:74:12:74:12 | ControlFlowNode for a | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:74:12:74:12 | ControlFlowNode for a | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:77:1:77:18 | ControlFlowNode for FunctionExpr | test.py:77:5:77:15 | GSSA Variable test_fields | +| test.py:77:1:77:18 | ControlFlowNode for FunctionExpr | test.py:77:5:77:15 | GSSA Variable test_fields | +| test.py:77:1:77:18 | GSSA Variable SINK | test.py:78:5:78:8 | ControlFlowNode for SINK | +| test.py:77:1:77:18 | GSSA Variable SINK | test.py:78:5:78:8 | ControlFlowNode for SINK | +| test.py:77:1:77:18 | GSSA Variable SOURCE | test.py:78:33:78:38 | ControlFlowNode for SOURCE | +| test.py:77:1:77:18 | GSSA Variable SOURCE | test.py:78:33:78:38 | ControlFlowNode for SOURCE | +| test.py:77:1:77:18 | GSSA Variable fields_with_local_flow | test.py:78:10:78:31 | ControlFlowNode for fields_with_local_flow | +| test.py:77:1:77:18 | GSSA Variable fields_with_local_flow | test.py:78:10:78:31 | ControlFlowNode for fields_with_local_flow | +| test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | test.py:10:10:10:10 | SSA variable x | +| test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | test.py:10:10:10:10 | SSA variable x | +| test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:71:28:71:28 | SSA variable x | +| test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:71:28:71:28 | SSA variable x | +| test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected index 4be38a469f6..e789b06925b 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected @@ -5,10 +5,10 @@ | examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | | examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | | examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | -| test.py:49:1:49:30 | GSSA Variable MyObj | test.py:50:11:50:15 | ControlFlowNode for MyObj | -| test.py:49:28:49:28 | SSA variable x | test.py:50:11:50:18 | SSA variable x | -| test.py:49:28:49:28 | SSA variable x | test.py:50:17:50:17 | ControlFlowNode for x | -| test.py:50:5:50:7 | SSA variable obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:5:50:7 | SSA variable obj | -| test.py:51:5:51:5 | SSA variable a | test.py:52:12:52:12 | ControlFlowNode for a | -| test.py:51:9:51:15 | ControlFlowNode for Attribute | test.py:51:5:51:5 | SSA variable a | +| test.py:71:1:71:30 | GSSA Variable MyObj | test.py:72:11:72:15 | ControlFlowNode for MyObj | +| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | +| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | +| test.py:72:5:72:7 | SSA variable obj | test.py:73:9:73:11 | ControlFlowNode for obj | +| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:72:5:72:7 | SSA variable obj | +| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | +| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected index 4dcabdd4b86..a096f48f0f2 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected @@ -24,29 +24,43 @@ | examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x | | examples.py:47:7:47:9 | [post read] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj | | examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| test.py:9:9:9:12 | [post store] ControlFlowNode for self | test.py:9:9:9:12 | ControlFlowNode for self | -| test.py:15:9:15:12 | [post store] ControlFlowNode for self | test.py:15:9:15:12 | ControlFlowNode for self | -| test.py:15:20:15:30 | ControlFlowNode for MyObj() | test.py:15:20:15:30 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:15:26:15:29 | [post arg] ControlFlowNode for Str | test.py:15:26:15:29 | ControlFlowNode for Str | -| test.py:18:16:18:19 | [post read] ControlFlowNode for self | test.py:18:16:18:19 | ControlFlowNode for self | -| test.py:22:12:22:14 | [post read] ControlFlowNode for obj | test.py:22:12:22:14 | ControlFlowNode for obj | -| test.py:23:5:23:7 | [post store] ControlFlowNode for obj | test.py:23:5:23:7 | ControlFlowNode for obj | -| test.py:27:13:27:23 | ControlFlowNode for MyObj() | test.py:27:13:27:23 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:27:19:27:22 | [post arg] ControlFlowNode for Str | test.py:27:19:27:22 | ControlFlowNode for Str | -| test.py:29:12:29:16 | [post arg] ControlFlowNode for myobj | test.py:29:12:29:16 | ControlFlowNode for myobj | -| test.py:29:19:29:24 | [post arg] ControlFlowNode for SOURCE | test.py:29:19:29:24 | ControlFlowNode for SOURCE | -| test.py:30:10:30:14 | [post read] ControlFlowNode for myobj | test.py:30:10:30:14 | ControlFlowNode for myobj | -| test.py:36:9:36:19 | ControlFlowNode for NestedObj() | test.py:36:9:36:19 | [pre objCreate] ControlFlowNode for NestedObj() | -| test.py:38:5:38:5 | [post read] ControlFlowNode for a | test.py:38:5:38:5 | ControlFlowNode for a | -| test.py:38:5:38:9 | [post store] ControlFlowNode for Attribute | test.py:38:5:38:9 | ControlFlowNode for Attribute | -| test.py:39:5:39:5 | [post read] ControlFlowNode for a | test.py:39:5:39:5 | ControlFlowNode for a | -| test.py:39:5:39:14 | [post store] ControlFlowNode for Attribute() | test.py:39:5:39:14 | ControlFlowNode for Attribute() | -| test.py:41:10:41:10 | [post read] ControlFlowNode for a | test.py:41:10:41:10 | ControlFlowNode for a | -| test.py:41:10:41:14 | [post read] ControlFlowNode for Attribute | test.py:41:10:41:14 | ControlFlowNode for Attribute | -| test.py:45:11:45:23 | ControlFlowNode for MyObj() | test.py:45:11:45:23 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:45:17:45:22 | [post arg] ControlFlowNode for SOURCE | test.py:45:17:45:22 | ControlFlowNode for SOURCE | -| test.py:46:10:46:12 | [post read] ControlFlowNode for obj | test.py:46:10:46:12 | ControlFlowNode for obj | -| test.py:50:11:50:18 | ControlFlowNode for MyObj() | test.py:50:11:50:18 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:50:17:50:17 | [post arg] ControlFlowNode for x | test.py:50:17:50:17 | ControlFlowNode for x | -| test.py:51:9:51:11 | [post read] ControlFlowNode for obj | test.py:51:9:51:11 | ControlFlowNode for obj | -| test.py:56:33:56:38 | [post arg] ControlFlowNode for SOURCE | test.py:56:33:56:38 | ControlFlowNode for SOURCE | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:11:18:11:18 | ControlFlowNode for x | +| test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:18:18:18:18 | ControlFlowNode for x | +| test.py:19:15:19:31 | [post arg] ControlFlowNode for Str | test.py:19:15:19:31 | ControlFlowNode for Str | +| test.py:19:34:19:34 | [post arg] ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | +| test.py:21:15:21:18 | [post arg] ControlFlowNode for Str | test.py:21:15:21:18 | ControlFlowNode for Str | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:27:9:27:12 | ControlFlowNode for self | +| test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:32:9:32:12 | ControlFlowNode for self | +| test.py:32:20:32:30 | ControlFlowNode for MyObj() | test.py:32:20:32:30 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:32:26:32:29 | [post arg] ControlFlowNode for Str | test.py:32:26:32:29 | ControlFlowNode for Str | +| test.py:35:16:35:19 | [post read] ControlFlowNode for self | test.py:35:16:35:19 | ControlFlowNode for self | +| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:39:12:39:14 | ControlFlowNode for obj | +| test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | test.py:39:12:39:18 | ControlFlowNode for Attribute | +| test.py:40:5:40:7 | [post store] ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | +| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:44:13:44:23 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:44:19:44:22 | [post arg] ControlFlowNode for Str | test.py:44:19:44:22 | ControlFlowNode for Str | +| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | test.py:46:12:46:16 | ControlFlowNode for myobj | +| test.py:46:19:46:24 | [post arg] ControlFlowNode for SOURCE | test.py:46:19:46:24 | ControlFlowNode for SOURCE | +| test.py:47:10:47:14 | [post read] ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | +| test.py:47:10:47:18 | [post arg] ControlFlowNode for Attribute | test.py:47:10:47:18 | ControlFlowNode for Attribute | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:9:53:19 | [pre objCreate] ControlFlowNode for NestedObj() | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:55:5:55:5 | ControlFlowNode for a | +| test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute | test.py:55:5:55:9 | ControlFlowNode for Attribute | +| test.py:56:5:56:5 | [post read] ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | +| test.py:56:5:56:14 | [post store] ControlFlowNode for Attribute() | test.py:56:5:56:14 | ControlFlowNode for Attribute() | +| test.py:58:10:58:10 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:58:10:58:14 | [post read] ControlFlowNode for Attribute | test.py:58:10:58:14 | ControlFlowNode for Attribute | +| test.py:58:10:58:18 | [post arg] ControlFlowNode for Attribute | test.py:58:10:58:18 | ControlFlowNode for Attribute | +| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:62:11:62:23 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:62:17:62:22 | [post arg] ControlFlowNode for SOURCE | test.py:62:17:62:22 | ControlFlowNode for SOURCE | +| test.py:63:10:63:12 | [post read] ControlFlowNode for obj | test.py:63:10:63:12 | ControlFlowNode for obj | +| test.py:63:10:63:16 | [post arg] ControlFlowNode for Attribute | test.py:63:10:63:16 | ControlFlowNode for Attribute | +| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:67:11:67:27 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:67:21:67:26 | [post arg] ControlFlowNode for SOURCE | test.py:67:21:67:26 | ControlFlowNode for SOURCE | +| test.py:68:10:68:12 | [post read] ControlFlowNode for obj | test.py:68:10:68:12 | ControlFlowNode for obj | +| test.py:68:10:68:16 | [post arg] ControlFlowNode for Attribute | test.py:68:10:68:16 | ControlFlowNode for Attribute | +| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:72:11:72:18 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:72:17:72:17 | [post arg] ControlFlowNode for x | test.py:72:17:72:17 | ControlFlowNode for x | +| test.py:73:9:73:11 | [post read] ControlFlowNode for obj | test.py:73:9:73:11 | ControlFlowNode for obj | +| test.py:78:10:78:39 | [post arg] ControlFlowNode for fields_with_local_flow() | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:78:33:78:38 | [post arg] ControlFlowNode for SOURCE | test.py:78:33:78:38 | ControlFlowNode for SOURCE | diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py index 68992851597..aaf8b1575c6 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/test.py +++ b/python/ql/test/experimental/dataflow/fieldflow/test.py @@ -65,7 +65,7 @@ def test_example3(): def test_example3_kw(): obj = MyObj(foo=SOURCE) - SINK(obj.foo) # Flow not found + SINK(obj.foo) def fields_with_local_flow(x): From 0841e92a6b871e6151f5138c117b809d598290eb Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 1 Oct 2020 16:26:12 +0200 Subject: [PATCH 020/166] Python: Test for method call --- .../dataflow/fieldflow/allLocalFlow.expected | 163 +-- .../dataflow/fieldflow/dataflow.expected | 133 +- .../dataflow/fieldflow/examples.py | 23 +- .../dataflow/fieldflow/globalStep.expected | 1100 +++++++++-------- .../dataflow/fieldflow/localFlow.expected | 28 +- .../dataflow/fieldflow/postupdates.expected | 77 +- .../experimental/dataflow/fieldflow/test.py | 11 +- 7 files changed, 833 insertions(+), 702 deletions(-) diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected index afd2d9ee803..840b3a27a7e 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected @@ -10,70 +10,76 @@ | examples.py:0:0:0:0 | GSSA Variable myobj | examples.py:1:1:1:66 | GSSA Variable myobj | | examples.py:0:0:0:0 | GSSA Variable obj | examples.py:1:1:1:66 | GSSA Variable obj | | examples.py:0:0:0:0 | GSSA Variable object | examples.py:1:1:1:66 | GSSA Variable object | -| examples.py:0:0:0:0 | GSSA Variable object | examples.py:5:13:5:18 | ControlFlowNode for object | +| examples.py:0:0:0:0 | GSSA Variable object | examples.py:6:13:6:18 | ControlFlowNode for object | | examples.py:0:0:0:0 | GSSA Variable x | examples.py:1:1:1:66 | GSSA Variable x | | examples.py:0:0:0:0 | SSA variable $ | examples.py:1:1:1:66 | SSA variable $ | | examples.py:0:0:0:0 | SSA variable * | examples.py:1:1:1:66 | SSA variable * | -| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:41:7:41:19 | GSSA Variable SOURCE | -| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:5:7:5:11 | GSSA Variable MyObj | -| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:25:9:25:13 | ControlFlowNode for MyObj | -| examples.py:5:13:5:18 | ControlFlowNode for object | examples.py:11:17:11:22 | ControlFlowNode for object | +| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:49:7:49:19 | GSSA Variable SOURCE | +| examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:6:7:6:11 | GSSA Variable MyObj | +| examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:25:9:25:13 | ControlFlowNode for MyObj | +| examples.py:6:13:6:18 | ControlFlowNode for object | examples.py:11:17:11:22 | ControlFlowNode for object | | examples.py:7:5:7:28 | ControlFlowNode for FunctionExpr | examples.py:7:9:7:16 | SSA variable __init__ | | examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:12 | ControlFlowNode for self | | examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:16 | SSA variable self | | examples.py:7:24:7:26 | SSA variable foo | examples.py:8:20:8:22 | ControlFlowNode for foo | | examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:11:7:11:15 | GSSA Variable NestedObj | | examples.py:11:7:11:15 | GSSA Variable NestedObj | examples.py:33:5:33:13 | ControlFlowNode for NestedObj | -| examples.py:13:5:13:23 | ControlFlowNode for FunctionExpr | examples.py:13:9:13:16 | SSA variable __init__ | -| examples.py:13:5:13:23 | GSSA Variable MyObj | examples.py:14:20:14:24 | ControlFlowNode for MyObj | -| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:12 | ControlFlowNode for self | -| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:16 | SSA variable self | -| examples.py:16:5:16:21 | ControlFlowNode for FunctionExpr | examples.py:16:9:16:14 | SSA variable getObj | -| examples.py:16:16:16:19 | SSA variable self | examples.py:17:16:17:19 | ControlFlowNode for self | -| examples.py:21:1:21:19 | ControlFlowNode for FunctionExpr | examples.py:21:5:21:10 | GSSA Variable setFoo | -| examples.py:21:1:21:19 | GSSA Variable SINK_F | examples.py:22:5:22:10 | ControlFlowNode for SINK_F | -| examples.py:21:5:21:10 | GSSA Variable setFoo | examples.py:27:1:27:6 | ControlFlowNode for setFoo | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:22:12:22:14 | ControlFlowNode for obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:11 | SSA variable obj | -| examples.py:21:17:21:17 | SSA variable x | examples.py:23:15:23:15 | ControlFlowNode for x | -| examples.py:22:12:22:14 | ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:12:5:12:23 | ControlFlowNode for FunctionExpr | examples.py:12:9:12:16 | SSA variable __init__ | +| examples.py:12:5:12:23 | GSSA Variable MyObj | examples.py:13:20:13:24 | ControlFlowNode for MyObj | +| examples.py:12:18:12:21 | SSA variable self | examples.py:13:9:13:12 | ControlFlowNode for self | +| examples.py:12:18:12:21 | SSA variable self | examples.py:13:9:13:16 | SSA variable self | +| examples.py:15:5:15:21 | ControlFlowNode for FunctionExpr | examples.py:15:9:15:14 | SSA variable getObj | +| examples.py:15:16:15:19 | SSA variable self | examples.py:16:16:16:19 | ControlFlowNode for self | +| examples.py:20:1:20:19 | ControlFlowNode for FunctionExpr | examples.py:20:5:20:10 | GSSA Variable setFoo | +| examples.py:20:1:20:19 | GSSA Variable SINK_F | examples.py:21:5:21:10 | ControlFlowNode for SINK_F | +| examples.py:20:5:20:10 | GSSA Variable setFoo | examples.py:27:1:27:6 | ControlFlowNode for setFoo | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:21:12:21:14 | ControlFlowNode for obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:22:5:22:11 | SSA variable obj | +| examples.py:20:17:20:17 | SSA variable x | examples.py:22:15:22:15 | ControlFlowNode for x | +| examples.py:21:12:21:14 | ControlFlowNode for obj | examples.py:22:5:22:7 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | [post read] ControlFlowNode for obj | examples.py:22:5:22:7 | ControlFlowNode for obj | | examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:27:1:27:21 | GSSA Variable myobj | | examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj | -| examples.py:25:9:25:13 | ControlFlowNode for MyObj | examples.py:41:7:41:11 | ControlFlowNode for MyObj | +| examples.py:25:9:25:13 | ControlFlowNode for MyObj | examples.py:49:7:49:11 | ControlFlowNode for MyObj | | examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:25:1:25:5 | GSSA Variable myobj | | examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | | examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | -| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:38:1:38:4 | ControlFlowNode for SINK | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:37:1:37:4 | ControlFlowNode for SINK | | examples.py:31:1:31:1 | GSSA Variable x | examples.py:35:13:35:13 | ControlFlowNode for x | | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | -| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | | examples.py:33:1:33:1 | GSSA Variable a | examples.py:35:1:35:1 | ControlFlowNode for a | -| examples.py:33:1:33:1 | GSSA Variable a | examples.py:36:1:36:10 | GSSA Variable a | +| examples.py:33:5:33:13 | ControlFlowNode for NestedObj | examples.py:42:5:42:13 | ControlFlowNode for NestedObj | | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:33:1:33:1 | GSSA Variable a | -| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:35:13:35:13 | ControlFlowNode for x | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:36:1:36:1 | ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:38:1:38:4 | ControlFlowNode for SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | -| examples.py:41:1:41:3 | GSSA Variable obj | examples.py:42:6:42:8 | ControlFlowNode for obj | -| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:41:1:41:3 | GSSA Variable obj | -| examples.py:41:7:41:19 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:42:1:42:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | -| examples.py:45:1:45:30 | ControlFlowNode for FunctionExpr | examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | -| examples.py:45:1:45:30 | GSSA Variable MyObj | examples.py:46:9:46:13 | ControlFlowNode for MyObj | -| examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | examples.py:50:6:50:27 | ControlFlowNode for fields_with_local_flow | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | -| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | -| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | -| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | +| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:37:6:37:6 | ControlFlowNode for a | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:37:6:37:6 | ControlFlowNode for a | +| examples.py:37:1:37:4 | ControlFlowNode for SINK | examples.py:46:1:46:4 | ControlFlowNode for SINK | +| examples.py:40:1:40:1 | GSSA Variable x | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:40:1:40:1 | GSSA Variable x | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:42:1:42:1 | GSSA Variable a | examples.py:44:1:44:1 | ControlFlowNode for a | +| examples.py:42:1:42:1 | GSSA Variable a | examples.py:44:1:44:10 | GSSA Variable a | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | examples.py:42:1:42:1 | GSSA Variable a | +| examples.py:44:1:44:1 | ControlFlowNode for a | examples.py:46:6:46:6 | ControlFlowNode for a | +| examples.py:44:1:44:1 | [post read] ControlFlowNode for a | examples.py:46:6:46:6 | ControlFlowNode for a | +| examples.py:46:1:46:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:49:1:49:3 | GSSA Variable obj | examples.py:50:6:50:8 | ControlFlowNode for obj | +| examples.py:49:7:49:19 | ControlFlowNode for MyObj() | examples.py:49:1:49:3 | GSSA Variable obj | +| examples.py:49:7:49:19 | GSSA Variable SOURCE | examples.py:59:6:59:35 | GSSA Variable SOURCE | +| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:49:13:49:18 | [post arg] ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:50:1:50:4 | ControlFlowNode for SINK | examples.py:59:1:59:4 | ControlFlowNode for SINK | +| examples.py:53:1:53:30 | ControlFlowNode for FunctionExpr | examples.py:53:5:53:26 | GSSA Variable fields_with_local_flow | +| examples.py:53:1:53:30 | GSSA Variable MyObj | examples.py:54:11:54:15 | ControlFlowNode for MyObj | +| examples.py:53:5:53:26 | GSSA Variable fields_with_local_flow | examples.py:59:6:59:27 | ControlFlowNode for fields_with_local_flow | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:11:54:18 | SSA variable x | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:17:54:17 | ControlFlowNode for x | +| examples.py:54:5:54:7 | SSA variable obj | examples.py:55:9:55:11 | ControlFlowNode for obj | +| examples.py:54:11:54:18 | ControlFlowNode for MyObj() | examples.py:54:5:54:7 | SSA variable obj | +| examples.py:55:5:55:5 | SSA variable a | examples.py:56:12:56:12 | ControlFlowNode for a | +| examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:55:5:55:5 | SSA variable a | | test.py:2:13:2:26 | ControlFlowNode for Str | test.py:2:1:2:9 | GSSA Variable NONSOURCE | | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:3:1:3:6 | GSSA Variable SOURCE | | test.py:6:1:6:17 | ControlFlowNode for FunctionExpr | test.py:6:5:6:13 | GSSA Variable is_source | @@ -145,39 +151,46 @@ | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | | test.py:50:1:50:20 | ControlFlowNode for FunctionExpr | test.py:50:5:50:17 | GSSA Variable test_example2 | | test.py:50:1:50:20 | GSSA Variable NestedObj | test.py:53:9:53:17 | ControlFlowNode for NestedObj | -| test.py:50:1:50:20 | GSSA Variable SINK | test.py:58:5:58:8 | ControlFlowNode for SINK | +| test.py:50:1:50:20 | GSSA Variable SINK | test.py:57:5:57:8 | ControlFlowNode for SINK | | test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:9:51:14 | ControlFlowNode for SOURCE | | test.py:51:5:51:5 | SSA variable x | test.py:55:17:55:17 | ControlFlowNode for x | | test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:51:5:51:5 | SSA variable x | | test.py:53:5:53:5 | SSA variable a | test.py:55:5:55:5 | ControlFlowNode for a | -| test.py:53:5:53:5 | SSA variable a | test.py:56:5:56:14 | SSA variable a | | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:5:53:5 | SSA variable a | -| test.py:55:5:55:5 | ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | -| test.py:55:17:55:17 | ControlFlowNode for x | test.py:56:22:56:22 | ControlFlowNode for x | -| test.py:56:5:56:5 | ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:56:5:56:5 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:61:1:61:20 | ControlFlowNode for FunctionExpr | test.py:61:5:61:17 | GSSA Variable test_example3 | -| test.py:61:1:61:20 | GSSA Variable MyObj | test.py:62:11:62:15 | ControlFlowNode for MyObj | -| test.py:61:1:61:20 | GSSA Variable SINK | test.py:63:5:63:8 | ControlFlowNode for SINK | -| test.py:61:1:61:20 | GSSA Variable SOURCE | test.py:62:17:62:22 | ControlFlowNode for SOURCE | -| test.py:62:5:62:7 | SSA variable obj | test.py:63:10:63:12 | ControlFlowNode for obj | -| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:62:5:62:7 | SSA variable obj | -| test.py:66:1:66:23 | ControlFlowNode for FunctionExpr | test.py:66:5:66:20 | GSSA Variable test_example3_kw | -| test.py:66:1:66:23 | GSSA Variable MyObj | test.py:67:11:67:15 | ControlFlowNode for MyObj | -| test.py:66:1:66:23 | GSSA Variable SINK | test.py:68:5:68:8 | ControlFlowNode for SINK | -| test.py:66:1:66:23 | GSSA Variable SOURCE | test.py:67:21:67:26 | ControlFlowNode for SOURCE | -| test.py:67:5:67:7 | SSA variable obj | test.py:68:10:68:12 | ControlFlowNode for obj | -| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:67:5:67:7 | SSA variable obj | -| test.py:71:1:71:30 | ControlFlowNode for FunctionExpr | test.py:71:5:71:26 | GSSA Variable fields_with_local_flow | -| test.py:71:1:71:30 | GSSA Variable MyObj | test.py:72:11:72:15 | ControlFlowNode for MyObj | -| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | -| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | -| test.py:72:5:72:7 | SSA variable obj | test.py:73:9:73:11 | ControlFlowNode for obj | -| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:72:5:72:7 | SSA variable obj | -| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | -| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | -| test.py:77:1:77:18 | ControlFlowNode for FunctionExpr | test.py:77:5:77:15 | GSSA Variable test_fields | -| test.py:77:1:77:18 | GSSA Variable SINK | test.py:78:5:78:8 | ControlFlowNode for SINK | -| test.py:77:1:77:18 | GSSA Variable SOURCE | test.py:78:33:78:38 | ControlFlowNode for SOURCE | -| test.py:77:1:77:18 | GSSA Variable fields_with_local_flow | test.py:78:10:78:31 | ControlFlowNode for fields_with_local_flow | +| test.py:55:5:55:5 | ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | +| test.py:60:1:60:27 | ControlFlowNode for FunctionExpr | test.py:60:5:60:24 | GSSA Variable test_example2_method | +| test.py:60:1:60:27 | GSSA Variable NestedObj | test.py:63:9:63:17 | ControlFlowNode for NestedObj | +| test.py:60:1:60:27 | GSSA Variable SINK | test.py:67:5:67:8 | ControlFlowNode for SINK | +| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:61:5:61:5 | SSA variable x | test.py:65:22:65:22 | ControlFlowNode for x | +| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:61:5:61:5 | SSA variable x | +| test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:5 | ControlFlowNode for a | +| test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:14 | SSA variable a | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:63:5:63:5 | SSA variable a | +| test.py:65:5:65:5 | ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:65:5:65:5 | [post read] ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:70:1:70:20 | ControlFlowNode for FunctionExpr | test.py:70:5:70:17 | GSSA Variable test_example3 | +| test.py:70:1:70:20 | GSSA Variable MyObj | test.py:71:11:71:15 | ControlFlowNode for MyObj | +| test.py:70:1:70:20 | GSSA Variable SINK | test.py:72:5:72:8 | ControlFlowNode for SINK | +| test.py:70:1:70:20 | GSSA Variable SOURCE | test.py:71:17:71:22 | ControlFlowNode for SOURCE | +| test.py:71:5:71:7 | SSA variable obj | test.py:72:10:72:12 | ControlFlowNode for obj | +| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:71:5:71:7 | SSA variable obj | +| test.py:75:1:75:23 | ControlFlowNode for FunctionExpr | test.py:75:5:75:20 | GSSA Variable test_example3_kw | +| test.py:75:1:75:23 | GSSA Variable MyObj | test.py:76:11:76:15 | ControlFlowNode for MyObj | +| test.py:75:1:75:23 | GSSA Variable SINK | test.py:77:5:77:8 | ControlFlowNode for SINK | +| test.py:75:1:75:23 | GSSA Variable SOURCE | test.py:76:21:76:26 | ControlFlowNode for SOURCE | +| test.py:76:5:76:7 | SSA variable obj | test.py:77:10:77:12 | ControlFlowNode for obj | +| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:76:5:76:7 | SSA variable obj | +| test.py:80:1:80:30 | ControlFlowNode for FunctionExpr | test.py:80:5:80:26 | GSSA Variable fields_with_local_flow | +| test.py:80:1:80:30 | GSSA Variable MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | +| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | +| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | +| test.py:81:5:81:7 | SSA variable obj | test.py:82:9:82:11 | ControlFlowNode for obj | +| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:81:5:81:7 | SSA variable obj | +| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | +| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | +| test.py:86:1:86:18 | ControlFlowNode for FunctionExpr | test.py:86:5:86:15 | GSSA Variable test_fields | +| test.py:86:1:86:18 | GSSA Variable SINK | test.py:87:5:87:8 | ControlFlowNode for SINK | +| test.py:86:1:86:18 | GSSA Variable SOURCE | test.py:87:33:87:38 | ControlFlowNode for SOURCE | +| test.py:86:1:86:18 | GSSA Variable fields_with_local_flow | test.py:87:10:87:31 | ControlFlowNode for fields_with_local_flow | diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected index dc4fba7f10a..0a360b9b4b9 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected @@ -2,45 +2,47 @@ edges | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:14 | ControlFlowNode for Attribute | | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | -| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | | examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute [Attribute foo] | examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | | examples.py:35:13:35:13 | ControlFlowNode for x | examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute [Attribute foo] | -| examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:38:6:38:10 | ControlFlowNode for Attribute [Attribute foo] | -| examples.py:38:6:38:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:38:6:38:14 | ControlFlowNode for Attribute | -| examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | examples.py:42:6:42:12 | ControlFlowNode for Attribute | -| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | +| examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:37:6:37:10 | ControlFlowNode for Attribute [Attribute foo] | +| examples.py:37:6:37:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:37:6:37:14 | ControlFlowNode for Attribute | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:49:7:49:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | +| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:49:7:49:19 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | examples.py:50:6:50:12 | ControlFlowNode for Attribute | +| examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:46:19:46:24 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:51:9:51:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:62:17:62:22 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:67:21:67:26 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:78:33:78:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:17:71:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:76:21:76:26 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:33:87:38 | ControlFlowNode for SOURCE | | test.py:3:1:3:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:3:1:3:6 | GSSA Variable SOURCE | | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | | test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:18 | ControlFlowNode for Attribute | | test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:55:17:55:17 | ControlFlowNode for x | test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | -| test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | -| test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:58:10:58:18 | ControlFlowNode for Attribute | -| test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | test.py:63:10:63:16 | ControlFlowNode for Attribute | -| test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:67:21:67:26 | ControlFlowNode for SOURCE | test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | test.py:68:10:68:16 | ControlFlowNode for Attribute | -| test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | +| test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute | +| test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:71:17:71:22 | ControlFlowNode for SOURCE | test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | test.py:72:10:72:16 | ControlFlowNode for Attribute | +| test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:76:21:76:26 | ControlFlowNode for SOURCE | test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | test.py:77:10:77:16 | ControlFlowNode for Attribute | +| test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | nodes | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] | | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | @@ -50,15 +52,16 @@ nodes | examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | | examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute [Attribute foo] | | examples.py:35:13:35:13 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | -| examples.py:38:6:38:10 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | -| examples.py:38:6:38:14 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | -| examples.py:42:6:42:12 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | -| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:37:6:37:10 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | +| examples.py:37:6:37:14 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| examples.py:49:7:49:19 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | +| examples.py:50:6:50:12 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | +| examples.py:59:29:59:34 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module test | | test.py:3:1:3:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | | test.py:3:10:3:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | @@ -70,37 +73,39 @@ nodes | test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute [Attribute foo] | | test.py:55:17:55:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | -| test.py:58:10:58:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:62:17:62:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | -| test.py:63:10:63:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:67:21:67:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | -| test.py:68:10:68:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | -| test.py:78:33:78:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | +| test.py:57:10:57:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:71:17:71:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | +| test.py:72:10:72:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:76:21:76:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | +| test.py:77:10:77:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | +| test.py:87:33:87:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | #select | examples.py:28:6:28:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:28:6:28:14 | ControlFlowNode for Attribute | Flow found | -| examples.py:38:6:38:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:38:6:38:14 | ControlFlowNode for Attribute | Flow found | -| examples.py:38:6:38:14 | ControlFlowNode for Attribute | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:38:6:38:14 | ControlFlowNode for Attribute | Flow found | -| examples.py:42:6:42:12 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:42:6:42:12 | ControlFlowNode for Attribute | Flow found | -| examples.py:42:6:42:12 | ControlFlowNode for Attribute | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:42:6:42:12 | ControlFlowNode for Attribute | Flow found | -| examples.py:42:6:42:12 | ControlFlowNode for Attribute | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:42:6:42:12 | ControlFlowNode for Attribute | Flow found | -| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | Flow found | -| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | Flow found | -| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | Flow found | -| examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | Flow found | +| examples.py:37:6:37:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:37:6:37:14 | ControlFlowNode for Attribute | Flow found | +| examples.py:37:6:37:14 | ControlFlowNode for Attribute | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:37:6:37:14 | ControlFlowNode for Attribute | Flow found | +| examples.py:50:6:50:12 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:50:6:50:12 | ControlFlowNode for Attribute | Flow found | +| examples.py:50:6:50:12 | ControlFlowNode for Attribute | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:50:6:50:12 | ControlFlowNode for Attribute | Flow found | +| examples.py:50:6:50:12 | ControlFlowNode for Attribute | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:50:6:50:12 | ControlFlowNode for Attribute | Flow found | +| examples.py:50:6:50:12 | ControlFlowNode for Attribute | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:50:6:50:12 | ControlFlowNode for Attribute | Flow found | +| examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | Flow found | +| examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | Flow found | +| examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | Flow found | +| examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | Flow found | +| examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | Flow found | | test.py:47:10:47:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:47:10:47:18 | ControlFlowNode for Attribute | Flow found | | test.py:47:10:47:18 | ControlFlowNode for Attribute | test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:47:10:47:18 | ControlFlowNode for Attribute | Flow found | -| test.py:58:10:58:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:58:10:58:18 | ControlFlowNode for Attribute | Flow found | -| test.py:58:10:58:18 | ControlFlowNode for Attribute | test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:58:10:58:18 | ControlFlowNode for Attribute | Flow found | -| test.py:63:10:63:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:63:10:63:16 | ControlFlowNode for Attribute | Flow found | -| test.py:63:10:63:16 | ControlFlowNode for Attribute | test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:63:10:63:16 | ControlFlowNode for Attribute | Flow found | -| test.py:68:10:68:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:68:10:68:16 | ControlFlowNode for Attribute | Flow found | -| test.py:68:10:68:16 | ControlFlowNode for Attribute | test.py:67:21:67:26 | ControlFlowNode for SOURCE | test.py:68:10:68:16 | ControlFlowNode for Attribute | Flow found | -| test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | Flow found | -| test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | Flow found | +| test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:57:10:57:18 | ControlFlowNode for Attribute | Flow found | +| test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:57:10:57:18 | ControlFlowNode for Attribute | Flow found | +| test.py:72:10:72:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:72:10:72:16 | ControlFlowNode for Attribute | Flow found | +| test.py:72:10:72:16 | ControlFlowNode for Attribute | test.py:71:17:71:22 | ControlFlowNode for SOURCE | test.py:72:10:72:16 | ControlFlowNode for Attribute | Flow found | +| test.py:77:10:77:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:77:10:77:16 | ControlFlowNode for Attribute | Flow found | +| test.py:77:10:77:16 | ControlFlowNode for Attribute | test.py:76:21:76:26 | ControlFlowNode for SOURCE | test.py:77:10:77:16 | ControlFlowNode for Attribute | Flow found | +| test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | Flow found | +| test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | Flow found | diff --git a/python/ql/test/experimental/dataflow/fieldflow/examples.py b/python/ql/test/experimental/dataflow/fieldflow/examples.py index ed961438822..68d50ecbc9e 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/examples.py +++ b/python/ql/test/experimental/dataflow/fieldflow/examples.py @@ -2,14 +2,13 @@ from python.ql.test.experimental.dataflow.testDefinitions import * # Preamble -class MyObj(object): +class MyObj(object): def __init__(self, foo): self.foo = foo class NestedObj(object): - def __init__(self): self.obj = MyObj("OK") @@ -22,6 +21,7 @@ def setFoo(obj, x): SINK_F(obj.foo) obj.foo = x + myobj = MyObj("OK") setFoo(myobj, SOURCE) @@ -33,18 +33,27 @@ x = SOURCE a = NestedObj() a.obj.foo = x -a.getObj().foo = x SINK(a.obj.foo) +# Example 2 with method call +x = SOURCE + +a = NestedObj() + +a.getObj().foo = x + +SINK(a.obj.foo) # Flow missing + # Example 3 obj = MyObj(SOURCE) SINK(obj.foo) # Local flow def fields_with_local_flow(x): - obj = MyObj(x) - a = obj.foo - return a + obj = MyObj(x) + a = obj.foo + return a -SINK(fields_with_local_flow(SOURCE)) \ No newline at end of file + +SINK(fields_with_local_flow(SOURCE)) diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected index 3d95a26fd8b..c12e0c08b1d 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected @@ -2,12 +2,14 @@ | examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:1:1:1:66 | GSSA Variable SINK | | examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:28:1:28:4 | ControlFlowNode for SINK | | examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:28:1:28:4 | ControlFlowNode for SINK | -| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:38:1:38:4 | ControlFlowNode for SINK | -| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:38:1:38:4 | ControlFlowNode for SINK | -| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | -| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:37:1:37:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:37:1:37:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:46:1:46:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:46:1:46:4 | ControlFlowNode for SINK | | examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | | examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:59:1:59:4 | ControlFlowNode for SINK | +| examples.py:0:0:0:0 | GSSA Variable SINK | examples.py:59:1:59:4 | ControlFlowNode for SINK | | examples.py:0:0:0:0 | GSSA Variable SINK_F | examples.py:1:1:1:66 | GSSA Variable SINK_F | | examples.py:0:0:0:0 | GSSA Variable SINK_F | examples.py:1:1:1:66 | GSSA Variable SINK_F | | examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:1:1:1:66 | GSSA Variable SOURCE | @@ -20,16 +22,20 @@ | examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | | examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | | examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | -| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:41:7:41:19 | GSSA Variable SOURCE | -| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:41:7:41:19 | GSSA Variable SOURCE | -| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | -| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | -| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:40:1:40:1 | GSSA Variable x | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:40:1:40:1 | GSSA Variable x | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:49:7:49:19 | GSSA Variable SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:49:7:49:19 | GSSA Variable SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:59:6:59:35 | GSSA Variable SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:59:6:59:35 | GSSA Variable SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:0:0:0:0 | GSSA Variable SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | | examples.py:0:0:0:0 | GSSA Variable __name__ | examples.py:1:1:1:66 | GSSA Variable __name__ | | examples.py:0:0:0:0 | GSSA Variable __name__ | examples.py:1:1:1:66 | GSSA Variable __name__ | | examples.py:0:0:0:0 | GSSA Variable __package__ | examples.py:1:1:1:66 | GSSA Variable __package__ | @@ -44,40 +50,40 @@ | examples.py:0:0:0:0 | GSSA Variable obj | examples.py:1:1:1:66 | GSSA Variable obj | | examples.py:0:0:0:0 | GSSA Variable object | examples.py:1:1:1:66 | GSSA Variable object | | examples.py:0:0:0:0 | GSSA Variable object | examples.py:1:1:1:66 | GSSA Variable object | -| examples.py:0:0:0:0 | GSSA Variable object | examples.py:5:13:5:18 | ControlFlowNode for object | -| examples.py:0:0:0:0 | GSSA Variable object | examples.py:5:13:5:18 | ControlFlowNode for object | +| examples.py:0:0:0:0 | GSSA Variable object | examples.py:6:13:6:18 | ControlFlowNode for object | +| examples.py:0:0:0:0 | GSSA Variable object | examples.py:6:13:6:18 | ControlFlowNode for object | | examples.py:0:0:0:0 | GSSA Variable object | examples.py:11:17:11:22 | ControlFlowNode for object | | examples.py:0:0:0:0 | GSSA Variable object | examples.py:11:17:11:22 | ControlFlowNode for object | | examples.py:0:0:0:0 | GSSA Variable x | examples.py:1:1:1:66 | GSSA Variable x | | examples.py:0:0:0:0 | GSSA Variable x | examples.py:1:1:1:66 | GSSA Variable x | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:14:20:14:24 | ControlFlowNode for MyObj | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:14:20:14:24 | ControlFlowNode for MyObj | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:46:9:46:13 | ControlFlowNode for MyObj | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:46:9:46:13 | ControlFlowNode for MyObj | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module examples | examples.py:22:5:22:10 | ControlFlowNode for SINK_F | -| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module examples | examples.py:22:5:22:10 | ControlFlowNode for SINK_F | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:13:20:13:24 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:13:20:13:24 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:54:11:54:15 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | examples.py:54:11:54:15 | ControlFlowNode for MyObj | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module examples | examples.py:21:5:21:10 | ControlFlowNode for SINK_F | +| examples.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module examples | examples.py:21:5:21:10 | ControlFlowNode for SINK_F | | examples.py:0:0:0:0 | SSA variable $ | examples.py:1:1:1:66 | SSA variable $ | | examples.py:0:0:0:0 | SSA variable $ | examples.py:1:1:1:66 | SSA variable $ | | examples.py:0:0:0:0 | SSA variable * | examples.py:1:1:1:66 | SSA variable * | | examples.py:0:0:0:0 | SSA variable * | examples.py:1:1:1:66 | SSA variable * | -| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:41:7:41:19 | GSSA Variable SOURCE | -| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:41:7:41:19 | GSSA Variable SOURCE | -| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | -| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | -| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:5:7:5:11 | GSSA Variable MyObj | -| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:5:7:5:11 | GSSA Variable MyObj | -| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:25:9:25:13 | ControlFlowNode for MyObj | -| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:25:9:25:13 | ControlFlowNode for MyObj | -| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:41:7:41:11 | ControlFlowNode for MyObj | -| examples.py:5:1:5:20 | ControlFlowNode for ClassExpr | examples.py:41:7:41:11 | ControlFlowNode for MyObj | -| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | -| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | -| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:25:9:25:13 | ControlFlowNode for MyObj | -| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:25:9:25:13 | ControlFlowNode for MyObj | -| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:41:7:41:11 | ControlFlowNode for MyObj | -| examples.py:5:7:5:11 | GSSA Variable MyObj | examples.py:41:7:41:11 | ControlFlowNode for MyObj | -| examples.py:5:13:5:18 | ControlFlowNode for object | examples.py:11:17:11:22 | ControlFlowNode for object | -| examples.py:5:13:5:18 | ControlFlowNode for object | examples.py:11:17:11:22 | ControlFlowNode for object | +| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:49:7:49:19 | GSSA Variable SOURCE | +| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:49:7:49:19 | GSSA Variable SOURCE | +| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:59:6:59:35 | GSSA Variable SOURCE | +| examples.py:1:1:1:66 | GSSA Variable SOURCE | examples.py:59:6:59:35 | GSSA Variable SOURCE | +| examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:6:7:6:11 | GSSA Variable MyObj | +| examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:6:7:6:11 | GSSA Variable MyObj | +| examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:25:9:25:13 | ControlFlowNode for MyObj | +| examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:25:9:25:13 | ControlFlowNode for MyObj | +| examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:49:7:49:11 | ControlFlowNode for MyObj | +| examples.py:6:1:6:20 | ControlFlowNode for ClassExpr | examples.py:49:7:49:11 | ControlFlowNode for MyObj | +| examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | +| examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module examples | +| examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:25:9:25:13 | ControlFlowNode for MyObj | +| examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:25:9:25:13 | ControlFlowNode for MyObj | +| examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:49:7:49:11 | ControlFlowNode for MyObj | +| examples.py:6:7:6:11 | GSSA Variable MyObj | examples.py:49:7:49:11 | ControlFlowNode for MyObj | +| examples.py:6:13:6:18 | ControlFlowNode for object | examples.py:11:17:11:22 | ControlFlowNode for object | +| examples.py:6:13:6:18 | ControlFlowNode for object | examples.py:11:17:11:22 | ControlFlowNode for object | | examples.py:7:5:7:28 | ControlFlowNode for FunctionExpr | examples.py:7:9:7:16 | SSA variable __init__ | | examples.py:7:5:7:28 | ControlFlowNode for FunctionExpr | examples.py:7:9:7:16 | SSA variable __init__ | | examples.py:7:18:7:21 | SSA variable self | examples.py:8:9:8:12 | ControlFlowNode for self | @@ -92,93 +98,101 @@ | examples.py:7:24:7:26 | SSA variable foo | examples.py:8:20:8:22 | ControlFlowNode for foo | | examples.py:7:24:7:26 | SSA variable foo | examples.py:8:20:8:22 | ControlFlowNode for foo | | examples.py:7:24:7:26 | SSA variable foo | examples.py:8:20:8:22 | ControlFlowNode for foo | -| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:14:20:14:30 | ControlFlowNode for MyObj() | -| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:14:20:14:30 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:13:20:13:30 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:13:20:13:30 | ControlFlowNode for MyObj() | | examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:25:9:25:19 | ControlFlowNode for MyObj() | | examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:25:9:25:19 | ControlFlowNode for MyObj() | -| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:41:7:41:19 | ControlFlowNode for MyObj() | -| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:41:7:41:19 | ControlFlowNode for MyObj() | -| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:46:9:46:16 | ControlFlowNode for MyObj() | -| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:46:9:46:16 | ControlFlowNode for MyObj() | -| examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:14:20:14:30 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:49:7:49:19 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:49:7:49:19 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:54:11:54:18 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:54:11:54:18 | ControlFlowNode for MyObj() | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:13:20:13:30 | ControlFlowNode for MyObj() [Attribute foo] | | examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:25:9:25:19 | ControlFlowNode for MyObj() [Attribute foo] | -| examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | -| examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:49:7:49:19 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | examples.py:54:11:54:18 | ControlFlowNode for MyObj() [Attribute foo] | | examples.py:8:20:8:22 | ControlFlowNode for foo | examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | | examples.py:8:20:8:22 | ControlFlowNode for foo | examples.py:8:9:8:12 | [post store] ControlFlowNode for self [Attribute foo] | | examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:11:7:11:15 | GSSA Variable NestedObj | | examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:11:7:11:15 | GSSA Variable NestedObj | | examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:33:5:33:13 | ControlFlowNode for NestedObj | | examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:33:5:33:13 | ControlFlowNode for NestedObj | +| examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:42:5:42:13 | ControlFlowNode for NestedObj | +| examples.py:11:1:11:24 | ControlFlowNode for ClassExpr | examples.py:42:5:42:13 | ControlFlowNode for NestedObj | | examples.py:11:7:11:15 | GSSA Variable NestedObj | examples.py:33:5:33:13 | ControlFlowNode for NestedObj | | examples.py:11:7:11:15 | GSSA Variable NestedObj | examples.py:33:5:33:13 | ControlFlowNode for NestedObj | -| examples.py:13:5:13:23 | ControlFlowNode for FunctionExpr | examples.py:13:9:13:16 | SSA variable __init__ | -| examples.py:13:5:13:23 | ControlFlowNode for FunctionExpr | examples.py:13:9:13:16 | SSA variable __init__ | -| examples.py:13:5:13:23 | GSSA Variable MyObj | examples.py:14:20:14:24 | ControlFlowNode for MyObj | -| examples.py:13:5:13:23 | GSSA Variable MyObj | examples.py:14:20:14:24 | ControlFlowNode for MyObj | -| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:12 | ControlFlowNode for self | -| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:12 | ControlFlowNode for self | -| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:12 | ControlFlowNode for self | -| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:12 | ControlFlowNode for self | -| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:16 | SSA variable self | -| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:16 | SSA variable self | -| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:16 | SSA variable self | -| examples.py:13:18:13:21 | SSA variable self | examples.py:14:9:14:16 | SSA variable self | -| examples.py:14:9:14:12 | [post store] ControlFlowNode for self | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | -| examples.py:14:9:14:12 | [post store] ControlFlowNode for self | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | -| examples.py:14:9:14:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | -| examples.py:14:9:14:12 | [post store] ControlFlowNode for self [Attribute obj] | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | -| examples.py:14:20:14:30 | ControlFlowNode for MyObj() | examples.py:14:9:14:12 | [post store] ControlFlowNode for self [Attribute obj] | -| examples.py:14:20:14:30 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:14:9:14:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | -| examples.py:14:20:14:30 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | -| examples.py:14:20:14:30 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | -| examples.py:14:26:14:29 | ControlFlowNode for Str | examples.py:7:24:7:26 | SSA variable foo | -| examples.py:14:26:14:29 | ControlFlowNode for Str | examples.py:7:24:7:26 | SSA variable foo | -| examples.py:14:26:14:29 | ControlFlowNode for Str | examples.py:14:20:14:30 | ControlFlowNode for MyObj() [Attribute foo] | -| examples.py:16:5:16:21 | ControlFlowNode for FunctionExpr | examples.py:16:9:16:14 | SSA variable getObj | -| examples.py:16:5:16:21 | ControlFlowNode for FunctionExpr | examples.py:16:9:16:14 | SSA variable getObj | -| examples.py:16:16:16:19 | SSA variable self | examples.py:17:16:17:19 | ControlFlowNode for self | -| examples.py:16:16:16:19 | SSA variable self | examples.py:17:16:17:19 | ControlFlowNode for self | -| examples.py:21:1:21:19 | ControlFlowNode for FunctionExpr | examples.py:21:5:21:10 | GSSA Variable setFoo | -| examples.py:21:1:21:19 | ControlFlowNode for FunctionExpr | examples.py:21:5:21:10 | GSSA Variable setFoo | -| examples.py:21:1:21:19 | ControlFlowNode for FunctionExpr | examples.py:27:1:27:6 | ControlFlowNode for setFoo | -| examples.py:21:1:21:19 | ControlFlowNode for FunctionExpr | examples.py:27:1:27:6 | ControlFlowNode for setFoo | -| examples.py:21:1:21:19 | GSSA Variable SINK_F | examples.py:22:5:22:10 | ControlFlowNode for SINK_F | -| examples.py:21:1:21:19 | GSSA Variable SINK_F | examples.py:22:5:22:10 | ControlFlowNode for SINK_F | -| examples.py:21:5:21:10 | GSSA Variable setFoo | examples.py:27:1:27:6 | ControlFlowNode for setFoo | -| examples.py:21:5:21:10 | GSSA Variable setFoo | examples.py:27:1:27:6 | ControlFlowNode for setFoo | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:22:12:22:14 | ControlFlowNode for obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:22:12:22:14 | ControlFlowNode for obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:22:12:22:14 | ControlFlowNode for obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:22:12:22:14 | ControlFlowNode for obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:11 | SSA variable obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:11 | SSA variable obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:11 | SSA variable obj | -| examples.py:21:12:21:14 | SSA variable obj | examples.py:23:5:23:11 | SSA variable obj | -| examples.py:21:12:21:14 | SSA variable obj [Attribute foo] | examples.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | -| examples.py:21:17:21:17 | SSA variable x | examples.py:23:15:23:15 | ControlFlowNode for x | -| examples.py:21:17:21:17 | SSA variable x | examples.py:23:15:23:15 | ControlFlowNode for x | -| examples.py:21:17:21:17 | SSA variable x | examples.py:23:15:23:15 | ControlFlowNode for x | -| examples.py:21:17:21:17 | SSA variable x | examples.py:23:15:23:15 | ControlFlowNode for x | -| examples.py:22:12:22:14 | ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:22:12:22:14 | ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:22:12:22:14 | ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:22:12:22:14 | ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | examples.py:22:12:22:18 | ControlFlowNode for Attribute | -| examples.py:22:12:22:14 | ControlFlowNode for obj [Attribute foo] | examples.py:22:12:22:18 | ControlFlowNode for Attribute | -| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | -| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | -| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | -| examples.py:23:5:23:7 | [post store] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | -| examples.py:23:5:23:7 | [post store] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | -| examples.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | -| examples.py:23:15:23:15 | ControlFlowNode for x | examples.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | -| examples.py:23:15:23:15 | ControlFlowNode for x | examples.py:23:5:23:7 | [post store] ControlFlowNode for obj [Attribute foo] | +| examples.py:11:7:11:15 | GSSA Variable NestedObj | examples.py:42:5:42:13 | ControlFlowNode for NestedObj | +| examples.py:11:7:11:15 | GSSA Variable NestedObj | examples.py:42:5:42:13 | ControlFlowNode for NestedObj | +| examples.py:12:5:12:23 | ControlFlowNode for FunctionExpr | examples.py:12:9:12:16 | SSA variable __init__ | +| examples.py:12:5:12:23 | ControlFlowNode for FunctionExpr | examples.py:12:9:12:16 | SSA variable __init__ | +| examples.py:12:5:12:23 | GSSA Variable MyObj | examples.py:13:20:13:24 | ControlFlowNode for MyObj | +| examples.py:12:5:12:23 | GSSA Variable MyObj | examples.py:13:20:13:24 | ControlFlowNode for MyObj | +| examples.py:12:18:12:21 | SSA variable self | examples.py:13:9:13:12 | ControlFlowNode for self | +| examples.py:12:18:12:21 | SSA variable self | examples.py:13:9:13:12 | ControlFlowNode for self | +| examples.py:12:18:12:21 | SSA variable self | examples.py:13:9:13:12 | ControlFlowNode for self | +| examples.py:12:18:12:21 | SSA variable self | examples.py:13:9:13:12 | ControlFlowNode for self | +| examples.py:12:18:12:21 | SSA variable self | examples.py:13:9:13:16 | SSA variable self | +| examples.py:12:18:12:21 | SSA variable self | examples.py:13:9:13:16 | SSA variable self | +| examples.py:12:18:12:21 | SSA variable self | examples.py:13:9:13:16 | SSA variable self | +| examples.py:12:18:12:21 | SSA variable self | examples.py:13:9:13:16 | SSA variable self | +| examples.py:13:9:13:12 | [post store] ControlFlowNode for self | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | +| examples.py:13:9:13:12 | [post store] ControlFlowNode for self | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | +| examples.py:13:9:13:12 | [post store] ControlFlowNode for self | examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | +| examples.py:13:9:13:12 | [post store] ControlFlowNode for self | examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | +| examples.py:13:9:13:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | +| examples.py:13:9:13:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | examples.py:42:5:42:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | +| examples.py:13:9:13:12 | [post store] ControlFlowNode for self [Attribute obj] | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | +| examples.py:13:9:13:12 | [post store] ControlFlowNode for self [Attribute obj] | examples.py:42:5:42:15 | ControlFlowNode for NestedObj() [Attribute obj] | +| examples.py:13:20:13:30 | ControlFlowNode for MyObj() | examples.py:13:9:13:12 | [post store] ControlFlowNode for self [Attribute obj] | +| examples.py:13:20:13:30 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:13:9:13:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | +| examples.py:13:20:13:30 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:13:20:13:30 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:13:26:13:29 | ControlFlowNode for Str | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:13:26:13:29 | ControlFlowNode for Str | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:13:26:13:29 | ControlFlowNode for Str | examples.py:13:20:13:30 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:15:5:15:21 | ControlFlowNode for FunctionExpr | examples.py:15:9:15:14 | SSA variable getObj | +| examples.py:15:5:15:21 | ControlFlowNode for FunctionExpr | examples.py:15:9:15:14 | SSA variable getObj | +| examples.py:15:16:15:19 | SSA variable self | examples.py:16:16:16:19 | ControlFlowNode for self | +| examples.py:15:16:15:19 | SSA variable self | examples.py:16:16:16:19 | ControlFlowNode for self | +| examples.py:20:1:20:19 | ControlFlowNode for FunctionExpr | examples.py:20:5:20:10 | GSSA Variable setFoo | +| examples.py:20:1:20:19 | ControlFlowNode for FunctionExpr | examples.py:20:5:20:10 | GSSA Variable setFoo | +| examples.py:20:1:20:19 | ControlFlowNode for FunctionExpr | examples.py:27:1:27:6 | ControlFlowNode for setFoo | +| examples.py:20:1:20:19 | ControlFlowNode for FunctionExpr | examples.py:27:1:27:6 | ControlFlowNode for setFoo | +| examples.py:20:1:20:19 | GSSA Variable SINK_F | examples.py:21:5:21:10 | ControlFlowNode for SINK_F | +| examples.py:20:1:20:19 | GSSA Variable SINK_F | examples.py:21:5:21:10 | ControlFlowNode for SINK_F | +| examples.py:20:5:20:10 | GSSA Variable setFoo | examples.py:27:1:27:6 | ControlFlowNode for setFoo | +| examples.py:20:5:20:10 | GSSA Variable setFoo | examples.py:27:1:27:6 | ControlFlowNode for setFoo | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:21:12:21:14 | ControlFlowNode for obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:21:12:21:14 | ControlFlowNode for obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:21:12:21:14 | ControlFlowNode for obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:21:12:21:14 | ControlFlowNode for obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:22:5:22:7 | ControlFlowNode for obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:22:5:22:7 | ControlFlowNode for obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:22:5:22:7 | ControlFlowNode for obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:22:5:22:7 | ControlFlowNode for obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:22:5:22:11 | SSA variable obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:22:5:22:11 | SSA variable obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:22:5:22:11 | SSA variable obj | +| examples.py:20:12:20:14 | SSA variable obj | examples.py:22:5:22:11 | SSA variable obj | +| examples.py:20:12:20:14 | SSA variable obj [Attribute foo] | examples.py:21:12:21:14 | ControlFlowNode for obj [Attribute foo] | +| examples.py:20:17:20:17 | SSA variable x | examples.py:22:15:22:15 | ControlFlowNode for x | +| examples.py:20:17:20:17 | SSA variable x | examples.py:22:15:22:15 | ControlFlowNode for x | +| examples.py:20:17:20:17 | SSA variable x | examples.py:22:15:22:15 | ControlFlowNode for x | +| examples.py:20:17:20:17 | SSA variable x | examples.py:22:15:22:15 | ControlFlowNode for x | +| examples.py:21:12:21:14 | ControlFlowNode for obj | examples.py:22:5:22:7 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | ControlFlowNode for obj | examples.py:22:5:22:7 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | ControlFlowNode for obj | examples.py:22:5:22:7 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | ControlFlowNode for obj | examples.py:22:5:22:7 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | ControlFlowNode for obj [Attribute foo] | examples.py:21:12:21:18 | ControlFlowNode for Attribute | +| examples.py:21:12:21:14 | ControlFlowNode for obj [Attribute foo] | examples.py:21:12:21:18 | ControlFlowNode for Attribute | +| examples.py:21:12:21:14 | [post read] ControlFlowNode for obj | examples.py:22:5:22:7 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | [post read] ControlFlowNode for obj | examples.py:22:5:22:7 | ControlFlowNode for obj | +| examples.py:21:12:21:14 | [post read] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | +| examples.py:21:12:21:14 | [post read] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | +| examples.py:22:5:22:7 | [post store] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | +| examples.py:22:5:22:7 | [post store] ControlFlowNode for obj | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | +| examples.py:22:5:22:7 | [post store] ControlFlowNode for obj [Attribute foo] | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| examples.py:22:15:22:15 | ControlFlowNode for x | examples.py:22:5:22:7 | [post store] ControlFlowNode for obj [Attribute foo] | +| examples.py:22:15:22:15 | ControlFlowNode for x | examples.py:22:5:22:7 | [post store] ControlFlowNode for obj [Attribute foo] | | examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:27:1:27:21 | GSSA Variable myobj | | examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:27:1:27:21 | GSSA Variable myobj | | examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj | @@ -187,8 +201,8 @@ | examples.py:25:1:25:5 | GSSA Variable myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | | examples.py:25:1:25:5 | GSSA Variable myobj [Attribute foo] | examples.py:27:8:27:12 | ControlFlowNode for myobj [Attribute foo] | | examples.py:25:1:25:5 | GSSA Variable myobj [Attribute foo] | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | -| examples.py:25:9:25:13 | ControlFlowNode for MyObj | examples.py:41:7:41:11 | ControlFlowNode for MyObj | -| examples.py:25:9:25:13 | ControlFlowNode for MyObj | examples.py:41:7:41:11 | ControlFlowNode for MyObj | +| examples.py:25:9:25:13 | ControlFlowNode for MyObj | examples.py:49:7:49:11 | ControlFlowNode for MyObj | +| examples.py:25:9:25:13 | ControlFlowNode for MyObj | examples.py:49:7:49:11 | ControlFlowNode for MyObj | | examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:25:1:25:5 | GSSA Variable myobj | | examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:25:1:25:5 | GSSA Variable myobj | | examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:27:1:27:21 | GSSA Variable myobj | @@ -205,17 +219,17 @@ | examples.py:25:15:25:18 | ControlFlowNode for Str | examples.py:7:24:7:26 | SSA variable foo | | examples.py:25:15:25:18 | ControlFlowNode for Str | examples.py:7:24:7:26 | SSA variable foo | | examples.py:25:15:25:18 | ControlFlowNode for Str | examples.py:25:9:25:19 | ControlFlowNode for MyObj() [Attribute foo] | -| examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:21:12:21:14 | SSA variable obj | -| examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:21:12:21:14 | SSA variable obj | +| examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:20:12:20:14 | SSA variable obj | +| examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:20:12:20:14 | SSA variable obj | | examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | | examples.py:27:8:27:12 | ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | -| examples.py:27:8:27:12 | ControlFlowNode for myobj [Attribute foo] | examples.py:21:12:21:14 | SSA variable obj [Attribute foo] | +| examples.py:27:8:27:12 | ControlFlowNode for myobj [Attribute foo] | examples.py:20:12:20:14 | SSA variable obj [Attribute foo] | | examples.py:27:8:27:12 | ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:28:6:28:10 | ControlFlowNode for myobj | | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:21:17:21:17 | SSA variable x | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:21:17:21:17 | SSA variable x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:20:17:20:17 | SSA variable x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:20:17:20:17 | SSA variable x | | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | @@ -223,238 +237,283 @@ | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:40:1:40:1 | GSSA Variable x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:40:1:40:1 | GSSA Variable x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | | examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | | examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | | examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | | examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | | examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | | examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | -| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:38:1:38:4 | ControlFlowNode for SINK | -| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:38:1:38:4 | ControlFlowNode for SINK | -| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | -| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:40:1:40:1 | GSSA Variable x | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:40:1:40:1 | GSSA Variable x | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:27:15:27:20 | [post arg] ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:37:1:37:4 | ControlFlowNode for SINK | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:37:1:37:4 | ControlFlowNode for SINK | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:46:1:46:4 | ControlFlowNode for SINK | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:46:1:46:4 | ControlFlowNode for SINK | | examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | | examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:59:1:59:4 | ControlFlowNode for SINK | +| examples.py:28:1:28:4 | ControlFlowNode for SINK | examples.py:59:1:59:4 | ControlFlowNode for SINK | | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:14 | ControlFlowNode for Attribute | | examples.py:28:6:28:10 | ControlFlowNode for myobj [Attribute foo] | examples.py:28:6:28:14 | ControlFlowNode for Attribute | | examples.py:31:1:31:1 | GSSA Variable x | examples.py:35:13:35:13 | ControlFlowNode for x | | examples.py:31:1:31:1 | GSSA Variable x | examples.py:35:13:35:13 | ControlFlowNode for x | -| examples.py:31:1:31:1 | GSSA Variable x | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:31:1:31:1 | GSSA Variable x | examples.py:36:18:36:18 | ControlFlowNode for x | | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:31:1:31:1 | GSSA Variable x | | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | | examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:35:13:35:13 | ControlFlowNode for x | -| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:40:1:40:1 | GSSA Variable x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:40:1:40:1 | GSSA Variable x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:31:5:31:10 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | | examples.py:33:1:33:1 | GSSA Variable a | examples.py:35:1:35:1 | ControlFlowNode for a | | examples.py:33:1:33:1 | GSSA Variable a | examples.py:35:1:35:1 | ControlFlowNode for a | -| examples.py:33:1:33:1 | GSSA Variable a | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:33:1:33:1 | GSSA Variable a | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:33:1:33:1 | GSSA Variable a | examples.py:36:1:36:10 | GSSA Variable a | -| examples.py:33:1:33:1 | GSSA Variable a | examples.py:36:1:36:10 | GSSA Variable a | -| examples.py:33:1:33:1 | GSSA Variable a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:33:1:33:1 | GSSA Variable a | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:37:6:37:6 | ControlFlowNode for a | +| examples.py:33:1:33:1 | GSSA Variable a | examples.py:37:6:37:6 | ControlFlowNode for a | | examples.py:33:1:33:1 | GSSA Variable a [Attribute obj, Attribute foo] | examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj, Attribute foo] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj, Attribute foo] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj, Attribute foo] | examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | | examples.py:33:1:33:1 | GSSA Variable a [Attribute obj] | examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | -| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj] | -| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:33:1:33:1 | GSSA Variable a [Attribute obj] | examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:33:5:33:13 | ControlFlowNode for NestedObj | examples.py:42:5:42:13 | ControlFlowNode for NestedObj | +| examples.py:33:5:33:13 | ControlFlowNode for NestedObj | examples.py:42:5:42:13 | ControlFlowNode for NestedObj | | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:33:1:33:1 | GSSA Variable a | | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:33:1:33:1 | GSSA Variable a | | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:35:1:35:1 | ControlFlowNode for a | | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:35:1:35:1 | ControlFlowNode for a | -| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:36:1:36:10 | GSSA Variable a | -| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:36:1:36:10 | GSSA Variable a | -| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:38:6:38:6 | ControlFlowNode for a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:37:6:37:6 | ControlFlowNode for a | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:37:6:37:6 | ControlFlowNode for a | | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:33:1:33:1 | GSSA Variable a [Attribute obj, Attribute foo] | | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:33:1:33:1 | GSSA Variable a [Attribute obj] | | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | -| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj] | -| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | -| examples.py:33:5:33:15 | [pre objCreate] ControlFlowNode for NestedObj() | examples.py:13:18:13:21 | SSA variable self | -| examples.py:33:5:33:15 | [pre objCreate] ControlFlowNode for NestedObj() | examples.py:13:18:13:21 | SSA variable self | -| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:33:5:33:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:33:5:33:15 | [pre objCreate] ControlFlowNode for NestedObj() | examples.py:12:18:12:21 | SSA variable self | +| examples.py:33:5:33:15 | [pre objCreate] ControlFlowNode for NestedObj() | examples.py:12:18:12:21 | SSA variable self | +| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:37:6:37:6 | ControlFlowNode for a | +| examples.py:35:1:35:1 | ControlFlowNode for a | examples.py:37:6:37:6 | ControlFlowNode for a | +| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | | examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | examples.py:35:1:35:5 | ControlFlowNode for Attribute | | examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | examples.py:35:1:35:5 | ControlFlowNode for Attribute | -| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj] | -| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | -| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj] | examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj] | -| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:35:1:35:1 | ControlFlowNode for a [Attribute obj] | examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:37:6:37:6 | ControlFlowNode for a | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:37:6:37:6 | ControlFlowNode for a | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj] | examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj] | | examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute | examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj] | | examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute [Attribute foo] | examples.py:35:1:35:1 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | | examples.py:35:13:35:13 | ControlFlowNode for x | examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute [Attribute foo] | -| examples.py:35:13:35:13 | ControlFlowNode for x | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:35:13:35:13 | ControlFlowNode for x | examples.py:36:18:36:18 | ControlFlowNode for x | -| examples.py:36:1:36:1 | ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:36:1:36:1 | ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| examples.py:36:1:36:1 | ControlFlowNode for a [Attribute obj] | examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | -| examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:38:1:38:4 | ControlFlowNode for SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | -| examples.py:38:1:38:4 | ControlFlowNode for SINK | examples.py:42:1:42:4 | ControlFlowNode for SINK | -| examples.py:38:1:38:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | -| examples.py:38:1:38:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | -| examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:38:6:38:10 | ControlFlowNode for Attribute [Attribute foo] | -| examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | examples.py:38:6:38:10 | ControlFlowNode for Attribute | -| examples.py:38:6:38:6 | ControlFlowNode for a [Attribute obj] | examples.py:38:6:38:10 | ControlFlowNode for Attribute | -| examples.py:38:6:38:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:38:6:38:14 | ControlFlowNode for Attribute | -| examples.py:38:6:38:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:38:6:38:14 | ControlFlowNode for Attribute | -| examples.py:41:1:41:3 | GSSA Variable obj | examples.py:42:6:42:8 | ControlFlowNode for obj | -| examples.py:41:1:41:3 | GSSA Variable obj | examples.py:42:6:42:8 | ControlFlowNode for obj | -| examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] | examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | -| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:41:1:41:3 | GSSA Variable obj | -| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:41:1:41:3 | GSSA Variable obj | -| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:42:6:42:8 | ControlFlowNode for obj | -| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:42:6:42:8 | ControlFlowNode for obj | -| examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:41:1:41:3 | GSSA Variable obj [Attribute foo] | -| examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | -| examples.py:41:7:41:19 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | -| examples.py:41:7:41:19 | GSSA Variable SOURCE | examples.py:50:6:50:35 | GSSA Variable SOURCE | -| examples.py:41:7:41:19 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | -| examples.py:41:7:41:19 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:7:24:7:26 | SSA variable foo | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:7:24:7:26 | SSA variable foo | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:41:7:41:19 | ControlFlowNode for MyObj() [Attribute foo] | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:41:13:41:18 | ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | -| examples.py:42:1:42:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | -| examples.py:42:1:42:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | -| examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | examples.py:42:6:42:12 | ControlFlowNode for Attribute | -| examples.py:42:6:42:8 | ControlFlowNode for obj [Attribute foo] | examples.py:42:6:42:12 | ControlFlowNode for Attribute | -| examples.py:45:1:45:30 | ControlFlowNode for FunctionExpr | examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | -| examples.py:45:1:45:30 | ControlFlowNode for FunctionExpr | examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | -| examples.py:45:1:45:30 | ControlFlowNode for FunctionExpr | examples.py:50:6:50:27 | ControlFlowNode for fields_with_local_flow | -| examples.py:45:1:45:30 | ControlFlowNode for FunctionExpr | examples.py:50:6:50:27 | ControlFlowNode for fields_with_local_flow | -| examples.py:45:1:45:30 | GSSA Variable MyObj | examples.py:46:9:46:13 | ControlFlowNode for MyObj | -| examples.py:45:1:45:30 | GSSA Variable MyObj | examples.py:46:9:46:13 | ControlFlowNode for MyObj | -| examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | examples.py:50:6:50:27 | ControlFlowNode for fields_with_local_flow | -| examples.py:45:5:45:26 | GSSA Variable fields_with_local_flow | examples.py:50:6:50:27 | ControlFlowNode for fields_with_local_flow | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | -| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj | -| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj | -| examples.py:46:3:46:5 | SSA variable obj [Attribute foo] | examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | -| examples.py:46:3:46:5 | SSA variable obj [Attribute foo] | examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:47:7:47:9 | ControlFlowNode for obj | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:47:7:47:9 | ControlFlowNode for obj | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:46:3:46:5 | SSA variable obj [Attribute foo] | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:46:3:46:5 | SSA variable obj [Attribute foo] | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | -| examples.py:46:9:46:16 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | -| examples.py:46:9:46:16 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | -| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | -| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | -| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | -| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | -| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | -| examples.py:46:15:46:15 | ControlFlowNode for x | examples.py:46:9:46:16 | ControlFlowNode for MyObj() [Attribute foo] | -| examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | -| examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | -| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | -| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | -| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | -| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | -| examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | examples.py:47:7:47:13 | ControlFlowNode for Attribute | -| examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | examples.py:47:7:47:13 | ControlFlowNode for Attribute | -| examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | examples.py:47:7:47:13 | ControlFlowNode for Attribute | -| examples.py:47:7:47:9 | ControlFlowNode for obj [Attribute foo] | examples.py:47:7:47:13 | ControlFlowNode for Attribute | -| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | -| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | -| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | -| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | -| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:48:10:48:10 | ControlFlowNode for a | -| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:48:10:48:10 | ControlFlowNode for a | -| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:48:10:48:10 | ControlFlowNode for a | -| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:48:10:48:10 | ControlFlowNode for a | -| examples.py:48:10:48:10 | ControlFlowNode for a | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | -| examples.py:48:10:48:10 | ControlFlowNode for a | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | -| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:45:28:45:28 | SSA variable x | -| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:45:28:45:28 | SSA variable x | -| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | -| examples.py:50:29:50:34 | ControlFlowNode for SOURCE | examples.py:50:6:50:35 | ControlFlowNode for fields_with_local_flow() | +| examples.py:37:1:37:4 | ControlFlowNode for SINK | examples.py:46:1:46:4 | ControlFlowNode for SINK | +| examples.py:37:1:37:4 | ControlFlowNode for SINK | examples.py:46:1:46:4 | ControlFlowNode for SINK | +| examples.py:37:1:37:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:37:1:37:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:37:1:37:4 | ControlFlowNode for SINK | examples.py:59:1:59:4 | ControlFlowNode for SINK | +| examples.py:37:1:37:4 | ControlFlowNode for SINK | examples.py:59:1:59:4 | ControlFlowNode for SINK | +| examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:37:6:37:10 | ControlFlowNode for Attribute [Attribute foo] | +| examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj] | examples.py:37:6:37:10 | ControlFlowNode for Attribute | +| examples.py:37:6:37:6 | ControlFlowNode for a [Attribute obj] | examples.py:37:6:37:10 | ControlFlowNode for Attribute | +| examples.py:37:6:37:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:37:6:37:14 | ControlFlowNode for Attribute | +| examples.py:37:6:37:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:37:6:37:14 | ControlFlowNode for Attribute | +| examples.py:40:1:40:1 | GSSA Variable x | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:40:1:40:1 | GSSA Variable x | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:40:1:40:1 | GSSA Variable x | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:40:1:40:1 | GSSA Variable x | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:44:18:44:18 | ControlFlowNode for x | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:42:1:42:1 | GSSA Variable a | examples.py:44:1:44:1 | ControlFlowNode for a | +| examples.py:42:1:42:1 | GSSA Variable a | examples.py:44:1:44:1 | ControlFlowNode for a | +| examples.py:42:1:42:1 | GSSA Variable a | examples.py:44:1:44:10 | GSSA Variable a | +| examples.py:42:1:42:1 | GSSA Variable a | examples.py:44:1:44:10 | GSSA Variable a | +| examples.py:42:1:42:1 | GSSA Variable a | examples.py:46:6:46:6 | ControlFlowNode for a | +| examples.py:42:1:42:1 | GSSA Variable a | examples.py:46:6:46:6 | ControlFlowNode for a | +| examples.py:42:1:42:1 | GSSA Variable a [Attribute obj, Attribute foo] | examples.py:44:1:44:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:42:1:42:1 | GSSA Variable a [Attribute obj, Attribute foo] | examples.py:46:6:46:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:42:1:42:1 | GSSA Variable a [Attribute obj] | examples.py:44:1:44:1 | ControlFlowNode for a [Attribute obj] | +| examples.py:42:1:42:1 | GSSA Variable a [Attribute obj] | examples.py:46:6:46:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | examples.py:42:1:42:1 | GSSA Variable a | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | examples.py:42:1:42:1 | GSSA Variable a | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | examples.py:44:1:44:1 | ControlFlowNode for a | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | examples.py:44:1:44:1 | ControlFlowNode for a | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | examples.py:44:1:44:10 | GSSA Variable a | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | examples.py:44:1:44:10 | GSSA Variable a | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | examples.py:46:6:46:6 | ControlFlowNode for a | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | examples.py:46:6:46:6 | ControlFlowNode for a | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:42:1:42:1 | GSSA Variable a [Attribute obj, Attribute foo] | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:44:1:44:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | examples.py:46:6:46:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:42:1:42:1 | GSSA Variable a [Attribute obj] | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:44:1:44:1 | ControlFlowNode for a [Attribute obj] | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() [Attribute obj] | examples.py:46:6:46:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:42:5:42:15 | [pre objCreate] ControlFlowNode for NestedObj() | examples.py:12:18:12:21 | SSA variable self | +| examples.py:42:5:42:15 | [pre objCreate] ControlFlowNode for NestedObj() | examples.py:12:18:12:21 | SSA variable self | +| examples.py:44:1:44:1 | ControlFlowNode for a | examples.py:46:6:46:6 | ControlFlowNode for a | +| examples.py:44:1:44:1 | ControlFlowNode for a | examples.py:46:6:46:6 | ControlFlowNode for a | +| examples.py:44:1:44:1 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:46:6:46:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| examples.py:44:1:44:1 | ControlFlowNode for a [Attribute obj] | examples.py:46:6:46:6 | ControlFlowNode for a [Attribute obj] | +| examples.py:44:1:44:1 | [post read] ControlFlowNode for a | examples.py:46:6:46:6 | ControlFlowNode for a | +| examples.py:44:1:44:1 | [post read] ControlFlowNode for a | examples.py:46:6:46:6 | ControlFlowNode for a | +| examples.py:46:1:46:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:46:1:46:4 | ControlFlowNode for SINK | examples.py:50:1:50:4 | ControlFlowNode for SINK | +| examples.py:46:1:46:4 | ControlFlowNode for SINK | examples.py:59:1:59:4 | ControlFlowNode for SINK | +| examples.py:46:1:46:4 | ControlFlowNode for SINK | examples.py:59:1:59:4 | ControlFlowNode for SINK | +| examples.py:46:6:46:6 | ControlFlowNode for a [Attribute obj, Attribute foo] | examples.py:46:6:46:10 | ControlFlowNode for Attribute [Attribute foo] | +| examples.py:46:6:46:6 | ControlFlowNode for a [Attribute obj] | examples.py:46:6:46:10 | ControlFlowNode for Attribute | +| examples.py:46:6:46:6 | ControlFlowNode for a [Attribute obj] | examples.py:46:6:46:10 | ControlFlowNode for Attribute | +| examples.py:46:6:46:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:46:6:46:14 | ControlFlowNode for Attribute | +| examples.py:46:6:46:10 | ControlFlowNode for Attribute [Attribute foo] | examples.py:46:6:46:14 | ControlFlowNode for Attribute | +| examples.py:49:1:49:3 | GSSA Variable obj | examples.py:50:6:50:8 | ControlFlowNode for obj | +| examples.py:49:1:49:3 | GSSA Variable obj | examples.py:50:6:50:8 | ControlFlowNode for obj | +| examples.py:49:1:49:3 | GSSA Variable obj [Attribute foo] | examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | +| examples.py:49:7:49:19 | ControlFlowNode for MyObj() | examples.py:49:1:49:3 | GSSA Variable obj | +| examples.py:49:7:49:19 | ControlFlowNode for MyObj() | examples.py:49:1:49:3 | GSSA Variable obj | +| examples.py:49:7:49:19 | ControlFlowNode for MyObj() | examples.py:50:6:50:8 | ControlFlowNode for obj | +| examples.py:49:7:49:19 | ControlFlowNode for MyObj() | examples.py:50:6:50:8 | ControlFlowNode for obj | +| examples.py:49:7:49:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:49:1:49:3 | GSSA Variable obj [Attribute foo] | +| examples.py:49:7:49:19 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | +| examples.py:49:7:49:19 | GSSA Variable SOURCE | examples.py:59:6:59:35 | GSSA Variable SOURCE | +| examples.py:49:7:49:19 | GSSA Variable SOURCE | examples.py:59:6:59:35 | GSSA Variable SOURCE | +| examples.py:49:7:49:19 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:49:7:49:19 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:49:7:49:19 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:49:13:49:18 | [post arg] ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:49:13:49:18 | [post arg] ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | +| examples.py:50:1:50:4 | ControlFlowNode for SINK | examples.py:59:1:59:4 | ControlFlowNode for SINK | +| examples.py:50:1:50:4 | ControlFlowNode for SINK | examples.py:59:1:59:4 | ControlFlowNode for SINK | +| examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | examples.py:50:6:50:12 | ControlFlowNode for Attribute | +| examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | examples.py:50:6:50:12 | ControlFlowNode for Attribute | +| examples.py:53:1:53:30 | ControlFlowNode for FunctionExpr | examples.py:53:5:53:26 | GSSA Variable fields_with_local_flow | +| examples.py:53:1:53:30 | ControlFlowNode for FunctionExpr | examples.py:53:5:53:26 | GSSA Variable fields_with_local_flow | +| examples.py:53:1:53:30 | ControlFlowNode for FunctionExpr | examples.py:59:6:59:27 | ControlFlowNode for fields_with_local_flow | +| examples.py:53:1:53:30 | ControlFlowNode for FunctionExpr | examples.py:59:6:59:27 | ControlFlowNode for fields_with_local_flow | +| examples.py:53:1:53:30 | GSSA Variable MyObj | examples.py:54:11:54:15 | ControlFlowNode for MyObj | +| examples.py:53:1:53:30 | GSSA Variable MyObj | examples.py:54:11:54:15 | ControlFlowNode for MyObj | +| examples.py:53:5:53:26 | GSSA Variable fields_with_local_flow | examples.py:59:6:59:27 | ControlFlowNode for fields_with_local_flow | +| examples.py:53:5:53:26 | GSSA Variable fields_with_local_flow | examples.py:59:6:59:27 | ControlFlowNode for fields_with_local_flow | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:11:54:18 | SSA variable x | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:11:54:18 | SSA variable x | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:11:54:18 | SSA variable x | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:11:54:18 | SSA variable x | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:17:54:17 | ControlFlowNode for x | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:17:54:17 | ControlFlowNode for x | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:17:54:17 | ControlFlowNode for x | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:17:54:17 | ControlFlowNode for x | +| examples.py:54:5:54:7 | SSA variable obj | examples.py:55:9:55:11 | ControlFlowNode for obj | +| examples.py:54:5:54:7 | SSA variable obj | examples.py:55:9:55:11 | ControlFlowNode for obj | +| examples.py:54:5:54:7 | SSA variable obj [Attribute foo] | examples.py:55:9:55:11 | ControlFlowNode for obj [Attribute foo] | +| examples.py:54:5:54:7 | SSA variable obj [Attribute foo] | examples.py:55:9:55:11 | ControlFlowNode for obj [Attribute foo] | +| examples.py:54:11:54:18 | ControlFlowNode for MyObj() | examples.py:54:5:54:7 | SSA variable obj | +| examples.py:54:11:54:18 | ControlFlowNode for MyObj() | examples.py:54:5:54:7 | SSA variable obj | +| examples.py:54:11:54:18 | ControlFlowNode for MyObj() | examples.py:55:9:55:11 | ControlFlowNode for obj | +| examples.py:54:11:54:18 | ControlFlowNode for MyObj() | examples.py:55:9:55:11 | ControlFlowNode for obj | +| examples.py:54:11:54:18 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:54:5:54:7 | SSA variable obj [Attribute foo] | +| examples.py:54:11:54:18 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:54:5:54:7 | SSA variable obj [Attribute foo] | +| examples.py:54:11:54:18 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:55:9:55:11 | ControlFlowNode for obj [Attribute foo] | +| examples.py:54:11:54:18 | ControlFlowNode for MyObj() [Attribute foo] | examples.py:55:9:55:11 | ControlFlowNode for obj [Attribute foo] | +| examples.py:54:11:54:18 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:54:11:54:18 | [pre objCreate] ControlFlowNode for MyObj() | examples.py:7:18:7:21 | SSA variable self | +| examples.py:54:17:54:17 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:54:17:54:17 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:54:17:54:17 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:54:17:54:17 | ControlFlowNode for x | examples.py:7:24:7:26 | SSA variable foo | +| examples.py:54:17:54:17 | ControlFlowNode for x | examples.py:54:11:54:18 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:54:17:54:17 | ControlFlowNode for x | examples.py:54:11:54:18 | ControlFlowNode for MyObj() [Attribute foo] | +| examples.py:54:17:54:17 | [post arg] ControlFlowNode for x | examples.py:59:29:59:34 | [post arg] ControlFlowNode for SOURCE | +| examples.py:54:17:54:17 | [post arg] ControlFlowNode for x | examples.py:59:29:59:34 | [post arg] ControlFlowNode for SOURCE | +| examples.py:55:5:55:5 | SSA variable a | examples.py:56:12:56:12 | ControlFlowNode for a | +| examples.py:55:5:55:5 | SSA variable a | examples.py:56:12:56:12 | ControlFlowNode for a | +| examples.py:55:5:55:5 | SSA variable a | examples.py:56:12:56:12 | ControlFlowNode for a | +| examples.py:55:5:55:5 | SSA variable a | examples.py:56:12:56:12 | ControlFlowNode for a | +| examples.py:55:9:55:11 | ControlFlowNode for obj [Attribute foo] | examples.py:55:9:55:15 | ControlFlowNode for Attribute | +| examples.py:55:9:55:11 | ControlFlowNode for obj [Attribute foo] | examples.py:55:9:55:15 | ControlFlowNode for Attribute | +| examples.py:55:9:55:11 | ControlFlowNode for obj [Attribute foo] | examples.py:55:9:55:15 | ControlFlowNode for Attribute | +| examples.py:55:9:55:11 | ControlFlowNode for obj [Attribute foo] | examples.py:55:9:55:15 | ControlFlowNode for Attribute | +| examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:55:5:55:5 | SSA variable a | +| examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:55:5:55:5 | SSA variable a | +| examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:55:5:55:5 | SSA variable a | +| examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:55:5:55:5 | SSA variable a | +| examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:56:12:56:12 | ControlFlowNode for a | +| examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:56:12:56:12 | ControlFlowNode for a | +| examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:56:12:56:12 | ControlFlowNode for a | +| examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:56:12:56:12 | ControlFlowNode for a | +| examples.py:56:12:56:12 | ControlFlowNode for a | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | +| examples.py:56:12:56:12 | ControlFlowNode for a | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | +| examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:53:28:53:28 | SSA variable x | +| examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:53:28:53:28 | SSA variable x | +| examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | +| examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:32:20:32:24 | ControlFlowNode for MyObj | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:32:20:32:24 | ControlFlowNode for MyObj | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:44:13:44:17 | ControlFlowNode for MyObj | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:44:13:44:17 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:62:11:62:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:62:11:62:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:67:11:67:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:67:11:67:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:72:11:72:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:72:11:72:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:71:11:71:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:71:11:71:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:76:11:76:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:76:11:76:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:81:11:81:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:81:11:81:15 | ControlFlowNode for MyObj | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:53:9:53:17 | ControlFlowNode for NestedObj | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:53:9:53:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:63:9:63:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:63:9:63:17 | ControlFlowNode for NestedObj | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:47:5:47:8 | ControlFlowNode for SINK | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:47:5:47:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:58:5:58:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:58:5:58:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:63:5:63:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:63:5:63:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:68:5:68:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:68:5:68:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:78:5:78:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:78:5:78:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:57:5:57:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:57:5:57:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:67:5:67:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:67:5:67:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:72:5:72:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:72:5:72:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:77:5:77:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:77:5:77:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:87:5:87:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:87:5:87:8 | ControlFlowNode for SINK | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:39:5:39:10 | ControlFlowNode for SINK_F | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:39:5:39:10 | ControlFlowNode for SINK_F | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:46:19:46:24 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:46:19:46:24 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:51:9:51:14 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:51:9:51:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:62:17:62:22 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:62:17:62:22 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:67:21:67:26 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:67:21:67:26 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:78:33:78:38 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:78:33:78:38 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:78:10:78:31 | ControlFlowNode for fields_with_local_flow | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:78:10:78:31 | ControlFlowNode for fields_with_local_flow | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:17:71:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:17:71:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:76:21:76:26 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:76:21:76:26 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:33:87:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:33:87:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:87:10:87:31 | ControlFlowNode for fields_with_local_flow | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:87:10:87:31 | ControlFlowNode for fields_with_local_flow | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:11:8:11:16 | ControlFlowNode for is_source | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:11:8:11:16 | ControlFlowNode for is_source | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:18:8:18:16 | ControlFlowNode for is_source | @@ -697,14 +756,16 @@ | test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | | test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:47:10:47:18 | [post arg] ControlFlowNode for Attribute | | test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:47:10:47:18 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:58:10:58:18 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:58:10:58:18 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:63:10:63:16 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:63:10:63:16 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:68:10:68:16 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:68:10:68:16 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:78:10:78:39 | [post arg] ControlFlowNode for fields_with_local_flow() | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:78:10:78:39 | [post arg] ControlFlowNode for fields_with_local_flow() | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:57:10:57:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:57:10:57:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:67:10:67:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:67:10:67:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:72:10:72:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:72:10:72:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:77:10:77:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:77:10:77:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:87:10:87:39 | [post arg] ControlFlowNode for fields_with_local_flow() | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:87:10:87:39 | [post arg] ControlFlowNode for fields_with_local_flow() | | test.py:12:9:12:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | | test.py:12:9:12:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | | test.py:12:9:12:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | @@ -785,17 +846,17 @@ | test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:32:20:32:30 | ControlFlowNode for MyObj() | | test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:44:13:44:23 | ControlFlowNode for MyObj() | | test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:44:13:44:23 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:62:11:62:23 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:62:11:62:23 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:67:11:67:27 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:67:11:67:27 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:72:11:72:18 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:72:11:72:18 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:71:11:71:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:71:11:71:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:76:11:76:27 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:76:11:76:27 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:81:11:81:18 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:81:11:81:18 | ControlFlowNode for MyObj() | | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:32:20:32:30 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:44:13:44:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:27:20:27:22 | ControlFlowNode for foo | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | | test.py:27:20:27:22 | ControlFlowNode for foo | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | | test.py:30:1:30:24 | ControlFlowNode for ClassExpr | test.py:30:7:30:15 | GSSA Variable NestedObj | @@ -816,8 +877,12 @@ | test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:16 | SSA variable self | | test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | | test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | +| test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | +| test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | | test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | +| test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | | test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | +| test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj] | | test.py:32:20:32:30 | ControlFlowNode for MyObj() | test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj] | | test.py:32:20:32:30 | ControlFlowNode for MyObj() [Attribute foo] | test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | | test.py:32:20:32:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | @@ -829,8 +894,8 @@ | test.py:34:5:34:21 | ControlFlowNode for FunctionExpr | test.py:34:9:34:14 | SSA variable getObj | | test.py:34:16:34:19 | SSA variable self | test.py:35:16:35:19 | ControlFlowNode for self | | test.py:34:16:34:19 | SSA variable self | test.py:35:16:35:19 | ControlFlowNode for self | -| test.py:35:16:35:23 | ControlFlowNode for Attribute | test.py:56:5:56:14 | ControlFlowNode for Attribute() | -| test.py:35:16:35:23 | ControlFlowNode for Attribute | test.py:56:5:56:14 | ControlFlowNode for Attribute() | +| test.py:35:16:35:23 | ControlFlowNode for Attribute | test.py:65:5:65:14 | ControlFlowNode for Attribute() | +| test.py:35:16:35:23 | ControlFlowNode for Attribute | test.py:65:5:65:14 | ControlFlowNode for Attribute() | | test.py:38:1:38:19 | ControlFlowNode for FunctionExpr | test.py:38:5:38:10 | GSSA Variable setFoo | | test.py:38:1:38:19 | ControlFlowNode for FunctionExpr | test.py:38:5:38:10 | GSSA Variable setFoo | | test.py:38:1:38:19 | GSSA Variable SINK_F | test.py:39:5:39:10 | ControlFlowNode for SINK_F | @@ -929,213 +994,236 @@ | test.py:50:1:50:20 | ControlFlowNode for FunctionExpr | test.py:50:5:50:17 | GSSA Variable test_example2 | | test.py:50:1:50:20 | GSSA Variable NestedObj | test.py:53:9:53:17 | ControlFlowNode for NestedObj | | test.py:50:1:50:20 | GSSA Variable NestedObj | test.py:53:9:53:17 | ControlFlowNode for NestedObj | -| test.py:50:1:50:20 | GSSA Variable SINK | test.py:58:5:58:8 | ControlFlowNode for SINK | -| test.py:50:1:50:20 | GSSA Variable SINK | test.py:58:5:58:8 | ControlFlowNode for SINK | +| test.py:50:1:50:20 | GSSA Variable SINK | test.py:57:5:57:8 | ControlFlowNode for SINK | +| test.py:50:1:50:20 | GSSA Variable SINK | test.py:57:5:57:8 | ControlFlowNode for SINK | | test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:5:51:5 | SSA variable x | | test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:5:51:5 | SSA variable x | | test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:9:51:14 | ControlFlowNode for SOURCE | | test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:9:51:14 | ControlFlowNode for SOURCE | | test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | | test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:56:22:56:22 | ControlFlowNode for x | -| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:56:22:56:22 | ControlFlowNode for x | | test.py:51:5:51:5 | SSA variable x | test.py:55:17:55:17 | ControlFlowNode for x | | test.py:51:5:51:5 | SSA variable x | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:51:5:51:5 | SSA variable x | test.py:56:22:56:22 | ControlFlowNode for x | -| test.py:51:5:51:5 | SSA variable x | test.py:56:22:56:22 | ControlFlowNode for x | | test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:51:5:51:5 | SSA variable x | | test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:51:5:51:5 | SSA variable x | | test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | | test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:56:22:56:22 | ControlFlowNode for x | -| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:56:22:56:22 | ControlFlowNode for x | | test.py:53:5:53:5 | SSA variable a | test.py:55:5:55:5 | ControlFlowNode for a | | test.py:53:5:53:5 | SSA variable a | test.py:55:5:55:5 | ControlFlowNode for a | -| test.py:53:5:53:5 | SSA variable a | test.py:56:5:56:5 | ControlFlowNode for a | -| test.py:53:5:53:5 | SSA variable a | test.py:56:5:56:5 | ControlFlowNode for a | -| test.py:53:5:53:5 | SSA variable a | test.py:56:5:56:14 | SSA variable a | -| test.py:53:5:53:5 | SSA variable a | test.py:56:5:56:14 | SSA variable a | -| test.py:53:5:53:5 | SSA variable a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:53:5:53:5 | SSA variable a | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:53:5:53:5 | SSA variable a | test.py:57:10:57:10 | ControlFlowNode for a | +| test.py:53:5:53:5 | SSA variable a | test.py:57:10:57:10 | ControlFlowNode for a | | test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:53:5:53:5 | SSA variable a [Attribute obj] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | -| test.py:53:5:53:5 | SSA variable a [Attribute obj] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj] | -| test.py:53:5:53:5 | SSA variable a [Attribute obj] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | +| test.py:53:5:53:5 | SSA variable a [Attribute obj] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:5:53:5 | SSA variable a | | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:5:53:5 | SSA variable a | | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:55:5:55:5 | ControlFlowNode for a | | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:55:5:55:5 | ControlFlowNode for a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:56:5:56:5 | ControlFlowNode for a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:56:5:56:5 | ControlFlowNode for a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:56:5:56:14 | SSA variable a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:56:5:56:14 | SSA variable a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:58:10:58:10 | ControlFlowNode for a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:57:10:57:10 | ControlFlowNode for a | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:57:10:57:10 | ControlFlowNode for a | | test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | | test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:53:5:53:5 | SSA variable a [Attribute obj] | | test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj] | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | +| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | | test.py:53:9:53:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:31:18:31:21 | SSA variable self | | test.py:53:9:53:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:31:18:31:21 | SSA variable self | -| test.py:55:5:55:5 | ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | -| test.py:55:5:55:5 | ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | -| test.py:55:5:55:5 | ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:55:5:55:5 | ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:55:5:55:5 | ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | +| test.py:55:5:55:5 | ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | +| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:55:5:55:9 | ControlFlowNode for Attribute | | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:55:5:55:9 | ControlFlowNode for Attribute | -| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj] | -| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj] | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | +| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | | test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute | test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj] | | test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:55:17:55:17 | ControlFlowNode for x | test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | -| test.py:55:17:55:17 | ControlFlowNode for x | test.py:56:22:56:22 | ControlFlowNode for x | -| test.py:55:17:55:17 | ControlFlowNode for x | test.py:56:22:56:22 | ControlFlowNode for x | -| test.py:56:5:56:5 | ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:56:5:56:5 | ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:56:5:56:5 | ControlFlowNode for a [Attribute obj] | test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | -| test.py:56:5:56:5 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:56:5:56:5 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | -| test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | test.py:58:10:58:14 | ControlFlowNode for Attribute | -| test.py:58:10:58:10 | ControlFlowNode for a [Attribute obj] | test.py:58:10:58:14 | ControlFlowNode for Attribute | -| test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:58:10:58:18 | ControlFlowNode for Attribute | -| test.py:58:10:58:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:58:10:58:18 | ControlFlowNode for Attribute | -| test.py:58:10:58:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:58:10:58:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:61:1:61:20 | ControlFlowNode for FunctionExpr | test.py:61:5:61:17 | GSSA Variable test_example3 | -| test.py:61:1:61:20 | ControlFlowNode for FunctionExpr | test.py:61:5:61:17 | GSSA Variable test_example3 | -| test.py:61:1:61:20 | GSSA Variable MyObj | test.py:62:11:62:15 | ControlFlowNode for MyObj | -| test.py:61:1:61:20 | GSSA Variable MyObj | test.py:62:11:62:15 | ControlFlowNode for MyObj | -| test.py:61:1:61:20 | GSSA Variable SINK | test.py:63:5:63:8 | ControlFlowNode for SINK | -| test.py:61:1:61:20 | GSSA Variable SINK | test.py:63:5:63:8 | ControlFlowNode for SINK | -| test.py:61:1:61:20 | GSSA Variable SOURCE | test.py:62:17:62:22 | ControlFlowNode for SOURCE | -| test.py:61:1:61:20 | GSSA Variable SOURCE | test.py:62:17:62:22 | ControlFlowNode for SOURCE | -| test.py:62:5:62:7 | SSA variable obj | test.py:63:10:63:12 | ControlFlowNode for obj | -| test.py:62:5:62:7 | SSA variable obj | test.py:63:10:63:12 | ControlFlowNode for obj | -| test.py:62:5:62:7 | SSA variable obj [Attribute foo] | test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:62:5:62:7 | SSA variable obj | -| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:62:5:62:7 | SSA variable obj | -| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:63:10:63:12 | ControlFlowNode for obj | -| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:63:10:63:12 | ControlFlowNode for obj | -| test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:62:5:62:7 | SSA variable obj [Attribute foo] | -| test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:62:11:62:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:62:11:62:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | -| test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | -| test.py:62:17:62:22 | ControlFlowNode for SOURCE | test.py:62:11:62:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | test.py:63:10:63:16 | ControlFlowNode for Attribute | -| test.py:63:10:63:12 | ControlFlowNode for obj [Attribute foo] | test.py:63:10:63:16 | ControlFlowNode for Attribute | -| test.py:63:10:63:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:63:10:63:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:66:1:66:23 | ControlFlowNode for FunctionExpr | test.py:66:5:66:20 | GSSA Variable test_example3_kw | -| test.py:66:1:66:23 | ControlFlowNode for FunctionExpr | test.py:66:5:66:20 | GSSA Variable test_example3_kw | -| test.py:66:1:66:23 | GSSA Variable MyObj | test.py:67:11:67:15 | ControlFlowNode for MyObj | -| test.py:66:1:66:23 | GSSA Variable MyObj | test.py:67:11:67:15 | ControlFlowNode for MyObj | -| test.py:66:1:66:23 | GSSA Variable SINK | test.py:68:5:68:8 | ControlFlowNode for SINK | -| test.py:66:1:66:23 | GSSA Variable SINK | test.py:68:5:68:8 | ControlFlowNode for SINK | -| test.py:66:1:66:23 | GSSA Variable SOURCE | test.py:67:21:67:26 | ControlFlowNode for SOURCE | -| test.py:66:1:66:23 | GSSA Variable SOURCE | test.py:67:21:67:26 | ControlFlowNode for SOURCE | -| test.py:67:5:67:7 | SSA variable obj | test.py:68:10:68:12 | ControlFlowNode for obj | -| test.py:67:5:67:7 | SSA variable obj | test.py:68:10:68:12 | ControlFlowNode for obj | -| test.py:67:5:67:7 | SSA variable obj [Attribute foo] | test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:67:5:67:7 | SSA variable obj | -| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:67:5:67:7 | SSA variable obj | -| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:68:10:68:12 | ControlFlowNode for obj | -| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:68:10:68:12 | ControlFlowNode for obj | -| test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:67:5:67:7 | SSA variable obj [Attribute foo] | -| test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:67:11:67:27 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:67:11:67:27 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:67:21:67:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | -| test.py:67:21:67:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | -| test.py:67:21:67:26 | ControlFlowNode for SOURCE | test.py:67:11:67:27 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | test.py:68:10:68:16 | ControlFlowNode for Attribute | -| test.py:68:10:68:12 | ControlFlowNode for obj [Attribute foo] | test.py:68:10:68:16 | ControlFlowNode for Attribute | -| test.py:68:10:68:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:68:10:68:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:71:1:71:30 | ControlFlowNode for FunctionExpr | test.py:71:5:71:26 | GSSA Variable fields_with_local_flow | -| test.py:71:1:71:30 | ControlFlowNode for FunctionExpr | test.py:71:5:71:26 | GSSA Variable fields_with_local_flow | -| test.py:71:1:71:30 | GSSA Variable MyObj | test.py:72:11:72:15 | ControlFlowNode for MyObj | -| test.py:71:1:71:30 | GSSA Variable MyObj | test.py:72:11:72:15 | ControlFlowNode for MyObj | -| test.py:71:5:71:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | -| test.py:71:5:71:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | -| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | -| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | -| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | -| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | -| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | -| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | -| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | -| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | -| test.py:72:5:72:7 | SSA variable obj | test.py:73:9:73:11 | ControlFlowNode for obj | -| test.py:72:5:72:7 | SSA variable obj | test.py:73:9:73:11 | ControlFlowNode for obj | -| test.py:72:5:72:7 | SSA variable obj [Attribute foo] | test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:72:5:72:7 | SSA variable obj [Attribute foo] | test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:72:5:72:7 | SSA variable obj | -| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:72:5:72:7 | SSA variable obj | -| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:73:9:73:11 | ControlFlowNode for obj | -| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:73:9:73:11 | ControlFlowNode for obj | -| test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:72:5:72:7 | SSA variable obj [Attribute foo] | -| test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:72:5:72:7 | SSA variable obj [Attribute foo] | -| test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:72:11:72:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:72:11:72:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:72:17:72:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | -| test.py:72:17:72:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | -| test.py:72:17:72:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | -| test.py:72:17:72:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | -| test.py:72:17:72:17 | ControlFlowNode for x | test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:72:17:72:17 | ControlFlowNode for x | test.py:72:11:72:18 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:72:17:72:17 | [post arg] ControlFlowNode for x | test.py:78:33:78:38 | [post arg] ControlFlowNode for SOURCE | -| test.py:72:17:72:17 | [post arg] ControlFlowNode for x | test.py:78:33:78:38 | [post arg] ControlFlowNode for SOURCE | -| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | -| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | -| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | -| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | -| test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | test.py:73:9:73:15 | ControlFlowNode for Attribute | -| test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | test.py:73:9:73:15 | ControlFlowNode for Attribute | -| test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | test.py:73:9:73:15 | ControlFlowNode for Attribute | -| test.py:73:9:73:11 | ControlFlowNode for obj [Attribute foo] | test.py:73:9:73:15 | ControlFlowNode for Attribute | -| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | -| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | -| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | -| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | -| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:74:12:74:12 | ControlFlowNode for a | -| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:74:12:74:12 | ControlFlowNode for a | -| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:74:12:74:12 | ControlFlowNode for a | -| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:74:12:74:12 | ControlFlowNode for a | -| test.py:74:12:74:12 | ControlFlowNode for a | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:74:12:74:12 | ControlFlowNode for a | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:77:1:77:18 | ControlFlowNode for FunctionExpr | test.py:77:5:77:15 | GSSA Variable test_fields | -| test.py:77:1:77:18 | ControlFlowNode for FunctionExpr | test.py:77:5:77:15 | GSSA Variable test_fields | -| test.py:77:1:77:18 | GSSA Variable SINK | test.py:78:5:78:8 | ControlFlowNode for SINK | -| test.py:77:1:77:18 | GSSA Variable SINK | test.py:78:5:78:8 | ControlFlowNode for SINK | -| test.py:77:1:77:18 | GSSA Variable SOURCE | test.py:78:33:78:38 | ControlFlowNode for SOURCE | -| test.py:77:1:77:18 | GSSA Variable SOURCE | test.py:78:33:78:38 | ControlFlowNode for SOURCE | -| test.py:77:1:77:18 | GSSA Variable fields_with_local_flow | test.py:78:10:78:31 | ControlFlowNode for fields_with_local_flow | -| test.py:77:1:77:18 | GSSA Variable fields_with_local_flow | test.py:78:10:78:31 | ControlFlowNode for fields_with_local_flow | -| test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | test.py:10:10:10:10 | SSA variable x | -| test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | test.py:10:10:10:10 | SSA variable x | -| test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:71:28:71:28 | SSA variable x | -| test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:71:28:71:28 | SSA variable x | -| test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:78:33:78:38 | ControlFlowNode for SOURCE | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | +| test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | test.py:57:10:57:14 | ControlFlowNode for Attribute | +| test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | test.py:57:10:57:14 | ControlFlowNode for Attribute | +| test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute | +| test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute | +| test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:60:1:60:27 | ControlFlowNode for FunctionExpr | test.py:60:5:60:24 | GSSA Variable test_example2_method | +| test.py:60:1:60:27 | ControlFlowNode for FunctionExpr | test.py:60:5:60:24 | GSSA Variable test_example2_method | +| test.py:60:1:60:27 | GSSA Variable NestedObj | test.py:63:9:63:17 | ControlFlowNode for NestedObj | +| test.py:60:1:60:27 | GSSA Variable NestedObj | test.py:63:9:63:17 | ControlFlowNode for NestedObj | +| test.py:60:1:60:27 | GSSA Variable SINK | test.py:67:5:67:8 | ControlFlowNode for SINK | +| test.py:60:1:60:27 | GSSA Variable SINK | test.py:67:5:67:8 | ControlFlowNode for SINK | +| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:61:5:61:5 | SSA variable x | +| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:61:5:61:5 | SSA variable x | +| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:65:22:65:22 | ControlFlowNode for x | +| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:65:22:65:22 | ControlFlowNode for x | +| test.py:61:5:61:5 | SSA variable x | test.py:65:22:65:22 | ControlFlowNode for x | +| test.py:61:5:61:5 | SSA variable x | test.py:65:22:65:22 | ControlFlowNode for x | +| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:61:5:61:5 | SSA variable x | +| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:61:5:61:5 | SSA variable x | +| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:65:22:65:22 | ControlFlowNode for x | +| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:65:22:65:22 | ControlFlowNode for x | +| test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:5 | ControlFlowNode for a | +| test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:5 | ControlFlowNode for a | +| test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:14 | SSA variable a | +| test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:14 | SSA variable a | +| test.py:63:5:63:5 | SSA variable a | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:63:5:63:5 | SSA variable a | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:63:5:63:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:63:5:63:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:63:5:63:5 | SSA variable a [Attribute obj] | test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj] | +| test.py:63:5:63:5 | SSA variable a [Attribute obj] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj] | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:63:5:63:5 | SSA variable a | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:63:5:63:5 | SSA variable a | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:65:5:65:5 | ControlFlowNode for a | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:65:5:65:5 | ControlFlowNode for a | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:65:5:65:14 | SSA variable a | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:65:5:65:14 | SSA variable a | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:63:5:63:5 | SSA variable a [Attribute obj, Attribute foo] | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:63:5:63:5 | SSA variable a [Attribute obj] | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj] | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj] | +| test.py:63:9:63:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:31:18:31:21 | SSA variable self | +| test.py:63:9:63:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:31:18:31:21 | SSA variable self | +| test.py:65:5:65:5 | ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:65:5:65:5 | ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj] | +| test.py:65:5:65:5 | [post read] ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:65:5:65:5 | [post read] ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | +| test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj] | test.py:67:10:67:14 | ControlFlowNode for Attribute | +| test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj] | test.py:67:10:67:14 | ControlFlowNode for Attribute | +| test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:67:10:67:18 | ControlFlowNode for Attribute | +| test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:67:10:67:18 | ControlFlowNode for Attribute | +| test.py:67:10:67:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:67:10:67:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:70:1:70:20 | ControlFlowNode for FunctionExpr | test.py:70:5:70:17 | GSSA Variable test_example3 | +| test.py:70:1:70:20 | ControlFlowNode for FunctionExpr | test.py:70:5:70:17 | GSSA Variable test_example3 | +| test.py:70:1:70:20 | GSSA Variable MyObj | test.py:71:11:71:15 | ControlFlowNode for MyObj | +| test.py:70:1:70:20 | GSSA Variable MyObj | test.py:71:11:71:15 | ControlFlowNode for MyObj | +| test.py:70:1:70:20 | GSSA Variable SINK | test.py:72:5:72:8 | ControlFlowNode for SINK | +| test.py:70:1:70:20 | GSSA Variable SINK | test.py:72:5:72:8 | ControlFlowNode for SINK | +| test.py:70:1:70:20 | GSSA Variable SOURCE | test.py:71:17:71:22 | ControlFlowNode for SOURCE | +| test.py:70:1:70:20 | GSSA Variable SOURCE | test.py:71:17:71:22 | ControlFlowNode for SOURCE | +| test.py:71:5:71:7 | SSA variable obj | test.py:72:10:72:12 | ControlFlowNode for obj | +| test.py:71:5:71:7 | SSA variable obj | test.py:72:10:72:12 | ControlFlowNode for obj | +| test.py:71:5:71:7 | SSA variable obj [Attribute foo] | test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:71:5:71:7 | SSA variable obj | +| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:71:5:71:7 | SSA variable obj | +| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:72:10:72:12 | ControlFlowNode for obj | +| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:72:10:72:12 | ControlFlowNode for obj | +| test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:71:5:71:7 | SSA variable obj [Attribute foo] | +| test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:71:11:71:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:71:11:71:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:71:17:71:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:71:17:71:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:71:17:71:22 | ControlFlowNode for SOURCE | test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | test.py:72:10:72:16 | ControlFlowNode for Attribute | +| test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | test.py:72:10:72:16 | ControlFlowNode for Attribute | +| test.py:72:10:72:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:72:10:72:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:75:1:75:23 | ControlFlowNode for FunctionExpr | test.py:75:5:75:20 | GSSA Variable test_example3_kw | +| test.py:75:1:75:23 | ControlFlowNode for FunctionExpr | test.py:75:5:75:20 | GSSA Variable test_example3_kw | +| test.py:75:1:75:23 | GSSA Variable MyObj | test.py:76:11:76:15 | ControlFlowNode for MyObj | +| test.py:75:1:75:23 | GSSA Variable MyObj | test.py:76:11:76:15 | ControlFlowNode for MyObj | +| test.py:75:1:75:23 | GSSA Variable SINK | test.py:77:5:77:8 | ControlFlowNode for SINK | +| test.py:75:1:75:23 | GSSA Variable SINK | test.py:77:5:77:8 | ControlFlowNode for SINK | +| test.py:75:1:75:23 | GSSA Variable SOURCE | test.py:76:21:76:26 | ControlFlowNode for SOURCE | +| test.py:75:1:75:23 | GSSA Variable SOURCE | test.py:76:21:76:26 | ControlFlowNode for SOURCE | +| test.py:76:5:76:7 | SSA variable obj | test.py:77:10:77:12 | ControlFlowNode for obj | +| test.py:76:5:76:7 | SSA variable obj | test.py:77:10:77:12 | ControlFlowNode for obj | +| test.py:76:5:76:7 | SSA variable obj [Attribute foo] | test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:76:5:76:7 | SSA variable obj | +| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:76:5:76:7 | SSA variable obj | +| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:77:10:77:12 | ControlFlowNode for obj | +| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:77:10:77:12 | ControlFlowNode for obj | +| test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:76:5:76:7 | SSA variable obj [Attribute foo] | +| test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:76:11:76:27 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:76:11:76:27 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:76:21:76:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:76:21:76:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:76:21:76:26 | ControlFlowNode for SOURCE | test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | test.py:77:10:77:16 | ControlFlowNode for Attribute | +| test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | test.py:77:10:77:16 | ControlFlowNode for Attribute | +| test.py:77:10:77:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:77:10:77:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:80:1:80:30 | ControlFlowNode for FunctionExpr | test.py:80:5:80:26 | GSSA Variable fields_with_local_flow | +| test.py:80:1:80:30 | ControlFlowNode for FunctionExpr | test.py:80:5:80:26 | GSSA Variable fields_with_local_flow | +| test.py:80:1:80:30 | GSSA Variable MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | +| test.py:80:1:80:30 | GSSA Variable MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | +| test.py:80:5:80:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | +| test.py:80:5:80:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | +| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | +| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | +| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | +| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | +| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | +| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | +| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | +| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | +| test.py:81:5:81:7 | SSA variable obj | test.py:82:9:82:11 | ControlFlowNode for obj | +| test.py:81:5:81:7 | SSA variable obj | test.py:82:9:82:11 | ControlFlowNode for obj | +| test.py:81:5:81:7 | SSA variable obj [Attribute foo] | test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:81:5:81:7 | SSA variable obj [Attribute foo] | test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:81:5:81:7 | SSA variable obj | +| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:81:5:81:7 | SSA variable obj | +| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:82:9:82:11 | ControlFlowNode for obj | +| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:82:9:82:11 | ControlFlowNode for obj | +| test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:81:5:81:7 | SSA variable obj [Attribute foo] | +| test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:81:5:81:7 | SSA variable obj [Attribute foo] | +| test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:81:11:81:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:81:11:81:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:81:17:81:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:81:17:81:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:81:17:81:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:81:17:81:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:81:17:81:17 | ControlFlowNode for x | test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:81:17:81:17 | ControlFlowNode for x | test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:81:17:81:17 | [post arg] ControlFlowNode for x | test.py:87:33:87:38 | [post arg] ControlFlowNode for SOURCE | +| test.py:81:17:81:17 | [post arg] ControlFlowNode for x | test.py:87:33:87:38 | [post arg] ControlFlowNode for SOURCE | +| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | +| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | +| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | +| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | +| test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | test.py:82:9:82:15 | ControlFlowNode for Attribute | +| test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | test.py:82:9:82:15 | ControlFlowNode for Attribute | +| test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | test.py:82:9:82:15 | ControlFlowNode for Attribute | +| test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | test.py:82:9:82:15 | ControlFlowNode for Attribute | +| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | +| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | +| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | +| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | +| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:83:12:83:12 | ControlFlowNode for a | +| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:83:12:83:12 | ControlFlowNode for a | +| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:83:12:83:12 | ControlFlowNode for a | +| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:83:12:83:12 | ControlFlowNode for a | +| test.py:83:12:83:12 | ControlFlowNode for a | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:83:12:83:12 | ControlFlowNode for a | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:86:1:86:18 | ControlFlowNode for FunctionExpr | test.py:86:5:86:15 | GSSA Variable test_fields | +| test.py:86:1:86:18 | ControlFlowNode for FunctionExpr | test.py:86:5:86:15 | GSSA Variable test_fields | +| test.py:86:1:86:18 | GSSA Variable SINK | test.py:87:5:87:8 | ControlFlowNode for SINK | +| test.py:86:1:86:18 | GSSA Variable SINK | test.py:87:5:87:8 | ControlFlowNode for SINK | +| test.py:86:1:86:18 | GSSA Variable SOURCE | test.py:87:33:87:38 | ControlFlowNode for SOURCE | +| test.py:86:1:86:18 | GSSA Variable SOURCE | test.py:87:33:87:38 | ControlFlowNode for SOURCE | +| test.py:86:1:86:18 | GSSA Variable fields_with_local_flow | test.py:87:10:87:31 | ControlFlowNode for fields_with_local_flow | +| test.py:86:1:86:18 | GSSA Variable fields_with_local_flow | test.py:87:10:87:31 | ControlFlowNode for fields_with_local_flow | +| test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | test.py:10:10:10:10 | SSA variable x | +| test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | test.py:10:10:10:10 | SSA variable x | +| test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:80:28:80:28 | SSA variable x | +| test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:80:28:80:28 | SSA variable x | +| test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected index e789b06925b..9fd5d5ee7a3 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected @@ -1,14 +1,14 @@ -| examples.py:45:1:45:30 | GSSA Variable MyObj | examples.py:46:9:46:13 | ControlFlowNode for MyObj | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:9:46:16 | SSA variable x | -| examples.py:45:28:45:28 | SSA variable x | examples.py:46:15:46:15 | ControlFlowNode for x | -| examples.py:46:3:46:5 | SSA variable obj | examples.py:47:7:47:9 | ControlFlowNode for obj | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:3:46:5 | SSA variable obj | -| examples.py:47:3:47:3 | SSA variable a | examples.py:48:10:48:10 | ControlFlowNode for a | -| examples.py:47:7:47:13 | ControlFlowNode for Attribute | examples.py:47:3:47:3 | SSA variable a | -| test.py:71:1:71:30 | GSSA Variable MyObj | test.py:72:11:72:15 | ControlFlowNode for MyObj | -| test.py:71:28:71:28 | SSA variable x | test.py:72:11:72:18 | SSA variable x | -| test.py:71:28:71:28 | SSA variable x | test.py:72:17:72:17 | ControlFlowNode for x | -| test.py:72:5:72:7 | SSA variable obj | test.py:73:9:73:11 | ControlFlowNode for obj | -| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:72:5:72:7 | SSA variable obj | -| test.py:73:5:73:5 | SSA variable a | test.py:74:12:74:12 | ControlFlowNode for a | -| test.py:73:9:73:15 | ControlFlowNode for Attribute | test.py:73:5:73:5 | SSA variable a | +| examples.py:53:1:53:30 | GSSA Variable MyObj | examples.py:54:11:54:15 | ControlFlowNode for MyObj | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:11:54:18 | SSA variable x | +| examples.py:53:28:53:28 | SSA variable x | examples.py:54:17:54:17 | ControlFlowNode for x | +| examples.py:54:5:54:7 | SSA variable obj | examples.py:55:9:55:11 | ControlFlowNode for obj | +| examples.py:54:11:54:18 | ControlFlowNode for MyObj() | examples.py:54:5:54:7 | SSA variable obj | +| examples.py:55:5:55:5 | SSA variable a | examples.py:56:12:56:12 | ControlFlowNode for a | +| examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:55:5:55:5 | SSA variable a | +| test.py:80:1:80:30 | GSSA Variable MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | +| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | +| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | +| test.py:81:5:81:7 | SSA variable obj | test.py:82:9:82:11 | ControlFlowNode for obj | +| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:81:5:81:7 | SSA variable obj | +| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | +| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected index a096f48f0f2..71daa2a0b78 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected @@ -1,10 +1,10 @@ | examples.py:8:9:8:12 | [post store] ControlFlowNode for self | examples.py:8:9:8:12 | ControlFlowNode for self | -| examples.py:14:9:14:12 | [post store] ControlFlowNode for self | examples.py:14:9:14:12 | ControlFlowNode for self | -| examples.py:14:20:14:30 | ControlFlowNode for MyObj() | examples.py:14:20:14:30 | [pre objCreate] ControlFlowNode for MyObj() | -| examples.py:14:26:14:29 | [post arg] ControlFlowNode for Str | examples.py:14:26:14:29 | ControlFlowNode for Str | -| examples.py:17:16:17:19 | [post read] ControlFlowNode for self | examples.py:17:16:17:19 | ControlFlowNode for self | -| examples.py:22:12:22:14 | [post read] ControlFlowNode for obj | examples.py:22:12:22:14 | ControlFlowNode for obj | -| examples.py:23:5:23:7 | [post store] ControlFlowNode for obj | examples.py:23:5:23:7 | ControlFlowNode for obj | +| examples.py:13:9:13:12 | [post store] ControlFlowNode for self | examples.py:13:9:13:12 | ControlFlowNode for self | +| examples.py:13:20:13:30 | ControlFlowNode for MyObj() | examples.py:13:20:13:30 | [pre objCreate] ControlFlowNode for MyObj() | +| examples.py:13:26:13:29 | [post arg] ControlFlowNode for Str | examples.py:13:26:13:29 | ControlFlowNode for Str | +| examples.py:16:16:16:19 | [post read] ControlFlowNode for self | examples.py:16:16:16:19 | ControlFlowNode for self | +| examples.py:21:12:21:14 | [post read] ControlFlowNode for obj | examples.py:21:12:21:14 | ControlFlowNode for obj | +| examples.py:22:5:22:7 | [post store] ControlFlowNode for obj | examples.py:22:5:22:7 | ControlFlowNode for obj | | examples.py:25:9:25:19 | ControlFlowNode for MyObj() | examples.py:25:9:25:19 | [pre objCreate] ControlFlowNode for MyObj() | | examples.py:25:15:25:18 | [post arg] ControlFlowNode for Str | examples.py:25:15:25:18 | ControlFlowNode for Str | | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj | examples.py:27:8:27:12 | ControlFlowNode for myobj | @@ -13,17 +13,20 @@ | examples.py:33:5:33:15 | ControlFlowNode for NestedObj() | examples.py:33:5:33:15 | [pre objCreate] ControlFlowNode for NestedObj() | | examples.py:35:1:35:1 | [post read] ControlFlowNode for a | examples.py:35:1:35:1 | ControlFlowNode for a | | examples.py:35:1:35:5 | [post store] ControlFlowNode for Attribute | examples.py:35:1:35:5 | ControlFlowNode for Attribute | -| examples.py:36:1:36:1 | [post read] ControlFlowNode for a | examples.py:36:1:36:1 | ControlFlowNode for a | -| examples.py:36:1:36:10 | [post store] ControlFlowNode for Attribute() | examples.py:36:1:36:10 | ControlFlowNode for Attribute() | -| examples.py:38:6:38:6 | [post read] ControlFlowNode for a | examples.py:38:6:38:6 | ControlFlowNode for a | -| examples.py:38:6:38:10 | [post read] ControlFlowNode for Attribute | examples.py:38:6:38:10 | ControlFlowNode for Attribute | -| examples.py:41:7:41:19 | ControlFlowNode for MyObj() | examples.py:41:7:41:19 | [pre objCreate] ControlFlowNode for MyObj() | -| examples.py:41:13:41:18 | [post arg] ControlFlowNode for SOURCE | examples.py:41:13:41:18 | ControlFlowNode for SOURCE | -| examples.py:42:6:42:8 | [post read] ControlFlowNode for obj | examples.py:42:6:42:8 | ControlFlowNode for obj | -| examples.py:46:9:46:16 | ControlFlowNode for MyObj() | examples.py:46:9:46:16 | [pre objCreate] ControlFlowNode for MyObj() | -| examples.py:46:15:46:15 | [post arg] ControlFlowNode for x | examples.py:46:15:46:15 | ControlFlowNode for x | -| examples.py:47:7:47:9 | [post read] ControlFlowNode for obj | examples.py:47:7:47:9 | ControlFlowNode for obj | -| examples.py:50:29:50:34 | [post arg] ControlFlowNode for SOURCE | examples.py:50:29:50:34 | ControlFlowNode for SOURCE | +| examples.py:37:6:37:6 | [post read] ControlFlowNode for a | examples.py:37:6:37:6 | ControlFlowNode for a | +| examples.py:37:6:37:10 | [post read] ControlFlowNode for Attribute | examples.py:37:6:37:10 | ControlFlowNode for Attribute | +| examples.py:42:5:42:15 | ControlFlowNode for NestedObj() | examples.py:42:5:42:15 | [pre objCreate] ControlFlowNode for NestedObj() | +| examples.py:44:1:44:1 | [post read] ControlFlowNode for a | examples.py:44:1:44:1 | ControlFlowNode for a | +| examples.py:44:1:44:10 | [post store] ControlFlowNode for Attribute() | examples.py:44:1:44:10 | ControlFlowNode for Attribute() | +| examples.py:46:6:46:6 | [post read] ControlFlowNode for a | examples.py:46:6:46:6 | ControlFlowNode for a | +| examples.py:46:6:46:10 | [post read] ControlFlowNode for Attribute | examples.py:46:6:46:10 | ControlFlowNode for Attribute | +| examples.py:49:7:49:19 | ControlFlowNode for MyObj() | examples.py:49:7:49:19 | [pre objCreate] ControlFlowNode for MyObj() | +| examples.py:49:13:49:18 | [post arg] ControlFlowNode for SOURCE | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | +| examples.py:50:6:50:8 | [post read] ControlFlowNode for obj | examples.py:50:6:50:8 | ControlFlowNode for obj | +| examples.py:54:11:54:18 | ControlFlowNode for MyObj() | examples.py:54:11:54:18 | [pre objCreate] ControlFlowNode for MyObj() | +| examples.py:54:17:54:17 | [post arg] ControlFlowNode for x | examples.py:54:17:54:17 | ControlFlowNode for x | +| examples.py:55:9:55:11 | [post read] ControlFlowNode for obj | examples.py:55:9:55:11 | ControlFlowNode for obj | +| examples.py:59:29:59:34 | [post arg] ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | | test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:11:18:11:18 | ControlFlowNode for x | | test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:18:18:18:18 | ControlFlowNode for x | | test.py:19:15:19:31 | [post arg] ControlFlowNode for Str | test.py:19:15:19:31 | ControlFlowNode for Str | @@ -46,21 +49,25 @@ | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:9:53:19 | [pre objCreate] ControlFlowNode for NestedObj() | | test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:55:5:55:5 | ControlFlowNode for a | | test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute | test.py:55:5:55:9 | ControlFlowNode for Attribute | -| test.py:56:5:56:5 | [post read] ControlFlowNode for a | test.py:56:5:56:5 | ControlFlowNode for a | -| test.py:56:5:56:14 | [post store] ControlFlowNode for Attribute() | test.py:56:5:56:14 | ControlFlowNode for Attribute() | -| test.py:58:10:58:10 | [post read] ControlFlowNode for a | test.py:58:10:58:10 | ControlFlowNode for a | -| test.py:58:10:58:14 | [post read] ControlFlowNode for Attribute | test.py:58:10:58:14 | ControlFlowNode for Attribute | -| test.py:58:10:58:18 | [post arg] ControlFlowNode for Attribute | test.py:58:10:58:18 | ControlFlowNode for Attribute | -| test.py:62:11:62:23 | ControlFlowNode for MyObj() | test.py:62:11:62:23 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:62:17:62:22 | [post arg] ControlFlowNode for SOURCE | test.py:62:17:62:22 | ControlFlowNode for SOURCE | -| test.py:63:10:63:12 | [post read] ControlFlowNode for obj | test.py:63:10:63:12 | ControlFlowNode for obj | -| test.py:63:10:63:16 | [post arg] ControlFlowNode for Attribute | test.py:63:10:63:16 | ControlFlowNode for Attribute | -| test.py:67:11:67:27 | ControlFlowNode for MyObj() | test.py:67:11:67:27 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:67:21:67:26 | [post arg] ControlFlowNode for SOURCE | test.py:67:21:67:26 | ControlFlowNode for SOURCE | -| test.py:68:10:68:12 | [post read] ControlFlowNode for obj | test.py:68:10:68:12 | ControlFlowNode for obj | -| test.py:68:10:68:16 | [post arg] ControlFlowNode for Attribute | test.py:68:10:68:16 | ControlFlowNode for Attribute | -| test.py:72:11:72:18 | ControlFlowNode for MyObj() | test.py:72:11:72:18 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:72:17:72:17 | [post arg] ControlFlowNode for x | test.py:72:17:72:17 | ControlFlowNode for x | -| test.py:73:9:73:11 | [post read] ControlFlowNode for obj | test.py:73:9:73:11 | ControlFlowNode for obj | -| test.py:78:10:78:39 | [post arg] ControlFlowNode for fields_with_local_flow() | test.py:78:10:78:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:78:33:78:38 | [post arg] ControlFlowNode for SOURCE | test.py:78:33:78:38 | ControlFlowNode for SOURCE | +| test.py:57:10:57:10 | [post read] ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | +| test.py:57:10:57:14 | [post read] ControlFlowNode for Attribute | test.py:57:10:57:14 | ControlFlowNode for Attribute | +| test.py:57:10:57:18 | [post arg] ControlFlowNode for Attribute | test.py:57:10:57:18 | ControlFlowNode for Attribute | +| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:63:9:63:19 | [pre objCreate] ControlFlowNode for NestedObj() | +| test.py:65:5:65:5 | [post read] ControlFlowNode for a | test.py:65:5:65:5 | ControlFlowNode for a | +| test.py:65:5:65:14 | [post store] ControlFlowNode for Attribute() | test.py:65:5:65:14 | ControlFlowNode for Attribute() | +| test.py:67:10:67:10 | [post read] ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:67:10:67:14 | [post read] ControlFlowNode for Attribute | test.py:67:10:67:14 | ControlFlowNode for Attribute | +| test.py:67:10:67:18 | [post arg] ControlFlowNode for Attribute | test.py:67:10:67:18 | ControlFlowNode for Attribute | +| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:71:11:71:23 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:71:17:71:22 | [post arg] ControlFlowNode for SOURCE | test.py:71:17:71:22 | ControlFlowNode for SOURCE | +| test.py:72:10:72:12 | [post read] ControlFlowNode for obj | test.py:72:10:72:12 | ControlFlowNode for obj | +| test.py:72:10:72:16 | [post arg] ControlFlowNode for Attribute | test.py:72:10:72:16 | ControlFlowNode for Attribute | +| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:76:11:76:27 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:76:21:76:26 | [post arg] ControlFlowNode for SOURCE | test.py:76:21:76:26 | ControlFlowNode for SOURCE | +| test.py:77:10:77:12 | [post read] ControlFlowNode for obj | test.py:77:10:77:12 | ControlFlowNode for obj | +| test.py:77:10:77:16 | [post arg] ControlFlowNode for Attribute | test.py:77:10:77:16 | ControlFlowNode for Attribute | +| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:81:11:81:18 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:81:17:81:17 | [post arg] ControlFlowNode for x | test.py:81:17:81:17 | ControlFlowNode for x | +| test.py:82:9:82:11 | [post read] ControlFlowNode for obj | test.py:82:9:82:11 | ControlFlowNode for obj | +| test.py:87:10:87:39 | [post arg] ControlFlowNode for fields_with_local_flow() | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:87:33:87:38 | [post arg] ControlFlowNode for SOURCE | test.py:87:33:87:38 | ControlFlowNode for SOURCE | diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py index aaf8b1575c6..d5d613d53c0 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/test.py +++ b/python/ql/test/experimental/dataflow/fieldflow/test.py @@ -53,11 +53,20 @@ def test_example2(): a = NestedObj() a.obj.foo = x - a.getObj().foo = x SINK(a.obj.foo) +def test_example2_method(): + x = SOURCE + + a = NestedObj() + + a.getObj().foo = x + + SINK(a.obj.foo) # Flow missing + + def test_example3(): obj = MyObj(SOURCE) SINK(obj.foo) From ab3772eaeb5d7740373e5a0885c6b8a481cfb923 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Thu, 1 Oct 2020 15:38:56 -0400 Subject: [PATCH 021/166] Update JHipster CodeQL query from code review --- .../CWE/CWE-338/JHipsterGeneratedPRNG.ql | 26 ++++++++++--------- .../tests/JHipsterGeneratedPRNG.expected | 10 +++---- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql index 9717b9ce7e0..b184b5d047f 100644 --- a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql +++ b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql @@ -14,20 +14,23 @@ import semmle.code.java.frameworks.apache.Lang private class PredictableApacheRandomStringUtilsMethod extends Method { PredictableApacheRandomStringUtilsMethod() { - this.getDeclaringType() instanceof TypeApacheRandomStringUtils + this.getDeclaringType() instanceof TypeApacheRandomStringUtils and + // The one valid use of this type that uses SecureRandom as a source of data. + not this.getName() = "random" } } private class PredictableApacheRandomStringUtilsMethodAccess extends MethodAccess { PredictableApacheRandomStringUtilsMethodAccess() { - this.getMethod() instanceof PredictableApacheRandomStringUtilsMethod and - // The one valid use of this type that uses SecureRandom as a source of data. - not this.getMethod().getName() = "random" + this.getMethod() instanceof PredictableApacheRandomStringUtilsMethod } } private class VulnerableJHipsterRandomUtilClass extends Class { - VulnerableJHipsterRandomUtilClass() { getName() = "RandomUtil" } + VulnerableJHipsterRandomUtilClass() { + // The package name that JHipster generated the 'RandomUtil' class in was dynamic. Thus 'hasQualifiedName' can not be used here. + getName() = "RandomUtil" + } } private class VulnerableJHipsterRandomUtilMethod extends Method { @@ -35,14 +38,13 @@ private class VulnerableJHipsterRandomUtilMethod extends Method { this.getDeclaringType() instanceof VulnerableJHipsterRandomUtilClass and this.getName().matches("generate%") and this.getReturnType() instanceof TypeString and - exists(ReturnStmt s, PredictableApacheRandomStringUtilsMethodAccess access | - s = this.getBody().(SingletonBlock).getStmt() - | - s.getResult() = access + exists(ReturnStmt s | + s = this.getBody().(SingletonBlock).getStmt() and + s.getResult() instanceof PredictableApacheRandomStringUtilsMethodAccess ) } } -from VulnerableJHipsterRandomUtilMethod the_method -select the_method, - "RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303" +from VulnerableJHipsterRandomUtilMethod method +select method, + "Weak random number generator used in security sensitive method (JHipster CVE-2019-16303)." diff --git a/java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.expected b/java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.expected index 7234f316b6e..0a2e98cc7cb 100644 --- a/java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.expected +++ b/java/ql/test/query-tests/security/CWE-338/semmle/tests/JHipsterGeneratedPRNG.expected @@ -1,5 +1,5 @@ -| vulnerable/RandomUtil.java:20:26:20:41 | generatePassword | RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303 | -| vulnerable/RandomUtil.java:29:26:29:46 | generateActivationKey | RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303 | -| vulnerable/RandomUtil.java:38:26:38:41 | generateResetKey | RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303 | -| vulnerable/RandomUtil.java:48:26:48:43 | generateSeriesData | RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303 | -| vulnerable/RandomUtil.java:57:26:57:42 | generateTokenData | RandomUtil was generated by JHipster Generator version vulnerable to CVE-2019-16303 | +| vulnerable/RandomUtil.java:20:26:20:41 | generatePassword | Weak random number generator used in security sensitive method (JHipster CVE-2019-16303). | +| vulnerable/RandomUtil.java:29:26:29:46 | generateActivationKey | Weak random number generator used in security sensitive method (JHipster CVE-2019-16303). | +| vulnerable/RandomUtil.java:38:26:38:41 | generateResetKey | Weak random number generator used in security sensitive method (JHipster CVE-2019-16303). | +| vulnerable/RandomUtil.java:48:26:48:43 | generateSeriesData | Weak random number generator used in security sensitive method (JHipster CVE-2019-16303). | +| vulnerable/RandomUtil.java:57:26:57:42 | generateTokenData | Weak random number generator used in security sensitive method (JHipster CVE-2019-16303). | From 2a4d21a989b0c095228a7e41cd6611060ce8712e Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 2 Oct 2020 10:02:29 +0200 Subject: [PATCH 022/166] Python: Test method call --- .../dataflow/fieldflow/allLocalFlow.expected | 143 +-- .../dataflow/fieldflow/dataflow.expected | 104 +-- .../dataflow/fieldflow/globalStep.expected | 852 ++++++++++-------- .../dataflow/fieldflow/localFlow.expected | 14 +- .../dataflow/fieldflow/postupdates.expected | 71 +- .../experimental/dataflow/fieldflow/test.py | 10 + 6 files changed, 648 insertions(+), 546 deletions(-) diff --git a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected index 840b3a27a7e..d21e535f82e 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/allLocalFlow.expected @@ -120,77 +120,90 @@ | test.py:19:9:19:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | | test.py:21:9:21:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | | test.py:25:1:25:20 | ControlFlowNode for ClassExpr | test.py:25:7:25:11 | GSSA Variable MyObj | -| test.py:25:13:25:18 | ControlFlowNode for object | test.py:30:17:30:22 | ControlFlowNode for object | +| test.py:25:13:25:18 | ControlFlowNode for object | test.py:33:17:33:22 | ControlFlowNode for object | | test.py:26:5:26:28 | ControlFlowNode for FunctionExpr | test.py:26:9:26:16 | SSA variable __init__ | | test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:12 | ControlFlowNode for self | | test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:16 | SSA variable self | | test.py:26:24:26:26 | SSA variable foo | test.py:27:20:27:22 | ControlFlowNode for foo | -| test.py:30:1:30:24 | ControlFlowNode for ClassExpr | test.py:30:7:30:15 | GSSA Variable NestedObj | -| test.py:31:5:31:23 | ControlFlowNode for FunctionExpr | test.py:31:9:31:16 | SSA variable __init__ | -| test.py:31:5:31:23 | GSSA Variable MyObj | test.py:32:20:32:24 | ControlFlowNode for MyObj | -| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:12 | ControlFlowNode for self | -| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:16 | SSA variable self | -| test.py:34:5:34:21 | ControlFlowNode for FunctionExpr | test.py:34:9:34:14 | SSA variable getObj | -| test.py:34:16:34:19 | SSA variable self | test.py:35:16:35:19 | ControlFlowNode for self | -| test.py:38:1:38:19 | ControlFlowNode for FunctionExpr | test.py:38:5:38:10 | GSSA Variable setFoo | -| test.py:38:1:38:19 | GSSA Variable SINK_F | test.py:39:5:39:10 | ControlFlowNode for SINK_F | -| test.py:38:12:38:14 | SSA variable obj | test.py:39:12:39:14 | ControlFlowNode for obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:11 | SSA variable obj | -| test.py:38:17:38:17 | SSA variable x | test.py:40:15:40:15 | ControlFlowNode for x | -| test.py:39:12:39:14 | ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:43:1:43:20 | ControlFlowNode for FunctionExpr | test.py:43:5:43:17 | GSSA Variable test_example1 | -| test.py:43:1:43:20 | GSSA Variable MyObj | test.py:44:13:44:17 | ControlFlowNode for MyObj | -| test.py:43:1:43:20 | GSSA Variable SINK | test.py:47:5:47:8 | ControlFlowNode for SINK | -| test.py:43:1:43:20 | GSSA Variable SOURCE | test.py:46:19:46:24 | ControlFlowNode for SOURCE | -| test.py:43:1:43:20 | GSSA Variable setFoo | test.py:46:5:46:10 | ControlFlowNode for setFoo | -| test.py:44:5:44:9 | SSA variable myobj | test.py:46:5:46:25 | SSA variable myobj | -| test.py:44:5:44:9 | SSA variable myobj | test.py:46:12:46:16 | ControlFlowNode for myobj | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:44:5:44:9 | SSA variable myobj | -| test.py:46:12:46:16 | ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | -| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | -| test.py:50:1:50:20 | ControlFlowNode for FunctionExpr | test.py:50:5:50:17 | GSSA Variable test_example2 | -| test.py:50:1:50:20 | GSSA Variable NestedObj | test.py:53:9:53:17 | ControlFlowNode for NestedObj | -| test.py:50:1:50:20 | GSSA Variable SINK | test.py:57:5:57:8 | ControlFlowNode for SINK | -| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:9:51:14 | ControlFlowNode for SOURCE | -| test.py:51:5:51:5 | SSA variable x | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:51:5:51:5 | SSA variable x | -| test.py:53:5:53:5 | SSA variable a | test.py:55:5:55:5 | ControlFlowNode for a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:5:53:5 | SSA variable a | -| test.py:55:5:55:5 | ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | -| test.py:60:1:60:27 | ControlFlowNode for FunctionExpr | test.py:60:5:60:24 | GSSA Variable test_example2_method | -| test.py:60:1:60:27 | GSSA Variable NestedObj | test.py:63:9:63:17 | ControlFlowNode for NestedObj | -| test.py:60:1:60:27 | GSSA Variable SINK | test.py:67:5:67:8 | ControlFlowNode for SINK | -| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:61:9:61:14 | ControlFlowNode for SOURCE | -| test.py:61:5:61:5 | SSA variable x | test.py:65:22:65:22 | ControlFlowNode for x | +| test.py:29:5:29:26 | ControlFlowNode for FunctionExpr | test.py:29:9:29:14 | SSA variable setFoo | +| test.py:29:16:29:19 | SSA variable self | test.py:30:9:30:12 | ControlFlowNode for self | +| test.py:29:16:29:19 | SSA variable self | test.py:30:9:30:16 | SSA variable self | +| test.py:29:22:29:24 | SSA variable foo | test.py:30:20:30:22 | ControlFlowNode for foo | +| test.py:33:1:33:24 | ControlFlowNode for ClassExpr | test.py:33:7:33:15 | GSSA Variable NestedObj | +| test.py:34:5:34:23 | ControlFlowNode for FunctionExpr | test.py:34:9:34:16 | SSA variable __init__ | +| test.py:34:5:34:23 | GSSA Variable MyObj | test.py:35:20:35:24 | ControlFlowNode for MyObj | +| test.py:34:18:34:21 | SSA variable self | test.py:35:9:35:12 | ControlFlowNode for self | +| test.py:34:18:34:21 | SSA variable self | test.py:35:9:35:16 | SSA variable self | +| test.py:37:5:37:21 | ControlFlowNode for FunctionExpr | test.py:37:9:37:14 | SSA variable getObj | +| test.py:37:16:37:19 | SSA variable self | test.py:38:16:38:19 | ControlFlowNode for self | +| test.py:41:1:41:19 | ControlFlowNode for FunctionExpr | test.py:41:5:41:10 | GSSA Variable setFoo | +| test.py:41:1:41:19 | GSSA Variable SINK_F | test.py:42:5:42:10 | ControlFlowNode for SINK_F | +| test.py:41:12:41:14 | SSA variable obj | test.py:42:12:42:14 | ControlFlowNode for obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:43:5:43:11 | SSA variable obj | +| test.py:41:17:41:17 | SSA variable x | test.py:43:15:43:15 | ControlFlowNode for x | +| test.py:42:12:42:14 | ControlFlowNode for obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:42:12:42:14 | [post read] ControlFlowNode for obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:46:1:46:20 | ControlFlowNode for FunctionExpr | test.py:46:5:46:17 | GSSA Variable test_example1 | +| test.py:46:1:46:20 | GSSA Variable MyObj | test.py:47:13:47:17 | ControlFlowNode for MyObj | +| test.py:46:1:46:20 | GSSA Variable SINK | test.py:50:5:50:8 | ControlFlowNode for SINK | +| test.py:46:1:46:20 | GSSA Variable SOURCE | test.py:49:19:49:24 | ControlFlowNode for SOURCE | +| test.py:46:1:46:20 | GSSA Variable setFoo | test.py:49:5:49:10 | ControlFlowNode for setFoo | +| test.py:47:5:47:9 | SSA variable myobj | test.py:49:5:49:25 | SSA variable myobj | +| test.py:47:5:47:9 | SSA variable myobj | test.py:49:12:49:16 | ControlFlowNode for myobj | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() | test.py:47:5:47:9 | SSA variable myobj | +| test.py:49:12:49:16 | ControlFlowNode for myobj | test.py:50:10:50:14 | ControlFlowNode for myobj | +| test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj | test.py:50:10:50:14 | ControlFlowNode for myobj | +| test.py:53:1:53:27 | ControlFlowNode for FunctionExpr | test.py:53:5:53:24 | GSSA Variable test_example1_method | +| test.py:53:1:53:27 | GSSA Variable MyObj | test.py:54:13:54:17 | ControlFlowNode for MyObj | +| test.py:53:1:53:27 | GSSA Variable SINK | test.py:57:5:57:8 | ControlFlowNode for SINK | +| test.py:53:1:53:27 | GSSA Variable SOURCE | test.py:56:18:56:23 | ControlFlowNode for SOURCE | +| test.py:54:5:54:9 | SSA variable myobj | test.py:56:5:56:9 | ControlFlowNode for myobj | +| test.py:54:5:54:9 | SSA variable myobj | test.py:56:5:56:24 | SSA variable myobj | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() | test.py:54:5:54:9 | SSA variable myobj | +| test.py:56:5:56:9 | ControlFlowNode for myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | +| test.py:56:5:56:9 | [post read] ControlFlowNode for myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | +| test.py:60:1:60:20 | ControlFlowNode for FunctionExpr | test.py:60:5:60:17 | GSSA Variable test_example2 | +| test.py:60:1:60:20 | GSSA Variable NestedObj | test.py:63:9:63:17 | ControlFlowNode for NestedObj | +| test.py:60:1:60:20 | GSSA Variable SINK | test.py:67:5:67:8 | ControlFlowNode for SINK | +| test.py:60:1:60:20 | GSSA Variable SOURCE | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:61:5:61:5 | SSA variable x | test.py:65:17:65:17 | ControlFlowNode for x | | test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:61:5:61:5 | SSA variable x | | test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:5 | ControlFlowNode for a | -| test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:14 | SSA variable a | | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:63:5:63:5 | SSA variable a | | test.py:65:5:65:5 | ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | | test.py:65:5:65:5 | [post read] ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | -| test.py:70:1:70:20 | ControlFlowNode for FunctionExpr | test.py:70:5:70:17 | GSSA Variable test_example3 | -| test.py:70:1:70:20 | GSSA Variable MyObj | test.py:71:11:71:15 | ControlFlowNode for MyObj | -| test.py:70:1:70:20 | GSSA Variable SINK | test.py:72:5:72:8 | ControlFlowNode for SINK | -| test.py:70:1:70:20 | GSSA Variable SOURCE | test.py:71:17:71:22 | ControlFlowNode for SOURCE | -| test.py:71:5:71:7 | SSA variable obj | test.py:72:10:72:12 | ControlFlowNode for obj | -| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:71:5:71:7 | SSA variable obj | -| test.py:75:1:75:23 | ControlFlowNode for FunctionExpr | test.py:75:5:75:20 | GSSA Variable test_example3_kw | -| test.py:75:1:75:23 | GSSA Variable MyObj | test.py:76:11:76:15 | ControlFlowNode for MyObj | -| test.py:75:1:75:23 | GSSA Variable SINK | test.py:77:5:77:8 | ControlFlowNode for SINK | -| test.py:75:1:75:23 | GSSA Variable SOURCE | test.py:76:21:76:26 | ControlFlowNode for SOURCE | -| test.py:76:5:76:7 | SSA variable obj | test.py:77:10:77:12 | ControlFlowNode for obj | -| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:76:5:76:7 | SSA variable obj | -| test.py:80:1:80:30 | ControlFlowNode for FunctionExpr | test.py:80:5:80:26 | GSSA Variable fields_with_local_flow | -| test.py:80:1:80:30 | GSSA Variable MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | -| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | -| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | -| test.py:81:5:81:7 | SSA variable obj | test.py:82:9:82:11 | ControlFlowNode for obj | -| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:81:5:81:7 | SSA variable obj | -| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | -| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | -| test.py:86:1:86:18 | ControlFlowNode for FunctionExpr | test.py:86:5:86:15 | GSSA Variable test_fields | -| test.py:86:1:86:18 | GSSA Variable SINK | test.py:87:5:87:8 | ControlFlowNode for SINK | -| test.py:86:1:86:18 | GSSA Variable SOURCE | test.py:87:33:87:38 | ControlFlowNode for SOURCE | -| test.py:86:1:86:18 | GSSA Variable fields_with_local_flow | test.py:87:10:87:31 | ControlFlowNode for fields_with_local_flow | +| test.py:70:1:70:27 | ControlFlowNode for FunctionExpr | test.py:70:5:70:24 | GSSA Variable test_example2_method | +| test.py:70:1:70:27 | GSSA Variable NestedObj | test.py:73:9:73:17 | ControlFlowNode for NestedObj | +| test.py:70:1:70:27 | GSSA Variable SINK | test.py:77:5:77:8 | ControlFlowNode for SINK | +| test.py:70:1:70:27 | GSSA Variable SOURCE | test.py:71:9:71:14 | ControlFlowNode for SOURCE | +| test.py:71:5:71:5 | SSA variable x | test.py:75:22:75:22 | ControlFlowNode for x | +| test.py:71:9:71:14 | ControlFlowNode for SOURCE | test.py:71:5:71:5 | SSA variable x | +| test.py:73:5:73:5 | SSA variable a | test.py:75:5:75:5 | ControlFlowNode for a | +| test.py:73:5:73:5 | SSA variable a | test.py:75:5:75:14 | SSA variable a | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() | test.py:73:5:73:5 | SSA variable a | +| test.py:75:5:75:5 | ControlFlowNode for a | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:75:5:75:5 | [post read] ControlFlowNode for a | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:80:1:80:20 | ControlFlowNode for FunctionExpr | test.py:80:5:80:17 | GSSA Variable test_example3 | +| test.py:80:1:80:20 | GSSA Variable MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | +| test.py:80:1:80:20 | GSSA Variable SINK | test.py:82:5:82:8 | ControlFlowNode for SINK | +| test.py:80:1:80:20 | GSSA Variable SOURCE | test.py:81:17:81:22 | ControlFlowNode for SOURCE | +| test.py:81:5:81:7 | SSA variable obj | test.py:82:10:82:12 | ControlFlowNode for obj | +| test.py:81:11:81:23 | ControlFlowNode for MyObj() | test.py:81:5:81:7 | SSA variable obj | +| test.py:85:1:85:23 | ControlFlowNode for FunctionExpr | test.py:85:5:85:20 | GSSA Variable test_example3_kw | +| test.py:85:1:85:23 | GSSA Variable MyObj | test.py:86:11:86:15 | ControlFlowNode for MyObj | +| test.py:85:1:85:23 | GSSA Variable SINK | test.py:87:5:87:8 | ControlFlowNode for SINK | +| test.py:85:1:85:23 | GSSA Variable SOURCE | test.py:86:21:86:26 | ControlFlowNode for SOURCE | +| test.py:86:5:86:7 | SSA variable obj | test.py:87:10:87:12 | ControlFlowNode for obj | +| test.py:86:11:86:27 | ControlFlowNode for MyObj() | test.py:86:5:86:7 | SSA variable obj | +| test.py:90:1:90:30 | ControlFlowNode for FunctionExpr | test.py:90:5:90:26 | GSSA Variable fields_with_local_flow | +| test.py:90:1:90:30 | GSSA Variable MyObj | test.py:91:11:91:15 | ControlFlowNode for MyObj | +| test.py:90:28:90:28 | SSA variable x | test.py:91:11:91:18 | SSA variable x | +| test.py:90:28:90:28 | SSA variable x | test.py:91:17:91:17 | ControlFlowNode for x | +| test.py:91:5:91:7 | SSA variable obj | test.py:92:9:92:11 | ControlFlowNode for obj | +| test.py:91:11:91:18 | ControlFlowNode for MyObj() | test.py:91:5:91:7 | SSA variable obj | +| test.py:92:5:92:5 | SSA variable a | test.py:93:12:93:12 | ControlFlowNode for a | +| test.py:92:9:92:15 | ControlFlowNode for Attribute | test.py:92:5:92:5 | SSA variable a | +| test.py:96:1:96:18 | ControlFlowNode for FunctionExpr | test.py:96:5:96:15 | GSSA Variable test_fields | +| test.py:96:1:96:18 | GSSA Variable SINK | test.py:97:5:97:8 | ControlFlowNode for SINK | +| test.py:96:1:96:18 | GSSA Variable SOURCE | test.py:97:33:97:38 | ControlFlowNode for SOURCE | +| test.py:96:1:96:18 | GSSA Variable fields_with_local_flow | test.py:97:10:97:31 | ControlFlowNode for fields_with_local_flow | diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected index 0a360b9b4b9..0431fc802ff 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected @@ -20,29 +20,29 @@ edges | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | | examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | examples.py:50:6:50:12 | ControlFlowNode for Attribute | | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:46:19:46:24 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:51:9:51:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:17:71:22 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:76:21:76:26 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:33:87:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:49:19:49:24 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:81:17:81:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:86:21:86:26 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:97:33:97:38 | ControlFlowNode for SOURCE | | test.py:3:1:3:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:3:1:3:6 | GSSA Variable SOURCE | -| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | -| test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:18 | ControlFlowNode for Attribute | -| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:55:17:55:17 | ControlFlowNode for x | test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | -| test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | -| test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute | -| test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:71:17:71:22 | ControlFlowNode for SOURCE | test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | test.py:72:10:72:16 | ControlFlowNode for Attribute | -| test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:76:21:76:26 | ControlFlowNode for SOURCE | test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | test.py:77:10:77:16 | ControlFlowNode for Attribute | -| test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:18 | ControlFlowNode for Attribute | +| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:65:17:65:17 | ControlFlowNode for x | +| test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:65:17:65:17 | ControlFlowNode for x | test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | +| test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | +| test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:67:10:67:18 | ControlFlowNode for Attribute | +| test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | test.py:82:10:82:16 | ControlFlowNode for Attribute | +| test.py:86:11:86:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:87:10:87:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:86:21:86:26 | ControlFlowNode for SOURCE | test.py:86:11:86:27 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:87:10:87:12 | ControlFlowNode for obj [Attribute foo] | test.py:87:10:87:16 | ControlFlowNode for Attribute | +| test.py:97:33:97:38 | ControlFlowNode for SOURCE | test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | nodes | examples.py:27:8:27:12 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] | | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | @@ -65,27 +65,27 @@ nodes | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | semmle.label | ModuleVariableNode for Global Variable SOURCE in Module test | | test.py:3:1:3:6 | GSSA Variable SOURCE | semmle.label | GSSA Variable SOURCE | | test.py:3:10:3:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] | -| test.py:46:19:46:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] | -| test.py:47:10:47:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:51:9:51:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute [Attribute foo] | -| test.py:55:17:55:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | -| test.py:57:10:57:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:71:17:71:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | -| test.py:72:10:72:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:76:21:76:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | -| test.py:77:10:77:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | -| test.py:87:33:87:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:49:19:49:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] | +| test.py:50:10:50:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:61:9:61:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute [Attribute foo] | +| test.py:65:17:65:17 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | +| test.py:67:10:67:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:81:17:81:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | +| test.py:82:10:82:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:86:11:86:27 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:86:21:86:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:87:10:87:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | +| test.py:87:10:87:16 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | semmle.label | ControlFlowNode for fields_with_local_flow() | +| test.py:97:33:97:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | #select | examples.py:28:6:28:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:28:6:28:14 | ControlFlowNode for Attribute | Flow found | | examples.py:37:6:37:14 | ControlFlowNode for Attribute | examples.py:27:15:27:20 | ControlFlowNode for SOURCE | examples.py:37:6:37:14 | ControlFlowNode for Attribute | Flow found | @@ -99,13 +99,13 @@ nodes | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | examples.py:40:5:40:10 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | Flow found | | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | examples.py:49:13:49:18 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | Flow found | | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | Flow found | -| test.py:47:10:47:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:47:10:47:18 | ControlFlowNode for Attribute | Flow found | -| test.py:47:10:47:18 | ControlFlowNode for Attribute | test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:47:10:47:18 | ControlFlowNode for Attribute | Flow found | -| test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:57:10:57:18 | ControlFlowNode for Attribute | Flow found | -| test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:57:10:57:18 | ControlFlowNode for Attribute | Flow found | -| test.py:72:10:72:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:72:10:72:16 | ControlFlowNode for Attribute | Flow found | -| test.py:72:10:72:16 | ControlFlowNode for Attribute | test.py:71:17:71:22 | ControlFlowNode for SOURCE | test.py:72:10:72:16 | ControlFlowNode for Attribute | Flow found | -| test.py:77:10:77:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:77:10:77:16 | ControlFlowNode for Attribute | Flow found | -| test.py:77:10:77:16 | ControlFlowNode for Attribute | test.py:76:21:76:26 | ControlFlowNode for SOURCE | test.py:77:10:77:16 | ControlFlowNode for Attribute | Flow found | -| test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | Flow found | -| test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | Flow found | +| test.py:50:10:50:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:50:10:50:18 | ControlFlowNode for Attribute | Flow found | +| test.py:50:10:50:18 | ControlFlowNode for Attribute | test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:50:10:50:18 | ControlFlowNode for Attribute | Flow found | +| test.py:67:10:67:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:67:10:67:18 | ControlFlowNode for Attribute | Flow found | +| test.py:67:10:67:18 | ControlFlowNode for Attribute | test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:67:10:67:18 | ControlFlowNode for Attribute | Flow found | +| test.py:82:10:82:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:82:10:82:16 | ControlFlowNode for Attribute | Flow found | +| test.py:82:10:82:16 | ControlFlowNode for Attribute | test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:82:10:82:16 | ControlFlowNode for Attribute | Flow found | +| test.py:87:10:87:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:87:10:87:16 | ControlFlowNode for Attribute | Flow found | +| test.py:87:10:87:16 | ControlFlowNode for Attribute | test.py:86:21:86:26 | ControlFlowNode for SOURCE | test.py:87:10:87:16 | ControlFlowNode for Attribute | Flow found | +| test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | Flow found | +| test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | test.py:97:33:97:38 | ControlFlowNode for SOURCE | test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | Flow found | diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected index c12e0c08b1d..e4142b6da6b 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected @@ -472,48 +472,54 @@ | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:53:28:53:28 | SSA variable x | | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:32:20:32:24 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:32:20:32:24 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:44:13:44:17 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:44:13:44:17 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:71:11:71:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:71:11:71:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:76:11:76:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:76:11:76:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:35:20:35:24 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:35:20:35:24 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:47:13:47:17 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:47:13:47:17 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:54:13:54:17 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:54:13:54:17 | ControlFlowNode for MyObj | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:81:11:81:15 | ControlFlowNode for MyObj | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:81:11:81:15 | ControlFlowNode for MyObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:53:9:53:17 | ControlFlowNode for NestedObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:53:9:53:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:86:11:86:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:86:11:86:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:91:11:91:15 | ControlFlowNode for MyObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | test.py:91:11:91:15 | ControlFlowNode for MyObj | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:63:9:63:17 | ControlFlowNode for NestedObj | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:63:9:63:17 | ControlFlowNode for NestedObj | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:47:5:47:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:47:5:47:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:73:9:73:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | test.py:73:9:73:17 | ControlFlowNode for NestedObj | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:50:5:50:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:50:5:50:8 | ControlFlowNode for SINK | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:57:5:57:8 | ControlFlowNode for SINK | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:57:5:57:8 | ControlFlowNode for SINK | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:67:5:67:8 | ControlFlowNode for SINK | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:67:5:67:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:72:5:72:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:72:5:72:8 | ControlFlowNode for SINK | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:77:5:77:8 | ControlFlowNode for SINK | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:77:5:77:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:82:5:82:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:82:5:82:8 | ControlFlowNode for SINK | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:87:5:87:8 | ControlFlowNode for SINK | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:87:5:87:8 | ControlFlowNode for SINK | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:39:5:39:10 | ControlFlowNode for SINK_F | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:39:5:39:10 | ControlFlowNode for SINK_F | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:46:19:46:24 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:46:19:46:24 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:51:9:51:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:51:9:51:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:97:5:97:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK in Module test | test.py:97:5:97:8 | ControlFlowNode for SINK | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:42:5:42:10 | ControlFlowNode for SINK_F | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SINK_F in Module test | test.py:42:5:42:10 | ControlFlowNode for SINK_F | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:49:19:49:24 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:49:19:49:24 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:56:18:56:23 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:56:18:56:23 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:61:9:61:14 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:61:9:61:14 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:17:71:22 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:17:71:22 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:76:21:76:26 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:76:21:76:26 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:33:87:38 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:87:33:87:38 | ControlFlowNode for SOURCE | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:87:10:87:31 | ControlFlowNode for fields_with_local_flow | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:87:10:87:31 | ControlFlowNode for fields_with_local_flow | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:9:71:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:9:71:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:81:17:81:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:81:17:81:22 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:86:21:86:26 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:86:21:86:26 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:97:33:97:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:97:33:97:38 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:97:10:97:31 | ControlFlowNode for fields_with_local_flow | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | test.py:97:10:97:31 | ControlFlowNode for fields_with_local_flow | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:11:8:11:16 | ControlFlowNode for is_source | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:11:8:11:16 | ControlFlowNode for is_source | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable is_source in Module test | test.py:18:8:18:16 | ControlFlowNode for is_source | @@ -526,8 +532,8 @@ | test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:19:9:19:13 | ControlFlowNode for print | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:21:9:21:13 | ControlFlowNode for print | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable print in Module test | test.py:21:9:21:13 | ControlFlowNode for print | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:46:5:46:10 | ControlFlowNode for setFoo | -| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:46:5:46:10 | ControlFlowNode for setFoo | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:49:5:49:10 | ControlFlowNode for setFoo | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | test.py:49:5:49:10 | ControlFlowNode for setFoo | | test.py:2:13:2:26 | ControlFlowNode for Str | test.py:2:1:2:9 | GSSA Variable NONSOURCE | | test.py:2:13:2:26 | ControlFlowNode for Str | test.py:2:1:2:9 | GSSA Variable NONSOURCE | | test.py:3:1:3:6 | GSSA Variable SOURCE | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | @@ -754,18 +760,20 @@ | test.py:11:18:11:18 | ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | | test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | | test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:14:34:14:34 | ControlFlowNode for x | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:47:10:47:18 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:47:10:47:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:50:10:50:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:50:10:50:18 | [post arg] ControlFlowNode for Attribute | | test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:57:10:57:18 | [post arg] ControlFlowNode for Attribute | | test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:57:10:57:18 | [post arg] ControlFlowNode for Attribute | | test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:67:10:67:18 | [post arg] ControlFlowNode for Attribute | | test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:67:10:67:18 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:72:10:72:16 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:72:10:72:16 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:77:10:77:16 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:77:10:77:16 | [post arg] ControlFlowNode for Attribute | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:87:10:87:39 | [post arg] ControlFlowNode for fields_with_local_flow() | -| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:87:10:87:39 | [post arg] ControlFlowNode for fields_with_local_flow() | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:77:10:77:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:77:10:77:18 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:82:10:82:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:82:10:82:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:87:10:87:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:87:10:87:16 | [post arg] ControlFlowNode for Attribute | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:97:10:97:39 | [post arg] ControlFlowNode for fields_with_local_flow() | +| test.py:11:18:11:18 | [post arg] ControlFlowNode for x | test.py:97:10:97:39 | [post arg] ControlFlowNode for fields_with_local_flow() | | test.py:12:9:12:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | | test.py:12:9:12:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | | test.py:12:9:12:13 | SSA variable x | test.py:10:1:10:12 | SSA variable x | @@ -810,14 +818,14 @@ | test.py:18:18:18:18 | ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | | test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | | test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | -| test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | -| test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | +| test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:42:12:42:18 | [post arg] ControlFlowNode for Attribute | +| test.py:18:18:18:18 | [post arg] ControlFlowNode for x | test.py:42:12:42:18 | [post arg] ControlFlowNode for Attribute | | test.py:19:9:19:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | | test.py:19:9:19:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | | test.py:19:9:19:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | | test.py:19:9:19:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | -| test.py:19:34:19:34 | [post arg] ControlFlowNode for x | test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | -| test.py:19:34:19:34 | [post arg] ControlFlowNode for x | test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | +| test.py:19:34:19:34 | [post arg] ControlFlowNode for x | test.py:42:12:42:18 | [post arg] ControlFlowNode for Attribute | +| test.py:19:34:19:34 | [post arg] ControlFlowNode for x | test.py:42:12:42:18 | [post arg] ControlFlowNode for Attribute | | test.py:21:9:21:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | | test.py:21:9:21:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | | test.py:21:9:21:13 | SSA variable x | test.py:17:1:17:14 | SSA variable x | @@ -826,8 +834,8 @@ | test.py:25:1:25:20 | ControlFlowNode for ClassExpr | test.py:25:7:25:11 | GSSA Variable MyObj | | test.py:25:7:25:11 | GSSA Variable MyObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | | test.py:25:7:25:11 | GSSA Variable MyObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable MyObj in Module test | -| test.py:25:13:25:18 | ControlFlowNode for object | test.py:30:17:30:22 | ControlFlowNode for object | -| test.py:25:13:25:18 | ControlFlowNode for object | test.py:30:17:30:22 | ControlFlowNode for object | +| test.py:25:13:25:18 | ControlFlowNode for object | test.py:33:17:33:22 | ControlFlowNode for object | +| test.py:25:13:25:18 | ControlFlowNode for object | test.py:33:17:33:22 | ControlFlowNode for object | | test.py:26:5:26:28 | ControlFlowNode for FunctionExpr | test.py:26:9:26:16 | SSA variable __init__ | | test.py:26:5:26:28 | ControlFlowNode for FunctionExpr | test.py:26:9:26:16 | SSA variable __init__ | | test.py:26:18:26:21 | SSA variable self | test.py:27:9:27:12 | ControlFlowNode for self | @@ -842,236 +850,238 @@ | test.py:26:24:26:26 | SSA variable foo | test.py:27:20:27:22 | ControlFlowNode for foo | | test.py:26:24:26:26 | SSA variable foo | test.py:27:20:27:22 | ControlFlowNode for foo | | test.py:26:24:26:26 | SSA variable foo | test.py:27:20:27:22 | ControlFlowNode for foo | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:32:20:32:30 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:32:20:32:30 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:44:13:44:23 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:44:13:44:23 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:71:11:71:23 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:71:11:71:23 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:76:11:76:27 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:76:11:76:27 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:81:11:81:18 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:81:11:81:18 | ControlFlowNode for MyObj() | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:32:20:32:30 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:44:13:44:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:35:20:35:30 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:35:20:35:30 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:47:13:47:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:47:13:47:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:54:13:54:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:54:13:54:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:81:11:81:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:81:11:81:23 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:86:11:86:27 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:86:11:86:27 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:91:11:91:18 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:91:11:91:18 | ControlFlowNode for MyObj() | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:35:20:35:30 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:47:13:47:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:54:13:54:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:86:11:86:27 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:91:11:91:18 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:27:20:27:22 | ControlFlowNode for foo | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | | test.py:27:20:27:22 | ControlFlowNode for foo | test.py:27:9:27:12 | [post store] ControlFlowNode for self [Attribute foo] | -| test.py:30:1:30:24 | ControlFlowNode for ClassExpr | test.py:30:7:30:15 | GSSA Variable NestedObj | -| test.py:30:1:30:24 | ControlFlowNode for ClassExpr | test.py:30:7:30:15 | GSSA Variable NestedObj | -| test.py:30:7:30:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | -| test.py:30:7:30:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | -| test.py:31:5:31:23 | ControlFlowNode for FunctionExpr | test.py:31:9:31:16 | SSA variable __init__ | -| test.py:31:5:31:23 | ControlFlowNode for FunctionExpr | test.py:31:9:31:16 | SSA variable __init__ | -| test.py:31:5:31:23 | GSSA Variable MyObj | test.py:32:20:32:24 | ControlFlowNode for MyObj | -| test.py:31:5:31:23 | GSSA Variable MyObj | test.py:32:20:32:24 | ControlFlowNode for MyObj | -| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:12 | ControlFlowNode for self | -| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:12 | ControlFlowNode for self | -| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:12 | ControlFlowNode for self | -| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:12 | ControlFlowNode for self | -| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:16 | SSA variable self | -| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:16 | SSA variable self | -| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:16 | SSA variable self | -| test.py:31:18:31:21 | SSA variable self | test.py:32:9:32:16 | SSA variable self | -| test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | -| test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:53:9:53:19 | ControlFlowNode for NestedObj() | -| test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | -| test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | -| test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | -| test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | -| test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | -| test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj] | -| test.py:32:20:32:30 | ControlFlowNode for MyObj() | test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj] | -| test.py:32:20:32:30 | ControlFlowNode for MyObj() [Attribute foo] | test.py:32:9:32:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | -| test.py:32:20:32:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:32:20:32:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:32:26:32:29 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | -| test.py:32:26:32:29 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | -| test.py:32:26:32:29 | ControlFlowNode for Str | test.py:32:20:32:30 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:34:5:34:21 | ControlFlowNode for FunctionExpr | test.py:34:9:34:14 | SSA variable getObj | -| test.py:34:5:34:21 | ControlFlowNode for FunctionExpr | test.py:34:9:34:14 | SSA variable getObj | -| test.py:34:16:34:19 | SSA variable self | test.py:35:16:35:19 | ControlFlowNode for self | -| test.py:34:16:34:19 | SSA variable self | test.py:35:16:35:19 | ControlFlowNode for self | -| test.py:35:16:35:23 | ControlFlowNode for Attribute | test.py:65:5:65:14 | ControlFlowNode for Attribute() | -| test.py:35:16:35:23 | ControlFlowNode for Attribute | test.py:65:5:65:14 | ControlFlowNode for Attribute() | -| test.py:38:1:38:19 | ControlFlowNode for FunctionExpr | test.py:38:5:38:10 | GSSA Variable setFoo | -| test.py:38:1:38:19 | ControlFlowNode for FunctionExpr | test.py:38:5:38:10 | GSSA Variable setFoo | -| test.py:38:1:38:19 | GSSA Variable SINK_F | test.py:39:5:39:10 | ControlFlowNode for SINK_F | -| test.py:38:1:38:19 | GSSA Variable SINK_F | test.py:39:5:39:10 | ControlFlowNode for SINK_F | -| test.py:38:5:38:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | -| test.py:38:5:38:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | -| test.py:38:12:38:14 | SSA variable obj | test.py:39:12:39:14 | ControlFlowNode for obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:39:12:39:14 | ControlFlowNode for obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:39:12:39:14 | ControlFlowNode for obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:39:12:39:14 | ControlFlowNode for obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:11 | SSA variable obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:11 | SSA variable obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:11 | SSA variable obj | -| test.py:38:12:38:14 | SSA variable obj | test.py:40:5:40:11 | SSA variable obj | -| test.py:38:12:38:14 | SSA variable obj [Attribute foo] | test.py:39:12:39:14 | ControlFlowNode for obj [Attribute foo] | -| test.py:38:17:38:17 | SSA variable x | test.py:40:15:40:15 | ControlFlowNode for x | -| test.py:38:17:38:17 | SSA variable x | test.py:40:15:40:15 | ControlFlowNode for x | -| test.py:38:17:38:17 | SSA variable x | test.py:40:15:40:15 | ControlFlowNode for x | -| test.py:38:17:38:17 | SSA variable x | test.py:40:15:40:15 | ControlFlowNode for x | -| test.py:39:12:39:14 | ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:39:12:39:14 | ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:39:12:39:14 | ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:39:12:39:14 | ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:39:12:39:14 | ControlFlowNode for obj [Attribute foo] | test.py:39:12:39:18 | ControlFlowNode for Attribute | -| test.py:39:12:39:14 | ControlFlowNode for obj [Attribute foo] | test.py:39:12:39:18 | ControlFlowNode for Attribute | -| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | -| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | -| test.py:39:12:39:14 | [post read] ControlFlowNode for obj [Attribute foo] | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | -| test.py:39:12:39:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | -| test.py:39:12:39:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | -| test.py:39:12:39:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | -| test.py:39:12:39:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | -| test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | test.py:39:12:39:14 | [post read] ControlFlowNode for obj [Attribute foo] | -| test.py:40:5:40:7 | [post store] ControlFlowNode for obj | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | -| test.py:40:5:40:7 | [post store] ControlFlowNode for obj | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | -| test.py:40:5:40:7 | [post store] ControlFlowNode for obj [Attribute foo] | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | -| test.py:40:15:40:15 | ControlFlowNode for x | test.py:40:5:40:7 | [post store] ControlFlowNode for obj [Attribute foo] | -| test.py:40:15:40:15 | ControlFlowNode for x | test.py:40:5:40:7 | [post store] ControlFlowNode for obj [Attribute foo] | -| test.py:43:1:43:20 | ControlFlowNode for FunctionExpr | test.py:43:5:43:17 | GSSA Variable test_example1 | -| test.py:43:1:43:20 | ControlFlowNode for FunctionExpr | test.py:43:5:43:17 | GSSA Variable test_example1 | -| test.py:43:1:43:20 | GSSA Variable MyObj | test.py:44:13:44:17 | ControlFlowNode for MyObj | -| test.py:43:1:43:20 | GSSA Variable MyObj | test.py:44:13:44:17 | ControlFlowNode for MyObj | -| test.py:43:1:43:20 | GSSA Variable SINK | test.py:47:5:47:8 | ControlFlowNode for SINK | -| test.py:43:1:43:20 | GSSA Variable SINK | test.py:47:5:47:8 | ControlFlowNode for SINK | -| test.py:43:1:43:20 | GSSA Variable SOURCE | test.py:46:19:46:24 | ControlFlowNode for SOURCE | -| test.py:43:1:43:20 | GSSA Variable SOURCE | test.py:46:19:46:24 | ControlFlowNode for SOURCE | -| test.py:43:1:43:20 | GSSA Variable setFoo | test.py:46:5:46:10 | ControlFlowNode for setFoo | -| test.py:43:1:43:20 | GSSA Variable setFoo | test.py:46:5:46:10 | ControlFlowNode for setFoo | -| test.py:44:5:44:9 | SSA variable myobj | test.py:46:5:46:25 | SSA variable myobj | -| test.py:44:5:44:9 | SSA variable myobj | test.py:46:5:46:25 | SSA variable myobj | -| test.py:44:5:44:9 | SSA variable myobj | test.py:46:12:46:16 | ControlFlowNode for myobj | -| test.py:44:5:44:9 | SSA variable myobj | test.py:46:12:46:16 | ControlFlowNode for myobj | -| test.py:44:5:44:9 | SSA variable myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | -| test.py:44:5:44:9 | SSA variable myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | -| test.py:44:5:44:9 | SSA variable myobj [Attribute foo] | test.py:46:12:46:16 | ControlFlowNode for myobj [Attribute foo] | -| test.py:44:5:44:9 | SSA variable myobj [Attribute foo] | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:44:5:44:9 | SSA variable myobj | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:44:5:44:9 | SSA variable myobj | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:46:5:46:25 | SSA variable myobj | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:46:5:46:25 | SSA variable myobj | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:46:12:46:16 | ControlFlowNode for myobj | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:46:12:46:16 | ControlFlowNode for myobj | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:47:10:47:14 | ControlFlowNode for myobj | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:47:10:47:14 | ControlFlowNode for myobj | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:44:5:44:9 | SSA variable myobj [Attribute foo] | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:46:12:46:16 | ControlFlowNode for myobj [Attribute foo] | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:44:13:44:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:44:13:44:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:44:19:44:22 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | -| test.py:44:19:44:22 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | -| test.py:44:19:44:22 | ControlFlowNode for Str | test.py:44:13:44:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:46:12:46:16 | ControlFlowNode for myobj | test.py:38:12:38:14 | SSA variable obj | -| test.py:46:12:46:16 | ControlFlowNode for myobj | test.py:38:12:38:14 | SSA variable obj | -| test.py:46:12:46:16 | ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | -| test.py:46:12:46:16 | ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | -| test.py:46:12:46:16 | ControlFlowNode for myobj [Attribute foo] | test.py:38:12:38:14 | SSA variable obj [Attribute foo] | -| test.py:46:12:46:16 | ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | -| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | -| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | -| test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | SSA variable x | -| test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:38:17:38:17 | SSA variable x | -| test.py:46:19:46:24 | ControlFlowNode for SOURCE | test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | -| test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:18 | ControlFlowNode for Attribute | -| test.py:47:10:47:14 | ControlFlowNode for myobj [Attribute foo] | test.py:47:10:47:18 | ControlFlowNode for Attribute | -| test.py:47:10:47:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:47:10:47:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:50:1:50:20 | ControlFlowNode for FunctionExpr | test.py:50:5:50:17 | GSSA Variable test_example2 | -| test.py:50:1:50:20 | ControlFlowNode for FunctionExpr | test.py:50:5:50:17 | GSSA Variable test_example2 | -| test.py:50:1:50:20 | GSSA Variable NestedObj | test.py:53:9:53:17 | ControlFlowNode for NestedObj | -| test.py:50:1:50:20 | GSSA Variable NestedObj | test.py:53:9:53:17 | ControlFlowNode for NestedObj | -| test.py:50:1:50:20 | GSSA Variable SINK | test.py:57:5:57:8 | ControlFlowNode for SINK | -| test.py:50:1:50:20 | GSSA Variable SINK | test.py:57:5:57:8 | ControlFlowNode for SINK | -| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:5:51:5 | SSA variable x | -| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:5:51:5 | SSA variable x | -| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:9:51:14 | ControlFlowNode for SOURCE | -| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:51:9:51:14 | ControlFlowNode for SOURCE | -| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:50:1:50:20 | GSSA Variable SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:51:5:51:5 | SSA variable x | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:51:5:51:5 | SSA variable x | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:51:5:51:5 | SSA variable x | -| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:51:5:51:5 | SSA variable x | -| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:51:9:51:14 | ControlFlowNode for SOURCE | test.py:55:17:55:17 | ControlFlowNode for x | -| test.py:53:5:53:5 | SSA variable a | test.py:55:5:55:5 | ControlFlowNode for a | -| test.py:53:5:53:5 | SSA variable a | test.py:55:5:55:5 | ControlFlowNode for a | -| test.py:53:5:53:5 | SSA variable a | test.py:57:10:57:10 | ControlFlowNode for a | -| test.py:53:5:53:5 | SSA variable a | test.py:57:10:57:10 | ControlFlowNode for a | -| test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:53:5:53:5 | SSA variable a [Attribute obj] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | -| test.py:53:5:53:5 | SSA variable a [Attribute obj] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:5:53:5 | SSA variable a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:5:53:5 | SSA variable a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:55:5:55:5 | ControlFlowNode for a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:55:5:55:5 | ControlFlowNode for a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:57:10:57:10 | ControlFlowNode for a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:57:10:57:10 | ControlFlowNode for a | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:53:5:53:5 | SSA variable a [Attribute obj, Attribute foo] | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:53:5:53:5 | SSA variable a [Attribute obj] | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | -| test.py:53:9:53:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:31:18:31:21 | SSA variable self | -| test.py:53:9:53:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:31:18:31:21 | SSA variable self | -| test.py:55:5:55:5 | ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | -| test.py:55:5:55:5 | ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | -| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:55:5:55:9 | ControlFlowNode for Attribute | -| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:55:5:55:9 | ControlFlowNode for Attribute | -| test.py:55:5:55:5 | ControlFlowNode for a [Attribute obj] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | -| test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute | test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj] | -| test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:55:5:55:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | -| test.py:55:17:55:17 | ControlFlowNode for x | test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | -| test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | -| test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | test.py:57:10:57:14 | ControlFlowNode for Attribute | -| test.py:57:10:57:10 | ControlFlowNode for a [Attribute obj] | test.py:57:10:57:14 | ControlFlowNode for Attribute | -| test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute | -| test.py:57:10:57:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute | +| test.py:29:5:29:26 | ControlFlowNode for FunctionExpr | test.py:29:9:29:14 | SSA variable setFoo | +| test.py:29:5:29:26 | ControlFlowNode for FunctionExpr | test.py:29:9:29:14 | SSA variable setFoo | +| test.py:29:16:29:19 | SSA variable self | test.py:30:9:30:12 | ControlFlowNode for self | +| test.py:29:16:29:19 | SSA variable self | test.py:30:9:30:12 | ControlFlowNode for self | +| test.py:29:16:29:19 | SSA variable self | test.py:30:9:30:12 | ControlFlowNode for self | +| test.py:29:16:29:19 | SSA variable self | test.py:30:9:30:12 | ControlFlowNode for self | +| test.py:29:16:29:19 | SSA variable self | test.py:30:9:30:16 | SSA variable self | +| test.py:29:16:29:19 | SSA variable self | test.py:30:9:30:16 | SSA variable self | +| test.py:29:16:29:19 | SSA variable self | test.py:30:9:30:16 | SSA variable self | +| test.py:29:16:29:19 | SSA variable self | test.py:30:9:30:16 | SSA variable self | +| test.py:29:22:29:24 | SSA variable foo | test.py:30:20:30:22 | ControlFlowNode for foo | +| test.py:29:22:29:24 | SSA variable foo | test.py:30:20:30:22 | ControlFlowNode for foo | +| test.py:29:22:29:24 | SSA variable foo | test.py:30:20:30:22 | ControlFlowNode for foo | +| test.py:29:22:29:24 | SSA variable foo | test.py:30:20:30:22 | ControlFlowNode for foo | +| test.py:30:9:30:12 | [post store] ControlFlowNode for self | test.py:56:18:56:23 | [post arg] ControlFlowNode for SOURCE | +| test.py:30:9:30:12 | [post store] ControlFlowNode for self | test.py:56:18:56:23 | [post arg] ControlFlowNode for SOURCE | +| test.py:33:1:33:24 | ControlFlowNode for ClassExpr | test.py:33:7:33:15 | GSSA Variable NestedObj | +| test.py:33:1:33:24 | ControlFlowNode for ClassExpr | test.py:33:7:33:15 | GSSA Variable NestedObj | +| test.py:33:7:33:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | +| test.py:33:7:33:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | +| test.py:34:5:34:23 | ControlFlowNode for FunctionExpr | test.py:34:9:34:16 | SSA variable __init__ | +| test.py:34:5:34:23 | ControlFlowNode for FunctionExpr | test.py:34:9:34:16 | SSA variable __init__ | +| test.py:34:5:34:23 | GSSA Variable MyObj | test.py:35:20:35:24 | ControlFlowNode for MyObj | +| test.py:34:5:34:23 | GSSA Variable MyObj | test.py:35:20:35:24 | ControlFlowNode for MyObj | +| test.py:34:18:34:21 | SSA variable self | test.py:35:9:35:12 | ControlFlowNode for self | +| test.py:34:18:34:21 | SSA variable self | test.py:35:9:35:12 | ControlFlowNode for self | +| test.py:34:18:34:21 | SSA variable self | test.py:35:9:35:12 | ControlFlowNode for self | +| test.py:34:18:34:21 | SSA variable self | test.py:35:9:35:12 | ControlFlowNode for self | +| test.py:34:18:34:21 | SSA variable self | test.py:35:9:35:16 | SSA variable self | +| test.py:34:18:34:21 | SSA variable self | test.py:35:9:35:16 | SSA variable self | +| test.py:34:18:34:21 | SSA variable self | test.py:35:9:35:16 | SSA variable self | +| test.py:34:18:34:21 | SSA variable self | test.py:35:9:35:16 | SSA variable self | +| test.py:35:9:35:12 | [post store] ControlFlowNode for self | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | +| test.py:35:9:35:12 | [post store] ControlFlowNode for self | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | +| test.py:35:9:35:12 | [post store] ControlFlowNode for self | test.py:73:9:73:19 | ControlFlowNode for NestedObj() | +| test.py:35:9:35:12 | [post store] ControlFlowNode for self | test.py:73:9:73:19 | ControlFlowNode for NestedObj() | +| test.py:35:9:35:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | +| test.py:35:9:35:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | test.py:73:9:73:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | +| test.py:35:9:35:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj] | +| test.py:35:9:35:12 | [post store] ControlFlowNode for self [Attribute obj] | test.py:73:9:73:19 | ControlFlowNode for NestedObj() [Attribute obj] | +| test.py:35:20:35:30 | ControlFlowNode for MyObj() | test.py:35:9:35:12 | [post store] ControlFlowNode for self [Attribute obj] | +| test.py:35:20:35:30 | ControlFlowNode for MyObj() [Attribute foo] | test.py:35:9:35:12 | [post store] ControlFlowNode for self [Attribute obj, Attribute foo] | +| test.py:35:20:35:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:35:20:35:30 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:35:26:35:29 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | +| test.py:35:26:35:29 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | +| test.py:35:26:35:29 | ControlFlowNode for Str | test.py:35:20:35:30 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:37:5:37:21 | ControlFlowNode for FunctionExpr | test.py:37:9:37:14 | SSA variable getObj | +| test.py:37:5:37:21 | ControlFlowNode for FunctionExpr | test.py:37:9:37:14 | SSA variable getObj | +| test.py:37:16:37:19 | SSA variable self | test.py:38:16:38:19 | ControlFlowNode for self | +| test.py:37:16:37:19 | SSA variable self | test.py:38:16:38:19 | ControlFlowNode for self | +| test.py:38:16:38:23 | ControlFlowNode for Attribute | test.py:75:5:75:14 | ControlFlowNode for Attribute() | +| test.py:38:16:38:23 | ControlFlowNode for Attribute | test.py:75:5:75:14 | ControlFlowNode for Attribute() | +| test.py:41:1:41:19 | ControlFlowNode for FunctionExpr | test.py:41:5:41:10 | GSSA Variable setFoo | +| test.py:41:1:41:19 | ControlFlowNode for FunctionExpr | test.py:41:5:41:10 | GSSA Variable setFoo | +| test.py:41:1:41:19 | GSSA Variable SINK_F | test.py:42:5:42:10 | ControlFlowNode for SINK_F | +| test.py:41:1:41:19 | GSSA Variable SINK_F | test.py:42:5:42:10 | ControlFlowNode for SINK_F | +| test.py:41:5:41:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | +| test.py:41:5:41:10 | GSSA Variable setFoo | test.py:0:0:0:0 | ModuleVariableNode for Global Variable setFoo in Module test | +| test.py:41:12:41:14 | SSA variable obj | test.py:42:12:42:14 | ControlFlowNode for obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:42:12:42:14 | ControlFlowNode for obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:42:12:42:14 | ControlFlowNode for obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:42:12:42:14 | ControlFlowNode for obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:43:5:43:11 | SSA variable obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:43:5:43:11 | SSA variable obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:43:5:43:11 | SSA variable obj | +| test.py:41:12:41:14 | SSA variable obj | test.py:43:5:43:11 | SSA variable obj | +| test.py:41:12:41:14 | SSA variable obj [Attribute foo] | test.py:42:12:42:14 | ControlFlowNode for obj [Attribute foo] | +| test.py:41:17:41:17 | SSA variable x | test.py:43:15:43:15 | ControlFlowNode for x | +| test.py:41:17:41:17 | SSA variable x | test.py:43:15:43:15 | ControlFlowNode for x | +| test.py:41:17:41:17 | SSA variable x | test.py:43:15:43:15 | ControlFlowNode for x | +| test.py:41:17:41:17 | SSA variable x | test.py:43:15:43:15 | ControlFlowNode for x | +| test.py:42:12:42:14 | ControlFlowNode for obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:42:12:42:14 | ControlFlowNode for obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:42:12:42:14 | ControlFlowNode for obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:42:12:42:14 | ControlFlowNode for obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:42:12:42:14 | ControlFlowNode for obj [Attribute foo] | test.py:42:12:42:18 | ControlFlowNode for Attribute | +| test.py:42:12:42:14 | ControlFlowNode for obj [Attribute foo] | test.py:42:12:42:18 | ControlFlowNode for Attribute | +| test.py:42:12:42:14 | [post read] ControlFlowNode for obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:42:12:42:14 | [post read] ControlFlowNode for obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:42:12:42:14 | [post read] ControlFlowNode for obj | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj | +| test.py:42:12:42:14 | [post read] ControlFlowNode for obj | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj | +| test.py:42:12:42:14 | [post read] ControlFlowNode for obj [Attribute foo] | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:42:12:42:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | +| test.py:42:12:42:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | +| test.py:42:12:42:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | +| test.py:42:12:42:18 | ControlFlowNode for Attribute | test.py:17:12:17:12 | SSA variable x | +| test.py:42:12:42:18 | [post arg] ControlFlowNode for Attribute | test.py:42:12:42:14 | [post read] ControlFlowNode for obj [Attribute foo] | +| test.py:43:5:43:7 | [post store] ControlFlowNode for obj | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj | +| test.py:43:5:43:7 | [post store] ControlFlowNode for obj | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj | +| test.py:43:5:43:7 | [post store] ControlFlowNode for obj [Attribute foo] | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:43:15:43:15 | ControlFlowNode for x | test.py:43:5:43:7 | [post store] ControlFlowNode for obj [Attribute foo] | +| test.py:43:15:43:15 | ControlFlowNode for x | test.py:43:5:43:7 | [post store] ControlFlowNode for obj [Attribute foo] | +| test.py:46:1:46:20 | ControlFlowNode for FunctionExpr | test.py:46:5:46:17 | GSSA Variable test_example1 | +| test.py:46:1:46:20 | ControlFlowNode for FunctionExpr | test.py:46:5:46:17 | GSSA Variable test_example1 | +| test.py:46:1:46:20 | GSSA Variable MyObj | test.py:47:13:47:17 | ControlFlowNode for MyObj | +| test.py:46:1:46:20 | GSSA Variable MyObj | test.py:47:13:47:17 | ControlFlowNode for MyObj | +| test.py:46:1:46:20 | GSSA Variable SINK | test.py:50:5:50:8 | ControlFlowNode for SINK | +| test.py:46:1:46:20 | GSSA Variable SINK | test.py:50:5:50:8 | ControlFlowNode for SINK | +| test.py:46:1:46:20 | GSSA Variable SOURCE | test.py:49:19:49:24 | ControlFlowNode for SOURCE | +| test.py:46:1:46:20 | GSSA Variable SOURCE | test.py:49:19:49:24 | ControlFlowNode for SOURCE | +| test.py:46:1:46:20 | GSSA Variable setFoo | test.py:49:5:49:10 | ControlFlowNode for setFoo | +| test.py:46:1:46:20 | GSSA Variable setFoo | test.py:49:5:49:10 | ControlFlowNode for setFoo | +| test.py:47:5:47:9 | SSA variable myobj | test.py:49:5:49:25 | SSA variable myobj | +| test.py:47:5:47:9 | SSA variable myobj | test.py:49:5:49:25 | SSA variable myobj | +| test.py:47:5:47:9 | SSA variable myobj | test.py:49:12:49:16 | ControlFlowNode for myobj | +| test.py:47:5:47:9 | SSA variable myobj | test.py:49:12:49:16 | ControlFlowNode for myobj | +| test.py:47:5:47:9 | SSA variable myobj | test.py:50:10:50:14 | ControlFlowNode for myobj | +| test.py:47:5:47:9 | SSA variable myobj | test.py:50:10:50:14 | ControlFlowNode for myobj | +| test.py:47:5:47:9 | SSA variable myobj [Attribute foo] | test.py:49:12:49:16 | ControlFlowNode for myobj [Attribute foo] | +| test.py:47:5:47:9 | SSA variable myobj [Attribute foo] | test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() | test.py:47:5:47:9 | SSA variable myobj | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() | test.py:47:5:47:9 | SSA variable myobj | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() | test.py:49:5:49:25 | SSA variable myobj | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() | test.py:49:5:49:25 | SSA variable myobj | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() | test.py:49:12:49:16 | ControlFlowNode for myobj | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() | test.py:49:12:49:16 | ControlFlowNode for myobj | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() | test.py:50:10:50:14 | ControlFlowNode for myobj | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() | test.py:50:10:50:14 | ControlFlowNode for myobj | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:47:5:47:9 | SSA variable myobj [Attribute foo] | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:49:12:49:16 | ControlFlowNode for myobj [Attribute foo] | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:47:13:47:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:47:13:47:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:47:19:47:22 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | +| test.py:47:19:47:22 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | +| test.py:47:19:47:22 | ControlFlowNode for Str | test.py:47:13:47:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:49:12:49:16 | ControlFlowNode for myobj | test.py:41:12:41:14 | SSA variable obj | +| test.py:49:12:49:16 | ControlFlowNode for myobj | test.py:41:12:41:14 | SSA variable obj | +| test.py:49:12:49:16 | ControlFlowNode for myobj | test.py:50:10:50:14 | ControlFlowNode for myobj | +| test.py:49:12:49:16 | ControlFlowNode for myobj | test.py:50:10:50:14 | ControlFlowNode for myobj | +| test.py:49:12:49:16 | ControlFlowNode for myobj [Attribute foo] | test.py:41:12:41:14 | SSA variable obj [Attribute foo] | +| test.py:49:12:49:16 | ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj | test.py:50:10:50:14 | ControlFlowNode for myobj | +| test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj | test.py:50:10:50:14 | ControlFlowNode for myobj | +| test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:41:17:41:17 | SSA variable x | +| test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:41:17:41:17 | SSA variable x | +| test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | +| test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:18 | ControlFlowNode for Attribute | +| test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:18 | ControlFlowNode for Attribute | +| test.py:50:10:50:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:50:10:50:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:53:1:53:27 | ControlFlowNode for FunctionExpr | test.py:53:5:53:24 | GSSA Variable test_example1_method | +| test.py:53:1:53:27 | ControlFlowNode for FunctionExpr | test.py:53:5:53:24 | GSSA Variable test_example1_method | +| test.py:53:1:53:27 | GSSA Variable MyObj | test.py:54:13:54:17 | ControlFlowNode for MyObj | +| test.py:53:1:53:27 | GSSA Variable MyObj | test.py:54:13:54:17 | ControlFlowNode for MyObj | +| test.py:53:1:53:27 | GSSA Variable SINK | test.py:57:5:57:8 | ControlFlowNode for SINK | +| test.py:53:1:53:27 | GSSA Variable SINK | test.py:57:5:57:8 | ControlFlowNode for SINK | +| test.py:53:1:53:27 | GSSA Variable SOURCE | test.py:56:18:56:23 | ControlFlowNode for SOURCE | +| test.py:53:1:53:27 | GSSA Variable SOURCE | test.py:56:18:56:23 | ControlFlowNode for SOURCE | +| test.py:54:5:54:9 | SSA variable myobj | test.py:56:5:56:9 | ControlFlowNode for myobj | +| test.py:54:5:54:9 | SSA variable myobj | test.py:56:5:56:9 | ControlFlowNode for myobj | +| test.py:54:5:54:9 | SSA variable myobj | test.py:56:5:56:24 | SSA variable myobj | +| test.py:54:5:54:9 | SSA variable myobj | test.py:56:5:56:24 | SSA variable myobj | +| test.py:54:5:54:9 | SSA variable myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | +| test.py:54:5:54:9 | SSA variable myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | +| test.py:54:5:54:9 | SSA variable myobj [Attribute foo] | test.py:56:5:56:9 | ControlFlowNode for myobj [Attribute foo] | +| test.py:54:5:54:9 | SSA variable myobj [Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() | test.py:54:5:54:9 | SSA variable myobj | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() | test.py:54:5:54:9 | SSA variable myobj | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() | test.py:56:5:56:9 | ControlFlowNode for myobj | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() | test.py:56:5:56:9 | ControlFlowNode for myobj | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() | test.py:56:5:56:24 | SSA variable myobj | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() | test.py:56:5:56:24 | SSA variable myobj | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() | test.py:57:10:57:14 | ControlFlowNode for myobj | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() | test.py:57:10:57:14 | ControlFlowNode for myobj | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:54:5:54:9 | SSA variable myobj [Attribute foo] | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:56:5:56:9 | ControlFlowNode for myobj [Attribute foo] | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:54:13:54:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:54:13:54:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:54:19:54:22 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | +| test.py:54:19:54:22 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | +| test.py:54:19:54:22 | ControlFlowNode for Str | test.py:54:13:54:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:56:5:56:9 | ControlFlowNode for myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | +| test.py:56:5:56:9 | ControlFlowNode for myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | +| test.py:56:5:56:9 | ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:56:5:56:9 | [post read] ControlFlowNode for myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | +| test.py:56:5:56:9 | [post read] ControlFlowNode for myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | +| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:29:16:29:19 | SSA variable self | +| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:29:16:29:19 | SSA variable self | +| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:29:22:29:24 | SSA variable foo | +| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:29:22:29:24 | SSA variable foo | +| test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute | +| test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute | | test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | | test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:60:1:60:27 | ControlFlowNode for FunctionExpr | test.py:60:5:60:24 | GSSA Variable test_example2_method | -| test.py:60:1:60:27 | ControlFlowNode for FunctionExpr | test.py:60:5:60:24 | GSSA Variable test_example2_method | -| test.py:60:1:60:27 | GSSA Variable NestedObj | test.py:63:9:63:17 | ControlFlowNode for NestedObj | -| test.py:60:1:60:27 | GSSA Variable NestedObj | test.py:63:9:63:17 | ControlFlowNode for NestedObj | -| test.py:60:1:60:27 | GSSA Variable SINK | test.py:67:5:67:8 | ControlFlowNode for SINK | -| test.py:60:1:60:27 | GSSA Variable SINK | test.py:67:5:67:8 | ControlFlowNode for SINK | -| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:61:5:61:5 | SSA variable x | -| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:61:5:61:5 | SSA variable x | -| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:61:9:61:14 | ControlFlowNode for SOURCE | -| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:61:9:61:14 | ControlFlowNode for SOURCE | -| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:65:22:65:22 | ControlFlowNode for x | -| test.py:60:1:60:27 | GSSA Variable SOURCE | test.py:65:22:65:22 | ControlFlowNode for x | -| test.py:61:5:61:5 | SSA variable x | test.py:65:22:65:22 | ControlFlowNode for x | -| test.py:61:5:61:5 | SSA variable x | test.py:65:22:65:22 | ControlFlowNode for x | +| test.py:60:1:60:20 | ControlFlowNode for FunctionExpr | test.py:60:5:60:17 | GSSA Variable test_example2 | +| test.py:60:1:60:20 | ControlFlowNode for FunctionExpr | test.py:60:5:60:17 | GSSA Variable test_example2 | +| test.py:60:1:60:20 | GSSA Variable NestedObj | test.py:63:9:63:17 | ControlFlowNode for NestedObj | +| test.py:60:1:60:20 | GSSA Variable NestedObj | test.py:63:9:63:17 | ControlFlowNode for NestedObj | +| test.py:60:1:60:20 | GSSA Variable SINK | test.py:67:5:67:8 | ControlFlowNode for SINK | +| test.py:60:1:60:20 | GSSA Variable SINK | test.py:67:5:67:8 | ControlFlowNode for SINK | +| test.py:60:1:60:20 | GSSA Variable SOURCE | test.py:61:5:61:5 | SSA variable x | +| test.py:60:1:60:20 | GSSA Variable SOURCE | test.py:61:5:61:5 | SSA variable x | +| test.py:60:1:60:20 | GSSA Variable SOURCE | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:60:1:60:20 | GSSA Variable SOURCE | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:60:1:60:20 | GSSA Variable SOURCE | test.py:65:17:65:17 | ControlFlowNode for x | +| test.py:60:1:60:20 | GSSA Variable SOURCE | test.py:65:17:65:17 | ControlFlowNode for x | +| test.py:61:5:61:5 | SSA variable x | test.py:65:17:65:17 | ControlFlowNode for x | +| test.py:61:5:61:5 | SSA variable x | test.py:65:17:65:17 | ControlFlowNode for x | | test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:61:5:61:5 | SSA variable x | | test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:61:5:61:5 | SSA variable x | -| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:65:22:65:22 | ControlFlowNode for x | -| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:65:22:65:22 | ControlFlowNode for x | +| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:65:17:65:17 | ControlFlowNode for x | +| test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:65:17:65:17 | ControlFlowNode for x | | test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:5 | ControlFlowNode for a | | test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:5 | ControlFlowNode for a | -| test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:14 | SSA variable a | -| test.py:63:5:63:5 | SSA variable a | test.py:65:5:65:14 | SSA variable a | | test.py:63:5:63:5 | SSA variable a | test.py:67:10:67:10 | ControlFlowNode for a | | test.py:63:5:63:5 | SSA variable a | test.py:67:10:67:10 | ControlFlowNode for a | | test.py:63:5:63:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | @@ -1082,8 +1092,6 @@ | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:63:5:63:5 | SSA variable a | | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:65:5:65:5 | ControlFlowNode for a | | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:65:5:65:5 | ControlFlowNode for a | -| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:65:5:65:14 | SSA variable a | -| test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:65:5:65:14 | SSA variable a | | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:67:10:67:10 | ControlFlowNode for a | | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:67:10:67:10 | ControlFlowNode for a | | test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:63:5:63:5 | SSA variable a [Attribute obj, Attribute foo] | @@ -1092,14 +1100,21 @@ | test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:63:5:63:5 | SSA variable a [Attribute obj] | | test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj] | | test.py:63:9:63:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj] | -| test.py:63:9:63:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:31:18:31:21 | SSA variable self | -| test.py:63:9:63:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:31:18:31:21 | SSA variable self | +| test.py:63:9:63:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:34:18:34:21 | SSA variable self | +| test.py:63:9:63:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:34:18:34:21 | SSA variable self | | test.py:65:5:65:5 | ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | | test.py:65:5:65:5 | ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | | test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj] | test.py:65:5:65:9 | ControlFlowNode for Attribute | +| test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj] | test.py:65:5:65:9 | ControlFlowNode for Attribute | | test.py:65:5:65:5 | ControlFlowNode for a [Attribute obj] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj] | | test.py:65:5:65:5 | [post read] ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | | test.py:65:5:65:5 | [post read] ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | +| test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj] | +| test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute | test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj] | +| test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:65:17:65:17 | ControlFlowNode for x | test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj] | test.py:67:10:67:14 | ControlFlowNode for Attribute | | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj] | test.py:67:10:67:14 | ControlFlowNode for Attribute | @@ -1107,123 +1122,180 @@ | test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:67:10:67:18 | ControlFlowNode for Attribute | | test.py:67:10:67:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | | test.py:67:10:67:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:70:1:70:20 | ControlFlowNode for FunctionExpr | test.py:70:5:70:17 | GSSA Variable test_example3 | -| test.py:70:1:70:20 | ControlFlowNode for FunctionExpr | test.py:70:5:70:17 | GSSA Variable test_example3 | -| test.py:70:1:70:20 | GSSA Variable MyObj | test.py:71:11:71:15 | ControlFlowNode for MyObj | -| test.py:70:1:70:20 | GSSA Variable MyObj | test.py:71:11:71:15 | ControlFlowNode for MyObj | -| test.py:70:1:70:20 | GSSA Variable SINK | test.py:72:5:72:8 | ControlFlowNode for SINK | -| test.py:70:1:70:20 | GSSA Variable SINK | test.py:72:5:72:8 | ControlFlowNode for SINK | -| test.py:70:1:70:20 | GSSA Variable SOURCE | test.py:71:17:71:22 | ControlFlowNode for SOURCE | -| test.py:70:1:70:20 | GSSA Variable SOURCE | test.py:71:17:71:22 | ControlFlowNode for SOURCE | -| test.py:71:5:71:7 | SSA variable obj | test.py:72:10:72:12 | ControlFlowNode for obj | -| test.py:71:5:71:7 | SSA variable obj | test.py:72:10:72:12 | ControlFlowNode for obj | -| test.py:71:5:71:7 | SSA variable obj [Attribute foo] | test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:71:5:71:7 | SSA variable obj | -| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:71:5:71:7 | SSA variable obj | -| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:72:10:72:12 | ControlFlowNode for obj | -| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:72:10:72:12 | ControlFlowNode for obj | -| test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:71:5:71:7 | SSA variable obj [Attribute foo] | -| test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:71:11:71:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:71:11:71:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:71:17:71:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | -| test.py:71:17:71:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | -| test.py:71:17:71:22 | ControlFlowNode for SOURCE | test.py:71:11:71:23 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | test.py:72:10:72:16 | ControlFlowNode for Attribute | -| test.py:72:10:72:12 | ControlFlowNode for obj [Attribute foo] | test.py:72:10:72:16 | ControlFlowNode for Attribute | -| test.py:72:10:72:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:72:10:72:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:75:1:75:23 | ControlFlowNode for FunctionExpr | test.py:75:5:75:20 | GSSA Variable test_example3_kw | -| test.py:75:1:75:23 | ControlFlowNode for FunctionExpr | test.py:75:5:75:20 | GSSA Variable test_example3_kw | -| test.py:75:1:75:23 | GSSA Variable MyObj | test.py:76:11:76:15 | ControlFlowNode for MyObj | -| test.py:75:1:75:23 | GSSA Variable MyObj | test.py:76:11:76:15 | ControlFlowNode for MyObj | -| test.py:75:1:75:23 | GSSA Variable SINK | test.py:77:5:77:8 | ControlFlowNode for SINK | -| test.py:75:1:75:23 | GSSA Variable SINK | test.py:77:5:77:8 | ControlFlowNode for SINK | -| test.py:75:1:75:23 | GSSA Variable SOURCE | test.py:76:21:76:26 | ControlFlowNode for SOURCE | -| test.py:75:1:75:23 | GSSA Variable SOURCE | test.py:76:21:76:26 | ControlFlowNode for SOURCE | -| test.py:76:5:76:7 | SSA variable obj | test.py:77:10:77:12 | ControlFlowNode for obj | -| test.py:76:5:76:7 | SSA variable obj | test.py:77:10:77:12 | ControlFlowNode for obj | -| test.py:76:5:76:7 | SSA variable obj [Attribute foo] | test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:76:5:76:7 | SSA variable obj | -| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:76:5:76:7 | SSA variable obj | -| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:77:10:77:12 | ControlFlowNode for obj | -| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:77:10:77:12 | ControlFlowNode for obj | -| test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:76:5:76:7 | SSA variable obj [Attribute foo] | -| test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | -| test.py:76:11:76:27 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:76:11:76:27 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:76:21:76:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | -| test.py:76:21:76:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | -| test.py:76:21:76:26 | ControlFlowNode for SOURCE | test.py:76:11:76:27 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | test.py:77:10:77:16 | ControlFlowNode for Attribute | -| test.py:77:10:77:12 | ControlFlowNode for obj [Attribute foo] | test.py:77:10:77:16 | ControlFlowNode for Attribute | -| test.py:77:10:77:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:77:10:77:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | -| test.py:80:1:80:30 | ControlFlowNode for FunctionExpr | test.py:80:5:80:26 | GSSA Variable fields_with_local_flow | -| test.py:80:1:80:30 | ControlFlowNode for FunctionExpr | test.py:80:5:80:26 | GSSA Variable fields_with_local_flow | -| test.py:80:1:80:30 | GSSA Variable MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | -| test.py:80:1:80:30 | GSSA Variable MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | -| test.py:80:5:80:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | -| test.py:80:5:80:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | -| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | -| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | -| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | -| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | -| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | -| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | -| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | -| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | -| test.py:81:5:81:7 | SSA variable obj | test.py:82:9:82:11 | ControlFlowNode for obj | -| test.py:81:5:81:7 | SSA variable obj | test.py:82:9:82:11 | ControlFlowNode for obj | -| test.py:81:5:81:7 | SSA variable obj [Attribute foo] | test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:81:5:81:7 | SSA variable obj [Attribute foo] | test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:81:5:81:7 | SSA variable obj | -| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:81:5:81:7 | SSA variable obj | -| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:82:9:82:11 | ControlFlowNode for obj | -| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:82:9:82:11 | ControlFlowNode for obj | -| test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:81:5:81:7 | SSA variable obj [Attribute foo] | -| test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:81:5:81:7 | SSA variable obj [Attribute foo] | -| test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | -| test.py:81:11:81:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:81:11:81:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | -| test.py:81:17:81:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | -| test.py:81:17:81:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | -| test.py:81:17:81:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | -| test.py:81:17:81:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | -| test.py:81:17:81:17 | ControlFlowNode for x | test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:81:17:81:17 | ControlFlowNode for x | test.py:81:11:81:18 | ControlFlowNode for MyObj() [Attribute foo] | -| test.py:81:17:81:17 | [post arg] ControlFlowNode for x | test.py:87:33:87:38 | [post arg] ControlFlowNode for SOURCE | -| test.py:81:17:81:17 | [post arg] ControlFlowNode for x | test.py:87:33:87:38 | [post arg] ControlFlowNode for SOURCE | -| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | -| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | -| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | -| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | -| test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | test.py:82:9:82:15 | ControlFlowNode for Attribute | -| test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | test.py:82:9:82:15 | ControlFlowNode for Attribute | -| test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | test.py:82:9:82:15 | ControlFlowNode for Attribute | -| test.py:82:9:82:11 | ControlFlowNode for obj [Attribute foo] | test.py:82:9:82:15 | ControlFlowNode for Attribute | -| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | -| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | -| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | -| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | -| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:83:12:83:12 | ControlFlowNode for a | -| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:83:12:83:12 | ControlFlowNode for a | -| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:83:12:83:12 | ControlFlowNode for a | -| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:83:12:83:12 | ControlFlowNode for a | -| test.py:83:12:83:12 | ControlFlowNode for a | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:83:12:83:12 | ControlFlowNode for a | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:86:1:86:18 | ControlFlowNode for FunctionExpr | test.py:86:5:86:15 | GSSA Variable test_fields | -| test.py:86:1:86:18 | ControlFlowNode for FunctionExpr | test.py:86:5:86:15 | GSSA Variable test_fields | -| test.py:86:1:86:18 | GSSA Variable SINK | test.py:87:5:87:8 | ControlFlowNode for SINK | -| test.py:86:1:86:18 | GSSA Variable SINK | test.py:87:5:87:8 | ControlFlowNode for SINK | -| test.py:86:1:86:18 | GSSA Variable SOURCE | test.py:87:33:87:38 | ControlFlowNode for SOURCE | -| test.py:86:1:86:18 | GSSA Variable SOURCE | test.py:87:33:87:38 | ControlFlowNode for SOURCE | -| test.py:86:1:86:18 | GSSA Variable fields_with_local_flow | test.py:87:10:87:31 | ControlFlowNode for fields_with_local_flow | -| test.py:86:1:86:18 | GSSA Variable fields_with_local_flow | test.py:87:10:87:31 | ControlFlowNode for fields_with_local_flow | -| test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | test.py:10:10:10:10 | SSA variable x | -| test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | test.py:10:10:10:10 | SSA variable x | -| test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:80:28:80:28 | SSA variable x | -| test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:80:28:80:28 | SSA variable x | -| test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:87:33:87:38 | ControlFlowNode for SOURCE | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:70:1:70:27 | ControlFlowNode for FunctionExpr | test.py:70:5:70:24 | GSSA Variable test_example2_method | +| test.py:70:1:70:27 | ControlFlowNode for FunctionExpr | test.py:70:5:70:24 | GSSA Variable test_example2_method | +| test.py:70:1:70:27 | GSSA Variable NestedObj | test.py:73:9:73:17 | ControlFlowNode for NestedObj | +| test.py:70:1:70:27 | GSSA Variable NestedObj | test.py:73:9:73:17 | ControlFlowNode for NestedObj | +| test.py:70:1:70:27 | GSSA Variable SINK | test.py:77:5:77:8 | ControlFlowNode for SINK | +| test.py:70:1:70:27 | GSSA Variable SINK | test.py:77:5:77:8 | ControlFlowNode for SINK | +| test.py:70:1:70:27 | GSSA Variable SOURCE | test.py:71:5:71:5 | SSA variable x | +| test.py:70:1:70:27 | GSSA Variable SOURCE | test.py:71:5:71:5 | SSA variable x | +| test.py:70:1:70:27 | GSSA Variable SOURCE | test.py:71:9:71:14 | ControlFlowNode for SOURCE | +| test.py:70:1:70:27 | GSSA Variable SOURCE | test.py:71:9:71:14 | ControlFlowNode for SOURCE | +| test.py:70:1:70:27 | GSSA Variable SOURCE | test.py:75:22:75:22 | ControlFlowNode for x | +| test.py:70:1:70:27 | GSSA Variable SOURCE | test.py:75:22:75:22 | ControlFlowNode for x | +| test.py:71:5:71:5 | SSA variable x | test.py:75:22:75:22 | ControlFlowNode for x | +| test.py:71:5:71:5 | SSA variable x | test.py:75:22:75:22 | ControlFlowNode for x | +| test.py:71:9:71:14 | ControlFlowNode for SOURCE | test.py:71:5:71:5 | SSA variable x | +| test.py:71:9:71:14 | ControlFlowNode for SOURCE | test.py:71:5:71:5 | SSA variable x | +| test.py:71:9:71:14 | ControlFlowNode for SOURCE | test.py:75:22:75:22 | ControlFlowNode for x | +| test.py:71:9:71:14 | ControlFlowNode for SOURCE | test.py:75:22:75:22 | ControlFlowNode for x | +| test.py:73:5:73:5 | SSA variable a | test.py:75:5:75:5 | ControlFlowNode for a | +| test.py:73:5:73:5 | SSA variable a | test.py:75:5:75:5 | ControlFlowNode for a | +| test.py:73:5:73:5 | SSA variable a | test.py:75:5:75:14 | SSA variable a | +| test.py:73:5:73:5 | SSA variable a | test.py:75:5:75:14 | SSA variable a | +| test.py:73:5:73:5 | SSA variable a | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:73:5:73:5 | SSA variable a | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:73:5:73:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:75:5:75:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:73:5:73:5 | SSA variable a [Attribute obj, Attribute foo] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:73:5:73:5 | SSA variable a [Attribute obj] | test.py:75:5:75:5 | ControlFlowNode for a [Attribute obj] | +| test.py:73:5:73:5 | SSA variable a [Attribute obj] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj] | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() | test.py:73:5:73:5 | SSA variable a | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() | test.py:73:5:73:5 | SSA variable a | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() | test.py:75:5:75:5 | ControlFlowNode for a | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() | test.py:75:5:75:5 | ControlFlowNode for a | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() | test.py:75:5:75:14 | SSA variable a | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() | test.py:75:5:75:14 | SSA variable a | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:73:5:73:5 | SSA variable a [Attribute obj, Attribute foo] | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:75:5:75:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() [Attribute obj, Attribute foo] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:73:5:73:5 | SSA variable a [Attribute obj] | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:75:5:75:5 | ControlFlowNode for a [Attribute obj] | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj] | +| test.py:73:9:73:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:34:18:34:21 | SSA variable self | +| test.py:73:9:73:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:34:18:34:21 | SSA variable self | +| test.py:75:5:75:5 | ControlFlowNode for a | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:75:5:75:5 | ControlFlowNode for a | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:75:5:75:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:75:5:75:5 | ControlFlowNode for a [Attribute obj] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj] | +| test.py:75:5:75:5 | [post read] ControlFlowNode for a | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:75:5:75:5 | [post read] ControlFlowNode for a | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] | +| test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj] | test.py:77:10:77:14 | ControlFlowNode for Attribute | +| test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj] | test.py:77:10:77:14 | ControlFlowNode for Attribute | +| test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:77:10:77:18 | ControlFlowNode for Attribute | +| test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:77:10:77:18 | ControlFlowNode for Attribute | +| test.py:77:10:77:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:77:10:77:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:80:1:80:20 | ControlFlowNode for FunctionExpr | test.py:80:5:80:17 | GSSA Variable test_example3 | +| test.py:80:1:80:20 | ControlFlowNode for FunctionExpr | test.py:80:5:80:17 | GSSA Variable test_example3 | +| test.py:80:1:80:20 | GSSA Variable MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | +| test.py:80:1:80:20 | GSSA Variable MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | +| test.py:80:1:80:20 | GSSA Variable SINK | test.py:82:5:82:8 | ControlFlowNode for SINK | +| test.py:80:1:80:20 | GSSA Variable SINK | test.py:82:5:82:8 | ControlFlowNode for SINK | +| test.py:80:1:80:20 | GSSA Variable SOURCE | test.py:81:17:81:22 | ControlFlowNode for SOURCE | +| test.py:80:1:80:20 | GSSA Variable SOURCE | test.py:81:17:81:22 | ControlFlowNode for SOURCE | +| test.py:81:5:81:7 | SSA variable obj | test.py:82:10:82:12 | ControlFlowNode for obj | +| test.py:81:5:81:7 | SSA variable obj | test.py:82:10:82:12 | ControlFlowNode for obj | +| test.py:81:5:81:7 | SSA variable obj [Attribute foo] | test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:81:11:81:23 | ControlFlowNode for MyObj() | test.py:81:5:81:7 | SSA variable obj | +| test.py:81:11:81:23 | ControlFlowNode for MyObj() | test.py:81:5:81:7 | SSA variable obj | +| test.py:81:11:81:23 | ControlFlowNode for MyObj() | test.py:82:10:82:12 | ControlFlowNode for obj | +| test.py:81:11:81:23 | ControlFlowNode for MyObj() | test.py:82:10:82:12 | ControlFlowNode for obj | +| test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:81:5:81:7 | SSA variable obj [Attribute foo] | +| test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:81:11:81:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:81:11:81:23 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | test.py:82:10:82:16 | ControlFlowNode for Attribute | +| test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | test.py:82:10:82:16 | ControlFlowNode for Attribute | +| test.py:82:10:82:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:82:10:82:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:85:1:85:23 | ControlFlowNode for FunctionExpr | test.py:85:5:85:20 | GSSA Variable test_example3_kw | +| test.py:85:1:85:23 | ControlFlowNode for FunctionExpr | test.py:85:5:85:20 | GSSA Variable test_example3_kw | +| test.py:85:1:85:23 | GSSA Variable MyObj | test.py:86:11:86:15 | ControlFlowNode for MyObj | +| test.py:85:1:85:23 | GSSA Variable MyObj | test.py:86:11:86:15 | ControlFlowNode for MyObj | +| test.py:85:1:85:23 | GSSA Variable SINK | test.py:87:5:87:8 | ControlFlowNode for SINK | +| test.py:85:1:85:23 | GSSA Variable SINK | test.py:87:5:87:8 | ControlFlowNode for SINK | +| test.py:85:1:85:23 | GSSA Variable SOURCE | test.py:86:21:86:26 | ControlFlowNode for SOURCE | +| test.py:85:1:85:23 | GSSA Variable SOURCE | test.py:86:21:86:26 | ControlFlowNode for SOURCE | +| test.py:86:5:86:7 | SSA variable obj | test.py:87:10:87:12 | ControlFlowNode for obj | +| test.py:86:5:86:7 | SSA variable obj | test.py:87:10:87:12 | ControlFlowNode for obj | +| test.py:86:5:86:7 | SSA variable obj [Attribute foo] | test.py:87:10:87:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:86:11:86:27 | ControlFlowNode for MyObj() | test.py:86:5:86:7 | SSA variable obj | +| test.py:86:11:86:27 | ControlFlowNode for MyObj() | test.py:86:5:86:7 | SSA variable obj | +| test.py:86:11:86:27 | ControlFlowNode for MyObj() | test.py:87:10:87:12 | ControlFlowNode for obj | +| test.py:86:11:86:27 | ControlFlowNode for MyObj() | test.py:87:10:87:12 | ControlFlowNode for obj | +| test.py:86:11:86:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:86:5:86:7 | SSA variable obj [Attribute foo] | +| test.py:86:11:86:27 | ControlFlowNode for MyObj() [Attribute foo] | test.py:87:10:87:12 | ControlFlowNode for obj [Attribute foo] | +| test.py:86:11:86:27 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:86:11:86:27 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:86:21:86:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:86:21:86:26 | ControlFlowNode for SOURCE | test.py:26:24:26:26 | SSA variable foo | +| test.py:86:21:86:26 | ControlFlowNode for SOURCE | test.py:86:11:86:27 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:87:10:87:12 | ControlFlowNode for obj [Attribute foo] | test.py:87:10:87:16 | ControlFlowNode for Attribute | +| test.py:87:10:87:12 | ControlFlowNode for obj [Attribute foo] | test.py:87:10:87:16 | ControlFlowNode for Attribute | +| test.py:87:10:87:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:87:10:87:16 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | +| test.py:90:1:90:30 | ControlFlowNode for FunctionExpr | test.py:90:5:90:26 | GSSA Variable fields_with_local_flow | +| test.py:90:1:90:30 | ControlFlowNode for FunctionExpr | test.py:90:5:90:26 | GSSA Variable fields_with_local_flow | +| test.py:90:1:90:30 | GSSA Variable MyObj | test.py:91:11:91:15 | ControlFlowNode for MyObj | +| test.py:90:1:90:30 | GSSA Variable MyObj | test.py:91:11:91:15 | ControlFlowNode for MyObj | +| test.py:90:5:90:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | +| test.py:90:5:90:26 | GSSA Variable fields_with_local_flow | test.py:0:0:0:0 | ModuleVariableNode for Global Variable fields_with_local_flow in Module test | +| test.py:90:28:90:28 | SSA variable x | test.py:91:11:91:18 | SSA variable x | +| test.py:90:28:90:28 | SSA variable x | test.py:91:11:91:18 | SSA variable x | +| test.py:90:28:90:28 | SSA variable x | test.py:91:11:91:18 | SSA variable x | +| test.py:90:28:90:28 | SSA variable x | test.py:91:11:91:18 | SSA variable x | +| test.py:90:28:90:28 | SSA variable x | test.py:91:17:91:17 | ControlFlowNode for x | +| test.py:90:28:90:28 | SSA variable x | test.py:91:17:91:17 | ControlFlowNode for x | +| test.py:90:28:90:28 | SSA variable x | test.py:91:17:91:17 | ControlFlowNode for x | +| test.py:90:28:90:28 | SSA variable x | test.py:91:17:91:17 | ControlFlowNode for x | +| test.py:91:5:91:7 | SSA variable obj | test.py:92:9:92:11 | ControlFlowNode for obj | +| test.py:91:5:91:7 | SSA variable obj | test.py:92:9:92:11 | ControlFlowNode for obj | +| test.py:91:5:91:7 | SSA variable obj [Attribute foo] | test.py:92:9:92:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:91:5:91:7 | SSA variable obj [Attribute foo] | test.py:92:9:92:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:91:11:91:18 | ControlFlowNode for MyObj() | test.py:91:5:91:7 | SSA variable obj | +| test.py:91:11:91:18 | ControlFlowNode for MyObj() | test.py:91:5:91:7 | SSA variable obj | +| test.py:91:11:91:18 | ControlFlowNode for MyObj() | test.py:92:9:92:11 | ControlFlowNode for obj | +| test.py:91:11:91:18 | ControlFlowNode for MyObj() | test.py:92:9:92:11 | ControlFlowNode for obj | +| test.py:91:11:91:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:91:5:91:7 | SSA variable obj [Attribute foo] | +| test.py:91:11:91:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:91:5:91:7 | SSA variable obj [Attribute foo] | +| test.py:91:11:91:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:92:9:92:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:91:11:91:18 | ControlFlowNode for MyObj() [Attribute foo] | test.py:92:9:92:11 | ControlFlowNode for obj [Attribute foo] | +| test.py:91:11:91:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:91:11:91:18 | [pre objCreate] ControlFlowNode for MyObj() | test.py:26:18:26:21 | SSA variable self | +| test.py:91:17:91:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:91:17:91:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:91:17:91:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:91:17:91:17 | ControlFlowNode for x | test.py:26:24:26:26 | SSA variable foo | +| test.py:91:17:91:17 | ControlFlowNode for x | test.py:91:11:91:18 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:91:17:91:17 | ControlFlowNode for x | test.py:91:11:91:18 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:91:17:91:17 | [post arg] ControlFlowNode for x | test.py:97:33:97:38 | [post arg] ControlFlowNode for SOURCE | +| test.py:91:17:91:17 | [post arg] ControlFlowNode for x | test.py:97:33:97:38 | [post arg] ControlFlowNode for SOURCE | +| test.py:92:5:92:5 | SSA variable a | test.py:93:12:93:12 | ControlFlowNode for a | +| test.py:92:5:92:5 | SSA variable a | test.py:93:12:93:12 | ControlFlowNode for a | +| test.py:92:5:92:5 | SSA variable a | test.py:93:12:93:12 | ControlFlowNode for a | +| test.py:92:5:92:5 | SSA variable a | test.py:93:12:93:12 | ControlFlowNode for a | +| test.py:92:9:92:11 | ControlFlowNode for obj [Attribute foo] | test.py:92:9:92:15 | ControlFlowNode for Attribute | +| test.py:92:9:92:11 | ControlFlowNode for obj [Attribute foo] | test.py:92:9:92:15 | ControlFlowNode for Attribute | +| test.py:92:9:92:11 | ControlFlowNode for obj [Attribute foo] | test.py:92:9:92:15 | ControlFlowNode for Attribute | +| test.py:92:9:92:11 | ControlFlowNode for obj [Attribute foo] | test.py:92:9:92:15 | ControlFlowNode for Attribute | +| test.py:92:9:92:15 | ControlFlowNode for Attribute | test.py:92:5:92:5 | SSA variable a | +| test.py:92:9:92:15 | ControlFlowNode for Attribute | test.py:92:5:92:5 | SSA variable a | +| test.py:92:9:92:15 | ControlFlowNode for Attribute | test.py:92:5:92:5 | SSA variable a | +| test.py:92:9:92:15 | ControlFlowNode for Attribute | test.py:92:5:92:5 | SSA variable a | +| test.py:92:9:92:15 | ControlFlowNode for Attribute | test.py:93:12:93:12 | ControlFlowNode for a | +| test.py:92:9:92:15 | ControlFlowNode for Attribute | test.py:93:12:93:12 | ControlFlowNode for a | +| test.py:92:9:92:15 | ControlFlowNode for Attribute | test.py:93:12:93:12 | ControlFlowNode for a | +| test.py:92:9:92:15 | ControlFlowNode for Attribute | test.py:93:12:93:12 | ControlFlowNode for a | +| test.py:93:12:93:12 | ControlFlowNode for a | test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:93:12:93:12 | ControlFlowNode for a | test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:96:1:96:18 | ControlFlowNode for FunctionExpr | test.py:96:5:96:15 | GSSA Variable test_fields | +| test.py:96:1:96:18 | ControlFlowNode for FunctionExpr | test.py:96:5:96:15 | GSSA Variable test_fields | +| test.py:96:1:96:18 | GSSA Variable SINK | test.py:97:5:97:8 | ControlFlowNode for SINK | +| test.py:96:1:96:18 | GSSA Variable SINK | test.py:97:5:97:8 | ControlFlowNode for SINK | +| test.py:96:1:96:18 | GSSA Variable SOURCE | test.py:97:33:97:38 | ControlFlowNode for SOURCE | +| test.py:96:1:96:18 | GSSA Variable SOURCE | test.py:97:33:97:38 | ControlFlowNode for SOURCE | +| test.py:96:1:96:18 | GSSA Variable fields_with_local_flow | test.py:97:10:97:31 | ControlFlowNode for fields_with_local_flow | +| test.py:96:1:96:18 | GSSA Variable fields_with_local_flow | test.py:97:10:97:31 | ControlFlowNode for fields_with_local_flow | +| test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | test.py:10:10:10:10 | SSA variable x | +| test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | test.py:10:10:10:10 | SSA variable x | +| test.py:97:33:97:38 | ControlFlowNode for SOURCE | test.py:90:28:90:28 | SSA variable x | +| test.py:97:33:97:38 | ControlFlowNode for SOURCE | test.py:90:28:90:28 | SSA variable x | +| test.py:97:33:97:38 | ControlFlowNode for SOURCE | test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:97:33:97:38 | ControlFlowNode for SOURCE | test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | diff --git a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected index 9fd5d5ee7a3..548767b1531 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/localFlow.expected @@ -5,10 +5,10 @@ | examples.py:54:11:54:18 | ControlFlowNode for MyObj() | examples.py:54:5:54:7 | SSA variable obj | | examples.py:55:5:55:5 | SSA variable a | examples.py:56:12:56:12 | ControlFlowNode for a | | examples.py:55:9:55:15 | ControlFlowNode for Attribute | examples.py:55:5:55:5 | SSA variable a | -| test.py:80:1:80:30 | GSSA Variable MyObj | test.py:81:11:81:15 | ControlFlowNode for MyObj | -| test.py:80:28:80:28 | SSA variable x | test.py:81:11:81:18 | SSA variable x | -| test.py:80:28:80:28 | SSA variable x | test.py:81:17:81:17 | ControlFlowNode for x | -| test.py:81:5:81:7 | SSA variable obj | test.py:82:9:82:11 | ControlFlowNode for obj | -| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:81:5:81:7 | SSA variable obj | -| test.py:82:5:82:5 | SSA variable a | test.py:83:12:83:12 | ControlFlowNode for a | -| test.py:82:9:82:15 | ControlFlowNode for Attribute | test.py:82:5:82:5 | SSA variable a | +| test.py:90:1:90:30 | GSSA Variable MyObj | test.py:91:11:91:15 | ControlFlowNode for MyObj | +| test.py:90:28:90:28 | SSA variable x | test.py:91:11:91:18 | SSA variable x | +| test.py:90:28:90:28 | SSA variable x | test.py:91:17:91:17 | ControlFlowNode for x | +| test.py:91:5:91:7 | SSA variable obj | test.py:92:9:92:11 | ControlFlowNode for obj | +| test.py:91:11:91:18 | ControlFlowNode for MyObj() | test.py:91:5:91:7 | SSA variable obj | +| test.py:92:5:92:5 | SSA variable a | test.py:93:12:93:12 | ControlFlowNode for a | +| test.py:92:9:92:15 | ControlFlowNode for Attribute | test.py:92:5:92:5 | SSA variable a | diff --git a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected index 71daa2a0b78..1b2e545d192 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/postupdates.expected @@ -33,41 +33,48 @@ | test.py:19:34:19:34 | [post arg] ControlFlowNode for x | test.py:19:34:19:34 | ControlFlowNode for x | | test.py:21:15:21:18 | [post arg] ControlFlowNode for Str | test.py:21:15:21:18 | ControlFlowNode for Str | | test.py:27:9:27:12 | [post store] ControlFlowNode for self | test.py:27:9:27:12 | ControlFlowNode for self | -| test.py:32:9:32:12 | [post store] ControlFlowNode for self | test.py:32:9:32:12 | ControlFlowNode for self | -| test.py:32:20:32:30 | ControlFlowNode for MyObj() | test.py:32:20:32:30 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:32:26:32:29 | [post arg] ControlFlowNode for Str | test.py:32:26:32:29 | ControlFlowNode for Str | -| test.py:35:16:35:19 | [post read] ControlFlowNode for self | test.py:35:16:35:19 | ControlFlowNode for self | -| test.py:39:12:39:14 | [post read] ControlFlowNode for obj | test.py:39:12:39:14 | ControlFlowNode for obj | -| test.py:39:12:39:18 | [post arg] ControlFlowNode for Attribute | test.py:39:12:39:18 | ControlFlowNode for Attribute | -| test.py:40:5:40:7 | [post store] ControlFlowNode for obj | test.py:40:5:40:7 | ControlFlowNode for obj | -| test.py:44:13:44:23 | ControlFlowNode for MyObj() | test.py:44:13:44:23 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:44:19:44:22 | [post arg] ControlFlowNode for Str | test.py:44:19:44:22 | ControlFlowNode for Str | -| test.py:46:12:46:16 | [post arg] ControlFlowNode for myobj | test.py:46:12:46:16 | ControlFlowNode for myobj | -| test.py:46:19:46:24 | [post arg] ControlFlowNode for SOURCE | test.py:46:19:46:24 | ControlFlowNode for SOURCE | -| test.py:47:10:47:14 | [post read] ControlFlowNode for myobj | test.py:47:10:47:14 | ControlFlowNode for myobj | -| test.py:47:10:47:18 | [post arg] ControlFlowNode for Attribute | test.py:47:10:47:18 | ControlFlowNode for Attribute | -| test.py:53:9:53:19 | ControlFlowNode for NestedObj() | test.py:53:9:53:19 | [pre objCreate] ControlFlowNode for NestedObj() | -| test.py:55:5:55:5 | [post read] ControlFlowNode for a | test.py:55:5:55:5 | ControlFlowNode for a | -| test.py:55:5:55:9 | [post store] ControlFlowNode for Attribute | test.py:55:5:55:9 | ControlFlowNode for Attribute | -| test.py:57:10:57:10 | [post read] ControlFlowNode for a | test.py:57:10:57:10 | ControlFlowNode for a | -| test.py:57:10:57:14 | [post read] ControlFlowNode for Attribute | test.py:57:10:57:14 | ControlFlowNode for Attribute | +| test.py:30:9:30:12 | [post store] ControlFlowNode for self | test.py:30:9:30:12 | ControlFlowNode for self | +| test.py:35:9:35:12 | [post store] ControlFlowNode for self | test.py:35:9:35:12 | ControlFlowNode for self | +| test.py:35:20:35:30 | ControlFlowNode for MyObj() | test.py:35:20:35:30 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:35:26:35:29 | [post arg] ControlFlowNode for Str | test.py:35:26:35:29 | ControlFlowNode for Str | +| test.py:38:16:38:19 | [post read] ControlFlowNode for self | test.py:38:16:38:19 | ControlFlowNode for self | +| test.py:42:12:42:14 | [post read] ControlFlowNode for obj | test.py:42:12:42:14 | ControlFlowNode for obj | +| test.py:42:12:42:18 | [post arg] ControlFlowNode for Attribute | test.py:42:12:42:18 | ControlFlowNode for Attribute | +| test.py:43:5:43:7 | [post store] ControlFlowNode for obj | test.py:43:5:43:7 | ControlFlowNode for obj | +| test.py:47:13:47:23 | ControlFlowNode for MyObj() | test.py:47:13:47:23 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:47:19:47:22 | [post arg] ControlFlowNode for Str | test.py:47:19:47:22 | ControlFlowNode for Str | +| test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj | test.py:49:12:49:16 | ControlFlowNode for myobj | +| test.py:49:19:49:24 | [post arg] ControlFlowNode for SOURCE | test.py:49:19:49:24 | ControlFlowNode for SOURCE | +| test.py:50:10:50:14 | [post read] ControlFlowNode for myobj | test.py:50:10:50:14 | ControlFlowNode for myobj | +| test.py:50:10:50:18 | [post arg] ControlFlowNode for Attribute | test.py:50:10:50:18 | ControlFlowNode for Attribute | +| test.py:54:13:54:23 | ControlFlowNode for MyObj() | test.py:54:13:54:23 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:54:19:54:22 | [post arg] ControlFlowNode for Str | test.py:54:19:54:22 | ControlFlowNode for Str | +| test.py:56:5:56:9 | [post read] ControlFlowNode for myobj | test.py:56:5:56:9 | ControlFlowNode for myobj | +| test.py:56:18:56:23 | [post arg] ControlFlowNode for SOURCE | test.py:56:18:56:23 | ControlFlowNode for SOURCE | +| test.py:57:10:57:14 | [post read] ControlFlowNode for myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | | test.py:57:10:57:18 | [post arg] ControlFlowNode for Attribute | test.py:57:10:57:18 | ControlFlowNode for Attribute | | test.py:63:9:63:19 | ControlFlowNode for NestedObj() | test.py:63:9:63:19 | [pre objCreate] ControlFlowNode for NestedObj() | | test.py:65:5:65:5 | [post read] ControlFlowNode for a | test.py:65:5:65:5 | ControlFlowNode for a | -| test.py:65:5:65:14 | [post store] ControlFlowNode for Attribute() | test.py:65:5:65:14 | ControlFlowNode for Attribute() | +| test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute | test.py:65:5:65:9 | ControlFlowNode for Attribute | | test.py:67:10:67:10 | [post read] ControlFlowNode for a | test.py:67:10:67:10 | ControlFlowNode for a | | test.py:67:10:67:14 | [post read] ControlFlowNode for Attribute | test.py:67:10:67:14 | ControlFlowNode for Attribute | | test.py:67:10:67:18 | [post arg] ControlFlowNode for Attribute | test.py:67:10:67:18 | ControlFlowNode for Attribute | -| test.py:71:11:71:23 | ControlFlowNode for MyObj() | test.py:71:11:71:23 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:71:17:71:22 | [post arg] ControlFlowNode for SOURCE | test.py:71:17:71:22 | ControlFlowNode for SOURCE | -| test.py:72:10:72:12 | [post read] ControlFlowNode for obj | test.py:72:10:72:12 | ControlFlowNode for obj | -| test.py:72:10:72:16 | [post arg] ControlFlowNode for Attribute | test.py:72:10:72:16 | ControlFlowNode for Attribute | -| test.py:76:11:76:27 | ControlFlowNode for MyObj() | test.py:76:11:76:27 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:76:21:76:26 | [post arg] ControlFlowNode for SOURCE | test.py:76:21:76:26 | ControlFlowNode for SOURCE | -| test.py:77:10:77:12 | [post read] ControlFlowNode for obj | test.py:77:10:77:12 | ControlFlowNode for obj | -| test.py:77:10:77:16 | [post arg] ControlFlowNode for Attribute | test.py:77:10:77:16 | ControlFlowNode for Attribute | -| test.py:81:11:81:18 | ControlFlowNode for MyObj() | test.py:81:11:81:18 | [pre objCreate] ControlFlowNode for MyObj() | -| test.py:81:17:81:17 | [post arg] ControlFlowNode for x | test.py:81:17:81:17 | ControlFlowNode for x | -| test.py:82:9:82:11 | [post read] ControlFlowNode for obj | test.py:82:9:82:11 | ControlFlowNode for obj | -| test.py:87:10:87:39 | [post arg] ControlFlowNode for fields_with_local_flow() | test.py:87:10:87:39 | ControlFlowNode for fields_with_local_flow() | -| test.py:87:33:87:38 | [post arg] ControlFlowNode for SOURCE | test.py:87:33:87:38 | ControlFlowNode for SOURCE | +| test.py:73:9:73:19 | ControlFlowNode for NestedObj() | test.py:73:9:73:19 | [pre objCreate] ControlFlowNode for NestedObj() | +| test.py:75:5:75:5 | [post read] ControlFlowNode for a | test.py:75:5:75:5 | ControlFlowNode for a | +| test.py:75:5:75:14 | [post store] ControlFlowNode for Attribute() | test.py:75:5:75:14 | ControlFlowNode for Attribute() | +| test.py:77:10:77:10 | [post read] ControlFlowNode for a | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:77:10:77:14 | [post read] ControlFlowNode for Attribute | test.py:77:10:77:14 | ControlFlowNode for Attribute | +| test.py:77:10:77:18 | [post arg] ControlFlowNode for Attribute | test.py:77:10:77:18 | ControlFlowNode for Attribute | +| test.py:81:11:81:23 | ControlFlowNode for MyObj() | test.py:81:11:81:23 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:81:17:81:22 | [post arg] ControlFlowNode for SOURCE | test.py:81:17:81:22 | ControlFlowNode for SOURCE | +| test.py:82:10:82:12 | [post read] ControlFlowNode for obj | test.py:82:10:82:12 | ControlFlowNode for obj | +| test.py:82:10:82:16 | [post arg] ControlFlowNode for Attribute | test.py:82:10:82:16 | ControlFlowNode for Attribute | +| test.py:86:11:86:27 | ControlFlowNode for MyObj() | test.py:86:11:86:27 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:86:21:86:26 | [post arg] ControlFlowNode for SOURCE | test.py:86:21:86:26 | ControlFlowNode for SOURCE | +| test.py:87:10:87:12 | [post read] ControlFlowNode for obj | test.py:87:10:87:12 | ControlFlowNode for obj | +| test.py:87:10:87:16 | [post arg] ControlFlowNode for Attribute | test.py:87:10:87:16 | ControlFlowNode for Attribute | +| test.py:91:11:91:18 | ControlFlowNode for MyObj() | test.py:91:11:91:18 | [pre objCreate] ControlFlowNode for MyObj() | +| test.py:91:17:91:17 | [post arg] ControlFlowNode for x | test.py:91:17:91:17 | ControlFlowNode for x | +| test.py:92:9:92:11 | [post read] ControlFlowNode for obj | test.py:92:9:92:11 | ControlFlowNode for obj | +| test.py:97:10:97:39 | [post arg] ControlFlowNode for fields_with_local_flow() | test.py:97:10:97:39 | ControlFlowNode for fields_with_local_flow() | +| test.py:97:33:97:38 | [post arg] ControlFlowNode for SOURCE | test.py:97:33:97:38 | ControlFlowNode for SOURCE | diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py index d5d613d53c0..0054d9f66b7 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/test.py +++ b/python/ql/test/experimental/dataflow/fieldflow/test.py @@ -26,6 +26,9 @@ class MyObj(object): def __init__(self, foo): self.foo = foo + def setFoo(self, foo): + self.foo = foo + class NestedObj(object): def __init__(self): @@ -47,6 +50,13 @@ def test_example1(): SINK(myobj.foo) +def test_example1_method(): + myobj = MyObj("OK") + + myobj.setFoo(SOURCE) + SINK(myobj.foo) + + def test_example2(): x = SOURCE From bd32faf934bd26957a16a0aa2ac092c5759d2342 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Fri, 2 Oct 2020 10:06:54 +0200 Subject: [PATCH 023/166] Python: annotate new test --- python/ql/test/experimental/dataflow/fieldflow/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py index 0054d9f66b7..87b7b49dd0f 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/test.py +++ b/python/ql/test/experimental/dataflow/fieldflow/test.py @@ -54,7 +54,7 @@ def test_example1_method(): myobj = MyObj("OK") myobj.setFoo(SOURCE) - SINK(myobj.foo) + SINK(myobj.foo) # Flow not found def test_example2(): From ce18bff274f9135765dd6328baf333b50c5a668a Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Sat, 3 Oct 2020 23:34:39 +0200 Subject: [PATCH 024/166] Python: Support method calls --- .../dataflow/internal/DataFlowPrivate.qll | 61 +++++++++++++++++-- .../dataflow/fieldflow/dataflow.expected | 26 ++++++++ .../dataflow/fieldflow/globalStep.expected | 30 +++++++-- .../experimental/dataflow/fieldflow/test.py | 4 +- 4 files changed, 111 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 8e7223ea5aa..8a181359b3a 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -50,6 +50,9 @@ class ArgumentPreUpdateNode extends NeedsSyntheticPostUpdateNode, ArgumentNode { ArgumentPreUpdateNode() { this = any(CallNodeCall c).getArg(_) or + // this = any(BoundMethodCall c).getArg(_) + exists(BoundMethodCall c, int n | n > 0 | this = c.getArg(n)) + or this = any(SpecialCall c).getArg(_) or // Avoid argument 0 of class calls as those have non-synthetic post-update nodes. @@ -253,8 +256,12 @@ module ArgumentPassing { */ NameNode getParameter(CallableValue callable, int n) { // positional parameter + // bound method values have their positional parameters shifted. + not callable instanceof BoundMethodValue and result = callable.getParameter(n) or + result = callable.(BoundMethodValue).getParameter(n - 1) + or // starred parameter, `*tuple` exists(Function f | f = callable.getScope() and @@ -368,7 +375,11 @@ import ArgumentPassing * A module has no calls. */ newtype TDataFlowCallable = - TCallableValue(CallableValue callable) or + TCallableValue(CallableValue callable) { + callable instanceof FunctionValue + or + callable instanceof ClassValue + } or TModule(Module m) /** Represents a callable. */ @@ -443,7 +454,9 @@ class DataFlowModuleScope extends DataFlowCallable, TModule { * A call corresponding to a special method call is handled by the corresponding `SpecialMethodCallNode`. */ newtype TDataFlowCall = - TCallNode(CallNode call) { call = any(CallableValue c).getACall() } or + TCallNode(CallNode call) { call = any(FunctionValue f).getAFunctionCall() } or + /** Bound methods need to make room for the explicit self parameter */ + TBoundMethodCall(CallNode call) { call = any(FunctionValue f).getAMethodCall() } or TClassCall(CallNode call) { call = any(ClassValue c).getACall() } or TSpecialCall(SpecialMethodCallNode special) @@ -471,7 +484,12 @@ abstract class DataFlowCall extends TDataFlowCall { Location getLocation() { result = this.getNode().getLocation() } } -/** Represents a call to a callable (currently only callable values). */ +/** + * Represents a call to a callable (currently only callable values). + * This excludes calls to bound methods, classes, and special methods. + * Bound method calls and class calls insert an argument for the explicit + * `self` parameter, and special method calls have special argument passing. + */ class CallNodeCall extends DataFlowCall, TCallNode { CallNode call; DataFlowCallable callable; @@ -492,7 +510,42 @@ class CallNodeCall extends DataFlowCall, TCallNode { override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() } } -/** Represents a call to a class. */ +/** + * Represents a call to a bound method call. + * The node representing the instance is inserted as argument to the `self` parameter. + */ +class BoundMethodCall extends DataFlowCall, TBoundMethodCall { + CallNode call; + FunctionValue bm; + + BoundMethodCall() { + this = TBoundMethodCall(call) and + call = bm.getACall() + } + + private CallableValue getCallableValue() { result = bm } + + override string toString() { result = call.toString() } + + override Node getArg(int n) { + n > 0 and result = getArg(call, n - 1, this.getCallableValue(), n) + or + n = 0 and result = TCfgNode(call.getFunction().(AttrNode).getObject()) + } + + override ControlFlowNode getNode() { result = call } + + override DataFlowCallable getCallable() { result = TCallableValue(this.getCallableValue()) } + + override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getScope() } +} + +/** + * Represents a call to a class. + * The pre-update node for the call is inserted as argument to the `self` parameter. + * That makes the call node be the post-update node holding the value of the object + * after the constructor has run. + */ class ClassCall extends DataFlowCall, TClassCall { CallNode call; ClassValue c; diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected index 0431fc802ff..168ff7e66ce 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow.expected @@ -21,7 +21,9 @@ edges | examples.py:50:6:50:8 | ControlFlowNode for obj [Attribute foo] | examples.py:50:6:50:12 | ControlFlowNode for Attribute | | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:49:19:49:24 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:56:18:56:23 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:61:9:61:14 | ControlFlowNode for SOURCE | +| test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:71:9:71:14 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:81:17:81:22 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:86:21:86:26 | ControlFlowNode for SOURCE | | test.py:0:0:0:0 | ModuleVariableNode for Global Variable SOURCE in Module test | test.py:97:33:97:38 | ControlFlowNode for SOURCE | @@ -30,12 +32,21 @@ edges | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | | test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:49:12:49:16 | [post arg] ControlFlowNode for myobj [Attribute foo] | | test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | test.py:50:10:50:18 | ControlFlowNode for Attribute | +| test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | +| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] | +| test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute | | test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:65:17:65:17 | ControlFlowNode for x | | test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:65:17:65:17 | ControlFlowNode for x | test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | | test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:67:10:67:18 | ControlFlowNode for Attribute | +| test.py:71:9:71:14 | ControlFlowNode for SOURCE | test.py:75:22:75:22 | ControlFlowNode for x | +| test.py:75:5:75:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:75:5:75:14 | [post store] ControlFlowNode for Attribute() [Attribute foo] | test.py:75:5:75:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:75:22:75:22 | ControlFlowNode for x | test.py:75:5:75:14 | [post store] ControlFlowNode for Attribute() [Attribute foo] | +| test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] | +| test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] | test.py:77:10:77:18 | ControlFlowNode for Attribute | | test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | | test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | | test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | test.py:82:10:82:16 | ControlFlowNode for Attribute | @@ -69,6 +80,10 @@ nodes | test.py:49:19:49:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:50:10:50:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] | | test.py:50:10:50:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] | semmle.label | [post read] ControlFlowNode for myobj [Attribute foo] | +| test.py:56:18:56:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | semmle.label | ControlFlowNode for myobj [Attribute foo] | +| test.py:57:10:57:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | test.py:61:9:61:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:65:5:65:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:65:5:65:9 | [post store] ControlFlowNode for Attribute [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute [Attribute foo] | @@ -76,6 +91,13 @@ nodes | test.py:67:10:67:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | | test.py:67:10:67:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | | test.py:67:10:67:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:71:9:71:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | +| test.py:75:5:75:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:75:5:75:14 | [post store] ControlFlowNode for Attribute() [Attribute foo] | semmle.label | [post store] ControlFlowNode for Attribute() [Attribute foo] | +| test.py:75:22:75:22 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | semmle.label | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] | semmle.label | ControlFlowNode for Attribute [Attribute foo] | +| test.py:77:10:77:18 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | test.py:81:11:81:23 | ControlFlowNode for MyObj() [Attribute foo] | semmle.label | ControlFlowNode for MyObj() [Attribute foo] | | test.py:81:17:81:22 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:82:10:82:12 | ControlFlowNode for obj [Attribute foo] | semmle.label | ControlFlowNode for obj [Attribute foo] | @@ -101,8 +123,12 @@ nodes | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | examples.py:59:29:59:34 | ControlFlowNode for SOURCE | examples.py:59:6:59:35 | ControlFlowNode for fields_with_local_flow() | Flow found | | test.py:50:10:50:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:50:10:50:18 | ControlFlowNode for Attribute | Flow found | | test.py:50:10:50:18 | ControlFlowNode for Attribute | test.py:49:19:49:24 | ControlFlowNode for SOURCE | test.py:50:10:50:18 | ControlFlowNode for Attribute | Flow found | +| test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:57:10:57:18 | ControlFlowNode for Attribute | Flow found | +| test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:57:10:57:18 | ControlFlowNode for Attribute | Flow found | | test.py:67:10:67:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:67:10:67:18 | ControlFlowNode for Attribute | Flow found | | test.py:67:10:67:18 | ControlFlowNode for Attribute | test.py:61:9:61:14 | ControlFlowNode for SOURCE | test.py:67:10:67:18 | ControlFlowNode for Attribute | Flow found | +| test.py:77:10:77:18 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:77:10:77:18 | ControlFlowNode for Attribute | Flow found | +| test.py:77:10:77:18 | ControlFlowNode for Attribute | test.py:71:9:71:14 | ControlFlowNode for SOURCE | test.py:77:10:77:18 | ControlFlowNode for Attribute | Flow found | | test.py:82:10:82:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:82:10:82:16 | ControlFlowNode for Attribute | Flow found | | test.py:82:10:82:16 | ControlFlowNode for Attribute | test.py:81:17:81:22 | ControlFlowNode for SOURCE | test.py:82:10:82:16 | ControlFlowNode for Attribute | Flow found | | test.py:87:10:87:16 | ControlFlowNode for Attribute | test.py:3:10:3:17 | ControlFlowNode for Str | test.py:87:10:87:16 | ControlFlowNode for Attribute | Flow found | diff --git a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected index e4142b6da6b..785af9627ba 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/globalStep.expected @@ -884,8 +884,11 @@ | test.py:29:22:29:24 | SSA variable foo | test.py:30:20:30:22 | ControlFlowNode for foo | | test.py:29:22:29:24 | SSA variable foo | test.py:30:20:30:22 | ControlFlowNode for foo | | test.py:29:22:29:24 | SSA variable foo | test.py:30:20:30:22 | ControlFlowNode for foo | -| test.py:30:9:30:12 | [post store] ControlFlowNode for self | test.py:56:18:56:23 | [post arg] ControlFlowNode for SOURCE | -| test.py:30:9:30:12 | [post store] ControlFlowNode for self | test.py:56:18:56:23 | [post arg] ControlFlowNode for SOURCE | +| test.py:30:9:30:12 | [post store] ControlFlowNode for self | test.py:56:5:56:9 | [post read] ControlFlowNode for myobj | +| test.py:30:9:30:12 | [post store] ControlFlowNode for self | test.py:56:5:56:9 | [post read] ControlFlowNode for myobj | +| test.py:30:9:30:12 | [post store] ControlFlowNode for self [Attribute foo] | test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] | +| test.py:30:20:30:22 | ControlFlowNode for foo | test.py:30:9:30:12 | [post store] ControlFlowNode for self [Attribute foo] | +| test.py:30:20:30:22 | ControlFlowNode for foo | test.py:30:9:30:12 | [post store] ControlFlowNode for self [Attribute foo] | | test.py:33:1:33:24 | ControlFlowNode for ClassExpr | test.py:33:7:33:15 | GSSA Variable NestedObj | | test.py:33:1:33:24 | ControlFlowNode for ClassExpr | test.py:33:7:33:15 | GSSA Variable NestedObj | | test.py:33:7:33:15 | GSSA Variable NestedObj | test.py:0:0:0:0 | ModuleVariableNode for Global Variable NestedObj in Module test | @@ -921,6 +924,13 @@ | test.py:37:5:37:21 | ControlFlowNode for FunctionExpr | test.py:37:9:37:14 | SSA variable getObj | | test.py:37:16:37:19 | SSA variable self | test.py:38:16:38:19 | ControlFlowNode for self | | test.py:37:16:37:19 | SSA variable self | test.py:38:16:38:19 | ControlFlowNode for self | +| test.py:37:16:37:19 | SSA variable self | test.py:38:16:38:19 | ControlFlowNode for self | +| test.py:37:16:37:19 | SSA variable self | test.py:38:16:38:19 | ControlFlowNode for self | +| test.py:37:16:37:19 | SSA variable self [Attribute obj] | test.py:38:16:38:19 | ControlFlowNode for self [Attribute obj] | +| test.py:38:16:38:19 | ControlFlowNode for self [Attribute obj] | test.py:38:16:38:23 | ControlFlowNode for Attribute | +| test.py:38:16:38:19 | ControlFlowNode for self [Attribute obj] | test.py:38:16:38:23 | ControlFlowNode for Attribute | +| test.py:38:16:38:19 | [post read] ControlFlowNode for self | test.py:75:5:75:5 | [post read] ControlFlowNode for a | +| test.py:38:16:38:19 | [post read] ControlFlowNode for self | test.py:75:5:75:5 | [post read] ControlFlowNode for a | | test.py:38:16:38:23 | ControlFlowNode for Attribute | test.py:75:5:75:14 | ControlFlowNode for Attribute() | | test.py:38:16:38:23 | ControlFlowNode for Attribute | test.py:75:5:75:14 | ControlFlowNode for Attribute() | | test.py:41:1:41:19 | ControlFlowNode for FunctionExpr | test.py:41:5:41:10 | GSSA Variable setFoo | @@ -1049,15 +1059,17 @@ | test.py:54:19:54:22 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | | test.py:54:19:54:22 | ControlFlowNode for Str | test.py:26:24:26:26 | SSA variable foo | | test.py:54:19:54:22 | ControlFlowNode for Str | test.py:54:13:54:23 | ControlFlowNode for MyObj() [Attribute foo] | +| test.py:56:5:56:9 | ControlFlowNode for myobj | test.py:29:16:29:19 | SSA variable self | +| test.py:56:5:56:9 | ControlFlowNode for myobj | test.py:29:16:29:19 | SSA variable self | | test.py:56:5:56:9 | ControlFlowNode for myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | | test.py:56:5:56:9 | ControlFlowNode for myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | | test.py:56:5:56:9 | ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | | test.py:56:5:56:9 | [post read] ControlFlowNode for myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | | test.py:56:5:56:9 | [post read] ControlFlowNode for myobj | test.py:57:10:57:14 | ControlFlowNode for myobj | -| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:29:16:29:19 | SSA variable self | -| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:29:16:29:19 | SSA variable self | +| test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | | test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:29:22:29:24 | SSA variable foo | | test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:29:22:29:24 | SSA variable foo | +| test.py:56:18:56:23 | ControlFlowNode for SOURCE | test.py:56:5:56:9 | [post read] ControlFlowNode for myobj [Attribute foo] | | test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute | | test.py:57:10:57:14 | ControlFlowNode for myobj [Attribute foo] | test.py:57:10:57:18 | ControlFlowNode for Attribute | | test.py:57:10:57:18 | ControlFlowNode for Attribute | test.py:10:10:10:10 | SSA variable x | @@ -1166,12 +1178,22 @@ | test.py:73:9:73:19 | ControlFlowNode for NestedObj() [Attribute obj] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj] | | test.py:73:9:73:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:34:18:34:21 | SSA variable self | | test.py:73:9:73:19 | [pre objCreate] ControlFlowNode for NestedObj() | test.py:34:18:34:21 | SSA variable self | +| test.py:75:5:75:5 | ControlFlowNode for a | test.py:37:16:37:19 | SSA variable self | +| test.py:75:5:75:5 | ControlFlowNode for a | test.py:37:16:37:19 | SSA variable self | | test.py:75:5:75:5 | ControlFlowNode for a | test.py:77:10:77:10 | ControlFlowNode for a | | test.py:75:5:75:5 | ControlFlowNode for a | test.py:77:10:77:10 | ControlFlowNode for a | | test.py:75:5:75:5 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:75:5:75:5 | ControlFlowNode for a [Attribute obj] | test.py:37:16:37:19 | SSA variable self [Attribute obj] | +| test.py:75:5:75:5 | ControlFlowNode for a [Attribute obj] | test.py:75:5:75:14 | ControlFlowNode for Attribute() | +| test.py:75:5:75:5 | ControlFlowNode for a [Attribute obj] | test.py:75:5:75:14 | ControlFlowNode for Attribute() | | test.py:75:5:75:5 | ControlFlowNode for a [Attribute obj] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj] | | test.py:75:5:75:5 | [post read] ControlFlowNode for a | test.py:77:10:77:10 | ControlFlowNode for a | | test.py:75:5:75:5 | [post read] ControlFlowNode for a | test.py:77:10:77:10 | ControlFlowNode for a | +| test.py:75:5:75:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:75:5:75:5 | [post read] ControlFlowNode for a [Attribute obj] | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj] | +| test.py:75:5:75:14 | [post store] ControlFlowNode for Attribute() | test.py:75:5:75:5 | [post read] ControlFlowNode for a [Attribute obj] | +| test.py:75:5:75:14 | [post store] ControlFlowNode for Attribute() [Attribute foo] | test.py:75:5:75:5 | [post read] ControlFlowNode for a [Attribute obj, Attribute foo] | +| test.py:75:22:75:22 | ControlFlowNode for x | test.py:75:5:75:14 | [post store] ControlFlowNode for Attribute() [Attribute foo] | | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj, Attribute foo] | test.py:77:10:77:14 | ControlFlowNode for Attribute [Attribute foo] | | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj] | test.py:77:10:77:14 | ControlFlowNode for Attribute | | test.py:77:10:77:10 | ControlFlowNode for a [Attribute obj] | test.py:77:10:77:14 | ControlFlowNode for Attribute | diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py index 87b7b49dd0f..39d58f2a05b 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/test.py +++ b/python/ql/test/experimental/dataflow/fieldflow/test.py @@ -54,7 +54,7 @@ def test_example1_method(): myobj = MyObj("OK") myobj.setFoo(SOURCE) - SINK(myobj.foo) # Flow not found + SINK(myobj.foo) def test_example2(): @@ -74,7 +74,7 @@ def test_example2_method(): a.getObj().foo = x - SINK(a.obj.foo) # Flow missing + SINK(a.obj.foo) def test_example3(): From 385e213fcfc36e30734894a1714df18905906d2a Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Sun, 4 Oct 2020 09:33:30 +0200 Subject: [PATCH 025/166] Python: Fix comments --- .../dataflow/internal/DataFlowPublic.qll | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 54f77163512..7c6f396cd05 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -29,11 +29,22 @@ newtype TNode = TSyntheticPostUpdateNode(NeedsSyntheticPostUpdateNode pre) or /** A node representing a global (module-level) variable in a specific module. */ TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m and v.escapes() } or - /** A node representing the overflow positional arguments to a call. */ + /** + * A node representing the overflow positional arguments to a call. + * That is, `call` contains more positional arguments than there are + * positional parameters in `callable`. The extra ones are passed as + * a tuple to a starred parameter; this synthetic node represents that tuple. + */ TPosOverflowNode(CallNode call, CallableValue callable) { exists(getPositionalOverflowArg(call, callable, _)) } or - /** A node representing the overflow keyword arguments to a call. */ + /** + * A node representing the overflow keyword arguments to a call. + * That is, `call` contains keyword arguments for keys that do not have + * keyword parameters in `callable`. These extra ones are passed as + * a dictionary to a doubly starred parameter; this synthetic node + * represents that dictionary. + */ TKwOverflowNode(CallNode call, CallableValue callable) { exists(getKeywordOverflowArg(call, callable, _)) or @@ -43,7 +54,7 @@ newtype TNode = /** * A node representing an unpacked element of a dictionary argument. * That is, `call` contains argument `**{"foo": bar}` which is passed - * to parameter `foo` of `callable. + * to parameter `foo` of `callable`. */ TKwUnpacked(CallNode call, CallableValue callable, string name) { call_unpacks(call, _, callable, name, _) From 34638890100b05f81f0b2c560ab0a97e93c531be Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Sun, 4 Oct 2020 09:40:06 +0200 Subject: [PATCH 026/166] Python: Add comments --- .../dataflow/internal/DataFlowPublic.qll | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll index 7c6f396cd05..ac63f9b9e53 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll @@ -266,6 +266,10 @@ class ModuleVariableNode extends Node, TModuleVariableNode { override Location getLocation() { result = mod.getLocation() } } +/** + * The node holding the extra positional arguments to a call. This node is passed as a tuple + * to the starred parameter of the callable. + */ class PosOverflowNode extends Node, TPosOverflowNode { CallNode call; @@ -276,6 +280,10 @@ class PosOverflowNode extends Node, TPosOverflowNode { override Location getLocation() { result = call.getLocation() } } +/** + * The node holding the extra keyword arguments to a call. This node is passed as a dictionary + * to the doubly starred parameter of the callable. + */ class KwOverflowNode extends Node, TKwOverflowNode { CallNode call; @@ -286,6 +294,10 @@ class KwOverflowNode extends Node, TKwOverflowNode { override Location getLocation() { result = call.getLocation() } } +/** + * The node representing the synthetic argument of a call that is unpacked from a dictionary + * argument. + */ class KwUnpacked extends Node, TKwUnpacked { CallNode call; string name; From 8e27904f659c30aac41a0c91e41ba70a1b596c6b Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Sun, 4 Oct 2020 15:34:25 +0200 Subject: [PATCH 027/166] Python: Add explanatory comment. --- .../dataflow/internal/DataFlowPrivate.qll | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 8a181359b3a..3847d7d1e7e 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -245,7 +245,39 @@ private Node update(Node node) { // Global flow //-------- // -/** Computes routing of arguments to parameters */ +/** + * Computes routing of arguments to parameters + * + * When a call contains more positional arguments than there are positional parameters, + * the extra positional arguments are passed as a tuple to a starred parameter. This is + * achieved by synthesising a node `TPosOverflowNode(call, callable)` + * that represents the tuple of extra positional arguments. There is a store step from each + * extra positional argument to this node. + * + * CURRENTLY NOT SUPPORTED: + * When a call contains a tuple argument, it is expanded into positional arguments. + * + * CURRENTLY NOT SUPPORTED: + * Whe a call contains a tuple argument and the callee contains a starred argument, any extra + * positional arguments are passed to the starred argument. + * + * When a call contains keyword arguments that do not correspond to keyword parameters, these + * extra keyword arguments are passed as a dictionary to a doubly starred parameter. This is + * achieved by synthesising a node `TKwOverflowNode(call, callable)` + * that represents the dictionary of extra keyword arguments. There is a store step from each + * extra keyword argument to this node. + * + * When a call contains a dictionary argument with entries corresponding to a keyword parameter, + * the value at such a key is unpacked and passed to the positional parameter. This is achieved + * by synthesising an argument node `TKwUnpacked(call, callable, name)` representing the unpacked + * value. This is used as the argument passed to the matching keyword parameter. There is a read + * step from the dictionary argument to the synthesized argument node. + * + * When a call contains a dictionary argument and the callee contains a doubly starred parameter, + * entries which are not unpacked are passed to the doubly starred parameter. This is achieved by + * adding a dataflow step from the dictionary argument to `TKwOverflowNode(call, callable)` and a + * step to clear content of that node at any unpacked keys. + */ module ArgumentPassing { /** * Gets the `n`th parameter of `callable`. @@ -288,7 +320,7 @@ module ArgumentPassing { * Gets the argument to `call` that is passed to the `n`th parameter of `callable`. * If it is a positional argument, it must appear at position `argNr`. * `argNr` will differ from `n` for method- or class calls, where the first parameter - * is `self` and the first positional arguemnt is passed to the second positional parameter. + * is `self` and the first positional argument is passed to the second positional parameter. */ Node getArg(CallNode call, int argNr, CallableValue callable, int n) { connects(call, callable) and From f449da2fdbb19ae8257fb414c0abe599d80b7291 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 5 Oct 2020 11:39:18 +0200 Subject: [PATCH 028/166] Python: Write explanatory examples. --- .../dataflow/internal/DataFlowPrivate.qll | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 3847d7d1e7e..b2b4b7bcb2c 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -277,6 +277,38 @@ private Node update(Node node) { * entries which are not unpacked are passed to the doubly starred parameter. This is achieved by * adding a dataflow step from the dictionary argument to `TKwOverflowNode(call, callable)` and a * step to clear content of that node at any unpacked keys. + * + * ## Examples: + * Assume that we have the callable + * ```python + * def f(x, y, *t, *d): + * pass + * ``` + * Then the call + * ```python + * f(0, 1, 2, a=3) + * ``` + * will be modelled as + * ```python + * f(0, 1, [*t], [**d]) + * ``` + * where `[` and `]` denotes synthesisezed nodes, so `[*t]` is the synthesized tuple argument + * `TPosOverflowNode` and `[**d]` is the synthesized dictionary argument `TKwOverflowNode`. + * There will be a store step from `2` to `[*t]` at pos `0` and one from `3` to `[**d]` at key + * `a`. + * + * For the call + * ```python + * f(0, **{"y": 1, "a": 3}) + * ``` + * no tuple arguemnt is synthesized. It is modelled as + * ```python + * f(0, [y=1], [**d]) + * ``` + * where `[y=1]` is the synthesised unpacked argument `TKwUnpacked` (with `name` = `y`). There is + * a read step from `**{"y": 1, "a": 3}` to `[y=1]` at key `y` to get the value passed to the parameter + * `y`. There is a dataflow step from `**{"y": 1, "a": 3}` to `[**d]` to transfer the content and + * a clearing of content at key `y` for node `[**d]`, since that value has been unpacked. */ module ArgumentPassing { /** From 478cfd7310718d549fff1fc87c70cf9f865bcda0 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Mon, 5 Oct 2020 12:43:30 +0200 Subject: [PATCH 029/166] Python: Small clean-up --- .../dataflow/internal/DataFlowPrivate.qll | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index b2b4b7bcb2c..9072def56af 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -48,10 +48,10 @@ class ArgumentPreUpdateNode extends NeedsSyntheticPostUpdateNode, ArgumentNode { // Certain arguments, such as implicit self arguments are already post-update nodes // and should not have an extra node synthesised. ArgumentPreUpdateNode() { - this = any(CallNodeCall c).getArg(_) + this = any(FunctionCall c).getArg(_) or - // this = any(BoundMethodCall c).getArg(_) - exists(BoundMethodCall c, int n | n > 0 | this = c.getArg(n)) + // Avoid argument 0 of method calls as those have read post-update nodes. + exists(MethodCall c, int n | n > 0 | this = c.getArg(n)) or this = any(SpecialCall c).getArg(_) or @@ -311,6 +311,17 @@ private Node update(Node node) { * a clearing of content at key `y` for node `[**d]`, since that value has been unpacked. */ module ArgumentPassing { + /** + * Holds if `call` is a call to `callable`. + * Used to limit the size of predicates. + */ + predicate connects(CallNode call, CallableValue callable) { + exists(DataFlowCall c | + call = c.getNode() and + callable = c.getCallable().getCallableValue() + ) + } + /** * Gets the `n`th parameter of `callable`. * If the callable has a starred parameter, say `*tuple`, that is matched with `n=-1`. @@ -320,12 +331,8 @@ module ArgumentPassing { */ NameNode getParameter(CallableValue callable, int n) { // positional parameter - // bound method values have their positional parameters shifted. - not callable instanceof BoundMethodValue and result = callable.getParameter(n) or - result = callable.(BoundMethodValue).getParameter(n - 1) - or // starred parameter, `*tuple` exists(Function f | f = callable.getScope() and @@ -341,13 +348,6 @@ module ArgumentPassing { ) } - predicate connects(CallNode call, CallableValue callable) { - exists(DataFlowCall c | - call = c.getNode() and - callable = c.getCallable().getCallableValue() - ) - } - /** * Gets the argument to `call` that is passed to the `n`th parameter of `callable`. * If it is a positional argument, it must appear at position `argNr`. @@ -435,7 +435,7 @@ import ArgumentPassing /** * IPA type for DataFlowCallable. * - * A callable is either a callable value or a module (for enclosing `ModuleVariableNode`s). + * A callable is either a function value, a class value, or a module (for enclosing `ModuleVariableNode`s). * A module has no calls. */ newtype TDataFlowCallable = @@ -518,9 +518,9 @@ class DataFlowModuleScope extends DataFlowCallable, TModule { * A call corresponding to a special method call is handled by the corresponding `SpecialMethodCallNode`. */ newtype TDataFlowCall = - TCallNode(CallNode call) { call = any(FunctionValue f).getAFunctionCall() } or + TFunctionCall(CallNode call) { call = any(FunctionValue f).getAFunctionCall() } or /** Bound methods need to make room for the explicit self parameter */ - TBoundMethodCall(CallNode call) { call = any(FunctionValue f).getAMethodCall() } or + TMethodCall(CallNode call) { call = any(FunctionValue f).getAMethodCall() } or TClassCall(CallNode call) { call = any(ClassValue c).getACall() } or TSpecialCall(SpecialMethodCallNode special) @@ -554,12 +554,12 @@ abstract class DataFlowCall extends TDataFlowCall { * Bound method calls and class calls insert an argument for the explicit * `self` parameter, and special method calls have special argument passing. */ -class CallNodeCall extends DataFlowCall, TCallNode { +class FunctionCall extends DataFlowCall, TFunctionCall { CallNode call; DataFlowCallable callable; - CallNodeCall() { - this = TCallNode(call) and + FunctionCall() { + this = TFunctionCall(call) and call = callable.getACall() } @@ -578,12 +578,12 @@ class CallNodeCall extends DataFlowCall, TCallNode { * Represents a call to a bound method call. * The node representing the instance is inserted as argument to the `self` parameter. */ -class BoundMethodCall extends DataFlowCall, TBoundMethodCall { +class MethodCall extends DataFlowCall, TMethodCall { CallNode call; FunctionValue bm; - BoundMethodCall() { - this = TBoundMethodCall(call) and + MethodCall() { + this = TMethodCall(call) and call = bm.getACall() } From 8272d591b6a368a6a743e9ab4c0a08787bf49cea Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Mon, 5 Oct 2020 14:12:03 -0400 Subject: [PATCH 030/166] Apply suggestions from code review https://github.com/github/codeql/pull/4312 Co-authored-by: Felicity Chapman Co-authored-by: Arthur Baars --- .../CWE/CWE-338/JHipsterGeneratedPRNG.qhelp | 17 ++++++++++------- .../CWE/CWE-338/JHipsterGeneratedPRNG.ql | 4 ++-- .../semmle/code/java/frameworks/apache/Lang.qll | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp index 8c0bea090e6..075e18ad50f 100644 --- a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp +++ b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.qhelp @@ -3,10 +3,10 @@ "qhelp.dtd"> -

    This query detects instances of RandomUtil.java generated by a JHipster version vulnerable to CVE-2019-16303.

    +

    This query detects instances of RandomUtil.java that were generated by a JHipster version that is vulnerable to CVE-2019-16303.

    -

    Using one password reset token from your app combined with the proof of concept (POC) linked below, an attacker can determine all future password reset tokens to be generated by this server. -This allows an attacker to pick and choose what account they would like to takeover by sending account password reset requests for targeted accounts.

    +

    If an app uses RandomUtil.java generated by a vulnerable version of JHipster, attackers can request a password reset token and use this to predict the value of future reset tokens generated by this server. +Using this information, they can create a reset link that allows them to take over any account.

    This vulnerability has a @@ -26,24 +26,27 @@ This allows an attacker to pick and choose what account they would like to takeo -

    An automated refactoring rewrite module can be found here.

    +

    You should refactor the RandomUtil class and replace every call to RandomStringUtils.randomAlphaNumeric. You could regenerate the class using the latest version of JHipster, or use an automated refactoring. For example, using the Patching JHipster CWE-338 for the Rewrite project. +

  • + Cloudflare Blog: - Cloudflare Blog: Why secure systems require random numbers + Why secure systems require random numbers
  • + Hacker News: How I Hacked Hacker News (with arc security advisory)
  • - Research (Hacking Apache Commons RandomStringUtils): - + Posts by Pucara Information Security Team: + The Java Soothsayer: A practical application for insecure randomness. (Includes free 0day)
  • diff --git a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql index b184b5d047f..8eda68ebe70 100644 --- a/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql +++ b/java/ql/src/Security/CWE/CWE-338/JHipsterGeneratedPRNG.ql @@ -1,6 +1,6 @@ /** - * @name Detect JHipster Generator Vulnnerability CVE-2019-16303 - * @description Detector for the CVE-2019-16303 vulnerability that existed in the JHipster code generator. + * @name Detect JHipster Generator Vulnerability CVE-2019-16303 + * @description Using a vulnerable version of JHipster to generate random numbers makes it easier for attackers to take over accounts. * @kind problem * @problem.severity error * @precision very-high diff --git a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll index 394ba2014ab..b66b9f0b306 100644 --- a/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll +++ b/java/ql/src/semmle/code/java/frameworks/apache/Lang.qll @@ -1,4 +1,4 @@ -/* Definitions related to the Apache Commons Lang library. */ +/** Definitions related to the Apache Commons Lang library. */ import semmle.code.java.Type /*--- Types ---*/ From 6ec7ab2fd976f8e78409996d30fccbfc48aa52be Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 6 Oct 2020 18:49:50 +0200 Subject: [PATCH 031/166] Python: Add test of Python 2 specific SystemCommandExecution --- .../stdlib-py2/ConceptsTest.expected | 18 ++++++++++ .../frameworks/stdlib-py2/ConceptsTest.ql | 2 ++ .../stdlib-py2/SystemCommandExecution.py | 36 +++++++++++++++++++ .../frameworks/stdlib-py2/options | 1 + 4 files changed, 57 insertions(+) create mode 100644 python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected create mode 100644 python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.ql create mode 100644 python/ql/test/experimental/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py create mode 100644 python/ql/test/experimental/library-tests/frameworks/stdlib-py2/options diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected new file mode 100644 index 00000000000..31b95abdaa6 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected @@ -0,0 +1,18 @@ +| SystemCommandExecution.py:4:26:4:51 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:5:26:5:51 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:6:26:6:51 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:9:30:9:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:10:30:10:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:11:30:11:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:19:31:19:56 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:20:35:20:60 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:26:30:26:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:27:30:27:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:28:30:28:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:29:30:29:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:30:30:30:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:32:34:32:59 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:33:34:33:59 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:34:34:34:59 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:35:34:35:59 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | +| SystemCommandExecution.py:36:34:36:59 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.ql b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.ql new file mode 100644 index 00000000000..b557a0bccb6 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.ql @@ -0,0 +1,2 @@ +import python +import experimental.meta.ConceptsTest diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py new file mode 100644 index 00000000000..68e0aa5eeb8 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py @@ -0,0 +1,36 @@ +######################################## +import os + +os.popen2("cmd1; cmd2") # $getCommand="cmd1; cmd2" +os.popen3("cmd1; cmd2") # $getCommand="cmd1; cmd2" +os.popen4("cmd1; cmd2") # $getCommand="cmd1; cmd2" + + +os.popen2(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" +os.popen3(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" +os.popen4(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" + +# os.popen does not support keyword arguments +os.popen(cmd="cmd1; cmd2") + +######################################## +import platform + +platform.popen("cmd1; cmd2") # $getCommand="cmd1; cmd2" +platform.popen(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" + +######################################## +# popen2 was deprecated in Python 2.6, but still available in Python 2.7 +import popen2 + +popen2.popen2("cmd1; cmd2") # $getCommand="cmd1; cmd2" +popen2.popen3("cmd1; cmd2") # $getCommand="cmd1; cmd2" +popen2.popen4("cmd1; cmd2") # $getCommand="cmd1; cmd2" +popen2.Popen3("cmd1; cmd2") # $getCommand="cmd1; cmd2" +popen2.Popen4("cmd1; cmd2") # $getCommand="cmd1; cmd2" + +popen2.popen2(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" +popen2.popen3(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" +popen2.popen4(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" +popen2.Popen3(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" +popen2.Popen4(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/options b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/options new file mode 100644 index 00000000000..8dd8d25c998 --- /dev/null +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=1 --lang=2 From 8c2f55fbd05e9c3d3c20c7cecdfeaaca3f3cb1a3 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 6 Oct 2020 18:59:01 +0200 Subject: [PATCH 032/166] Python: Model Python 2 only os.popen2, popen3, popen4 functions --- .../semmle/python/frameworks/Stdlib.qll | 18 +++++++++++++++--- .../stdlib-py2/ConceptsTest.expected | 6 ------ .../stdlib-py2/SystemCommandExecution.py | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 5fa36c67e09..d3d77e206b5 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -32,7 +32,7 @@ private module Stdlib { * For example, using `attr_name = "system"` will get all uses of `os.system`. */ private DataFlow::Node os_attr(DataFlow::TypeTracker t, string attr_name) { - attr_name in ["system", "popen", + attr_name in ["system", "popen", "popen2", "popen3", "popen4", // exec "execl", "execle", "execlp", "execlpe", "execv", "execve", "execvp", "execvpe", // spawn @@ -111,14 +111,26 @@ private module Stdlib { } /** - * A call to `os.popen` + * A call to any of the `os.popen*` functions * See https://docs.python.org/3/library/os.html#os.popen + * + * Note that in Python 2, there are also `popen2`, `popen3`, and `popen4` functions. + * Although deprecated since version 2.6, they still work in 2.7. + * See https://docs.python.org/2.7/library/os.html#os.popen2 */ private class OsPopenCall extends SystemCommandExecution::Range { - OsPopenCall() { this.asCfgNode().(CallNode).getFunction() = os_attr("popen").asCfgNode() } + string name; + + OsPopenCall() { + name in ["popen", "popen2", "popen3", "popen4"] and + this.asCfgNode().(CallNode).getFunction() = os_attr(name).asCfgNode() + } override DataFlow::Node getCommand() { result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0) + or + not name = "popen" and + result.asCfgNode() = this.asCfgNode().(CallNode).getArgByName("cmd") } } diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected index 31b95abdaa6..624e3c98a52 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected @@ -1,9 +1,3 @@ -| SystemCommandExecution.py:4:26:4:51 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:5:26:5:51 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:6:26:6:51 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:9:30:9:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:10:30:10:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:11:30:11:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | | SystemCommandExecution.py:19:31:19:56 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | | SystemCommandExecution.py:20:35:20:60 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | | SystemCommandExecution.py:26:30:26:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py index 68e0aa5eeb8..1e61a9c9399 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/SystemCommandExecution.py @@ -10,7 +10,7 @@ os.popen2(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" os.popen3(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" os.popen4(cmd="cmd1; cmd2") # $getCommand="cmd1; cmd2" -# os.popen does not support keyword arguments +# os.popen does not support keyword arguments, so this is a TypeError os.popen(cmd="cmd1; cmd2") ######################################## From 12e4e07cae7ae5c976488974629aa6f513e8b341 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 6 Oct 2020 19:14:52 +0200 Subject: [PATCH 033/166] Python: Model Python 2 only module popen2 --- .../semmle/python/frameworks/Stdlib.qll | 74 +++++++++++++++++++ .../stdlib-py2/ConceptsTest.expected | 10 --- 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index d3d77e206b5..c49b8a3f1a3 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -339,4 +339,78 @@ private module Stdlib { ) } } + + // --------------------------------------------------------------------------- + // popen2 + // --------------------------------------------------------------------------- + /** Gets a reference to the `popen2` module (only available in Python 2). */ + private DataFlow::Node popen2(DataFlow::TypeTracker t) { + t.start() and + result = DataFlow::importModule("popen2") + or + exists(DataFlow::TypeTracker t2 | result = popen2(t2).track(t2, t)) + } + + /** Gets a reference to the `popen2` module. */ + DataFlow::Node popen2() { result = popen2(DataFlow::TypeTracker::end()) } + + /** + * Gets a reference to the attribute `attr_name` of the `popen2` module. + * WARNING: Only holds for a few predefined attributes. + */ + private DataFlow::Node popen2_attr(DataFlow::TypeTracker t, string attr_name) { + attr_name in ["popen2", "popen3", "popen4", + // classes + "Popen3", "Popen4"] and + ( + t.start() and + result = DataFlow::importMember("popen2", attr_name) + or + t.startInAttr(attr_name) and + result = DataFlow::importModule("popen2") + ) + or + // Due to bad performance when using normal setup with `popen2_attr(t2, attr_name).track(t2, t)` + // we have inlined that code and forced a join + exists(DataFlow::TypeTracker t2 | + exists(DataFlow::StepSummary summary | + popen2_attr_first_join(t2, attr_name, result, summary) and + t = t2.append(summary) + ) + ) + } + + pragma[nomagic] + private predicate popen2_attr_first_join( + DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary + ) { + DataFlow::StepSummary::step(popen2_attr(t2, attr_name), res, summary) + } + + /** + * Gets a reference to the attribute `attr_name` of the `popen2` module. + * WARNING: Only holds for a few predefined attributes. + */ + private DataFlow::Node popen2_attr(string attr_name) { + result = popen2_attr(DataFlow::TypeTracker::end(), attr_name) + } + + /** + * A call to any of the `popen.popen*` functions, or instantiation of a `popen.Popen*` class. + * See https://docs.python.org/2.7/library/popen2.html + */ + private class Popen2PopenCall extends SystemCommandExecution::Range { + Popen2PopenCall() { + exists(string name | + name in ["popen2", "popen3", "popen4", "Popen3", "Popen4"] and + this.asCfgNode().(CallNode).getFunction() = popen2_attr(name).asCfgNode() + ) + } + + override DataFlow::Node getCommand() { + result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0) + or + result.asCfgNode() = this.asCfgNode().(CallNode).getArgByName("cmd") + } + } } diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected index 624e3c98a52..076719e273a 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected @@ -1,12 +1,2 @@ | SystemCommandExecution.py:19:31:19:56 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | | SystemCommandExecution.py:20:35:20:60 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:26:30:26:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:27:30:27:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:28:30:28:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:29:30:29:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:30:30:30:55 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:32:34:32:59 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:33:34:33:59 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:34:34:34:59 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:35:34:35:59 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:36:34:36:59 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | From 6c4fd7c1ff1101889a69568cdff289deb8a50493 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Tue, 6 Oct 2020 19:17:41 +0200 Subject: [PATCH 034/166] Python: Model Python 2 only platform.popen command execution --- .../semmle/python/frameworks/Stdlib.qll | 69 +++++++++++++++++++ .../stdlib-py2/ConceptsTest.expected | 2 - 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index c49b8a3f1a3..94c405c3716 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -413,4 +413,73 @@ private module Stdlib { result.asCfgNode() = this.asCfgNode().(CallNode).getArgByName("cmd") } } + + // --------------------------------------------------------------------------- + // platform + // --------------------------------------------------------------------------- + /** Gets a reference to the `platform` module. */ + private DataFlow::Node platform(DataFlow::TypeTracker t) { + t.start() and + result = DataFlow::importModule("platform") + or + exists(DataFlow::TypeTracker t2 | result = platform(t2).track(t2, t)) + } + + /** Gets a reference to the `platform` module. */ + DataFlow::Node platform() { result = platform(DataFlow::TypeTracker::end()) } + + /** + * Gets a reference to the attribute `attr_name` of the `platform` module. + * WARNING: Only holds for a few predefined attributes. + */ + private DataFlow::Node platform_attr(DataFlow::TypeTracker t, string attr_name) { + attr_name in ["popen"] and + ( + t.start() and + result = DataFlow::importMember("platform", attr_name) + or + t.startInAttr(attr_name) and + result = DataFlow::importModule("platform") + ) + or + // Due to bad performance when using normal setup with `platform_attr(t2, attr_name).track(t2, t)` + // we have inlined that code and forced a join + exists(DataFlow::TypeTracker t2 | + exists(DataFlow::StepSummary summary | + platform_attr_first_join(t2, attr_name, result, summary) and + t = t2.append(summary) + ) + ) + } + + pragma[nomagic] + private predicate platform_attr_first_join( + DataFlow::TypeTracker t2, string attr_name, DataFlow::Node res, DataFlow::StepSummary summary + ) { + DataFlow::StepSummary::step(platform_attr(t2, attr_name), res, summary) + } + + /** + * Gets a reference to the attribute `attr_name` of the `platform` module. + * WARNING: Only holds for a few predefined attributes. + */ + private DataFlow::Node platform_attr(string attr_name) { + result = platform_attr(DataFlow::TypeTracker::end(), attr_name) + } + + /** + * A call to the `platform.popen` function. + * See https://docs.python.org/2.7/library/platform.html#platform.popen + */ + private class PlatformPopenCall extends SystemCommandExecution::Range { + PlatformPopenCall() { + this.asCfgNode().(CallNode).getFunction() = platform_attr("popen").asCfgNode() + } + + override DataFlow::Node getCommand() { + result.asCfgNode() = this.asCfgNode().(CallNode).getArg(0) + or + result.asCfgNode() = this.asCfgNode().(CallNode).getArgByName("cmd") + } + } } diff --git a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected index 076719e273a..e69de29bb2d 100644 --- a/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected +++ b/python/ql/test/experimental/library-tests/frameworks/stdlib-py2/ConceptsTest.expected @@ -1,2 +0,0 @@ -| SystemCommandExecution.py:19:31:19:56 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | -| SystemCommandExecution.py:20:35:20:60 | Comment # $getCommand="cmd1; cmd2" | Missing result:getCommand="cmd1; cmd2" | From d8a9eacd02afe80b952b0be6daeef2494c780b38 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 7 Oct 2020 10:47:28 +0200 Subject: [PATCH 035/166] Python: Remove TODO comment for popen2 module --- .../Security-new-dataflow/CWE-078/command_injection.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py index 75125f59fca..524d49aab5b 100644 --- a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078/command_injection.py @@ -55,7 +55,3 @@ def not_into_sink_impl(): subprocess.call(command) subprocess.check_call(command) subprocess.run(command) - - -# TODO: popen2 module for Python 2 only https://devdocs.io/python~2.7/library/popen2 -# (deprecated since Python 2.6, but still functional in Python 2.7.17) From 737b2b896f5a36585481e8e9a20f46c7ba45834e Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 7 Oct 2020 10:49:22 +0200 Subject: [PATCH 036/166] Python: Fix QLDoc for popen2 module --- python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll index 94c405c3716..ec947d20bc8 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Stdlib.qll @@ -351,7 +351,7 @@ private module Stdlib { exists(DataFlow::TypeTracker t2 | result = popen2(t2).track(t2, t)) } - /** Gets a reference to the `popen2` module. */ + /** Gets a reference to the `popen2` module (only available in Python 2). */ DataFlow::Node popen2() { result = popen2(DataFlow::TypeTracker::end()) } /** From 36812af2c27098318f6b787909f698a9cefa34c3 Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 7 Oct 2020 10:54:03 +0200 Subject: [PATCH 037/166] Python: Add test for Python2 specific command injection --- .../CWE-078-py2/CommandInjection.expected | 48 +++++++++++++++++++ .../CWE-078-py2/CommandInjection.qlref | 1 + .../CWE-078-py2/command_injection.py | 29 +++++++++++ .../Security-new-dataflow/CWE-078-py2/options | 1 + 4 files changed, 79 insertions(+) create mode 100644 python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.expected create mode 100644 python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.qlref create mode 100644 python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/command_injection.py create mode 100644 python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/options diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.expected b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.expected new file mode 100644 index 00000000000..9421ab950cc --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.expected @@ -0,0 +1,48 @@ +edges +| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:19:15:19:27 | ControlFlowNode for BinaryExpr | +| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:20:15:20:27 | ControlFlowNode for BinaryExpr | +| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:21:15:21:27 | ControlFlowNode for BinaryExpr | +| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | +| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | +| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:25:19:25:31 | ControlFlowNode for BinaryExpr | +| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:26:19:26:31 | ControlFlowNode for BinaryExpr | +| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:27:19:27:31 | ControlFlowNode for BinaryExpr | +| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:28:19:28:31 | ControlFlowNode for BinaryExpr | +| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:29:19:29:31 | ControlFlowNode for BinaryExpr | +| command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:453:11:453:13 | SSA variable cmd | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:415:23:415:25 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:421:19:421:41 | ControlFlowNode for BinaryExpr | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:453:11:453:13 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:453:11:453:13 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:453:11:453:13 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:484:22:484:24 | ControlFlowNode for cmd | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:415:23:415:25 | SSA variable cmd | +nodes +| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| command_injection.py:19:15:19:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:20:15:20:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:21:15:21:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:25:19:25:31 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:26:19:26:31 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:27:19:27:31 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:28:19:28:31 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| command_injection.py:29:19:29:31 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:415:23:415:25 | SSA variable cmd | semmle.label | SSA variable cmd | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:421:19:421:41 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:453:11:453:13 | SSA variable cmd | semmle.label | SSA variable cmd | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:484:22:484:24 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | +#select +| command_injection.py:19:15:19:27 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:19:15:19:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:20:15:20:27 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:20:15:20:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:21:15:21:27 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:21:15:21:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:25:19:25:31 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:25:19:25:31 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:26:19:26:31 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:26:19:26:31 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:27:19:27:31 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:27:19:27:31 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:28:19:28:31 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:28:19:28:31 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | +| command_injection.py:29:19:29:31 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:29:19:29:31 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:421:19:421:41 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:421:19:421:41 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | +| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:484:22:484:24 | ControlFlowNode for cmd | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:484:22:484:24 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.qlref b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.qlref new file mode 100644 index 00000000000..2d70a75c372 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.qlref @@ -0,0 +1 @@ +experimental/Security-new-dataflow/CWE-078/CommandInjection.ql diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/command_injection.py b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/command_injection.py new file mode 100644 index 00000000000..a9d6cb51326 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/command_injection.py @@ -0,0 +1,29 @@ +import os +import platform +import popen2 + +from flask import Flask, request + +app = Flask(__name__) + + +@app.route("/python2-specific") +def python2_specific(): + """ + These tests are mostly included to check for extra paths that can be generated if + we can track flow into the implementation of stdlib function, and then to an other sink. + See comment in query for more details. + """ + + files = request.args.get("files", "") + os.popen2("ls " + files) + os.popen3("ls " + files) + os.popen4("ls " + files) + + platform.popen("ls " + files) + + popen2.popen2("ls " + files) + popen2.popen3("ls " + files) + popen2.popen4("ls " + files) + popen2.Popen3("ls " + files) + popen2.Popen4("ls " + files) diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/options b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/options new file mode 100644 index 00000000000..8dd8d25c998 --- /dev/null +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/options @@ -0,0 +1 @@ +semmle-extractor-options: --max-import-depth=1 --lang=2 From 7721db206eab9523d189a9b5b2fd9c9c08ee6d1f Mon Sep 17 00:00:00 2001 From: Rasmus Wriedt Larsen Date: Wed, 7 Oct 2020 10:55:33 +0200 Subject: [PATCH 038/166] Python: Don't double report paths for platform.popen and popen2.* I was a bit surprised that we hadn't double reported for popen2, but it turns out that the implementation (at least on unix) looks like: ``` def popen2(cmd, bufsize=-1, mode='t'): ... = Popen3(cmd, False, bufsize) ... ``` but since the modeling I did only considers calls to `Popen3` only if it has been imported from the `popen2` module, we don't consider that call as a sink. --- .../CWE-078/CommandInjection.ql | 9 +++++---- .../CWE-078-py2/CommandInjection.expected | 17 ----------------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql index b1672ad995a..c902b55b3e4 100755 --- a/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql +++ b/python/ql/src/experimental/Security-new-dataflow/CWE-078/CommandInjection.ql @@ -46,15 +46,16 @@ class CommandInjectionConfiguration extends TaintTracking::Configuration { // os.system(cmd) // ``` // - // Best solution I could come up with is to exclude all sinks inside the `os` and - // `subprocess` modules. This does have a downside: If we have overlooked a function - // in any of these, that internally runs a command, we no longer give an alert :| + // Best solution I could come up with is to exclude all sinks inside the modules of + // known sinks. This does have a downside: If we have overlooked a function in any + // of these, that internally runs a command, we no longer give an alert :| -- and we + // need to keep them updated (which is hard to remember) // // This does not only affect `os.popen`, but also the helper functions in // `subprocess`. See: // https://github.com/python/cpython/blob/fa7ce080175f65d678a7d5756c94f82887fc9803/Lib/os.py#L974 // https://github.com/python/cpython/blob/fa7ce080175f65d678a7d5756c94f82887fc9803/Lib/subprocess.py#L341 - not sink.getScope().getEnclosingModule().getName() in ["os", "subprocess"] + not sink.getScope().getEnclosingModule().getName() in ["os", "subprocess", "platform", "popen2"] } } diff --git a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.expected b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.expected index 9421ab950cc..1ab00f7ecad 100644 --- a/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.expected +++ b/python/ql/test/experimental/query-tests/Security-new-dataflow/CWE-078-py2/CommandInjection.expected @@ -3,36 +3,22 @@ edges | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:20:15:20:27 | ControlFlowNode for BinaryExpr | | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:21:15:21:27 | ControlFlowNode for BinaryExpr | | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | -| command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:25:19:25:31 | ControlFlowNode for BinaryExpr | | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:26:19:26:31 | ControlFlowNode for BinaryExpr | | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:27:19:27:31 | ControlFlowNode for BinaryExpr | | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:28:19:28:31 | ControlFlowNode for BinaryExpr | | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:29:19:29:31 | ControlFlowNode for BinaryExpr | -| command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:453:11:453:13 | SSA variable cmd | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:415:23:415:25 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:421:19:421:41 | ControlFlowNode for BinaryExpr | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:453:11:453:13 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:453:11:453:13 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:453:11:453:13 | SSA variable cmd | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:484:22:484:24 | ControlFlowNode for cmd | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:415:23:415:25 | SSA variable cmd | nodes | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | command_injection.py:19:15:19:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | command_injection.py:20:15:20:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | command_injection.py:21:15:21:27 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | -| command_injection.py:23:20:23:32 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | command_injection.py:25:19:25:31 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | command_injection.py:26:19:26:31 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | command_injection.py:27:19:27:31 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | command_injection.py:28:19:28:31 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | command_injection.py:29:19:29:31 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:415:23:415:25 | SSA variable cmd | semmle.label | SSA variable cmd | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:421:19:421:41 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:453:11:453:13 | SSA variable cmd | semmle.label | SSA variable cmd | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:484:22:484:24 | ControlFlowNode for cmd | semmle.label | ControlFlowNode for cmd | #select | command_injection.py:19:15:19:27 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:19:15:19:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:20:15:20:27 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:20:15:20:27 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | @@ -43,6 +29,3 @@ nodes | command_injection.py:27:19:27:31 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:27:19:27:31 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:28:19:28:31 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:28:19:28:31 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | | command_injection.py:29:19:29:31 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | command_injection.py:29:19:29:31 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:421:19:421:41 | ControlFlowNode for BinaryExpr | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:421:19:421:41 | ControlFlowNode for BinaryExpr | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:482:22:482:24 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | -| file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:484:22:484:24 | ControlFlowNode for cmd | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | file:///home/rasmus/.pyenv/versions/2.7.17/lib/python2.7/platform.py:484:22:484:24 | ControlFlowNode for cmd | This command depends on $@. | command_injection.py:18:13:18:24 | ControlFlowNode for Attribute | a user-provided value | From 7e6f0b0bc32a89ec88609534bf724105ece66362 Mon Sep 17 00:00:00 2001 From: yoff Date: Wed, 7 Oct 2020 15:11:15 +0200 Subject: [PATCH 039/166] Apply suggestions from code review Co-authored-by: Rasmus Wriedt Larsen --- .../experimental/dataflow/internal/DataFlowPrivate.qll | 8 ++++---- .../experimental/dataflow/coverage/argumentPassing.py | 10 +++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 9072def56af..adad21a93a8 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -255,10 +255,10 @@ private Node update(Node node) { * extra positional argument to this node. * * CURRENTLY NOT SUPPORTED: - * When a call contains a tuple argument, it is expanded into positional arguments. + * When a call contains an iterable unpacking argument, such as `func(*args)`, it is expanded into positional arguments. * * CURRENTLY NOT SUPPORTED: - * Whe a call contains a tuple argument and the callee contains a starred argument, any extra + * When a call contains an iterable unpacking argument, such as `func(*args)`, and the callee contains a starred argument, any extra * positional arguments are passed to the starred argument. * * When a call contains keyword arguments that do not correspond to keyword parameters, these @@ -273,7 +273,7 @@ private Node update(Node node) { * value. This is used as the argument passed to the matching keyword parameter. There is a read * step from the dictionary argument to the synthesized argument node. * - * When a call contains a dictionary argument and the callee contains a doubly starred parameter, + * When a call contains a dictionary unpacking argument, such as `func(**kwargs)`, and the callee contains a doubly starred parameter, * entries which are not unpacked are passed to the doubly starred parameter. This is achieved by * adding a dataflow step from the dictionary argument to `TKwOverflowNode(call, callable)` and a * step to clear content of that node at any unpacked keys. @@ -281,7 +281,7 @@ private Node update(Node node) { * ## Examples: * Assume that we have the callable * ```python - * def f(x, y, *t, *d): + * def f(x, y, *t, **d): * pass * ``` * Then the call diff --git a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py index 6f21f40111a..d48eb6a2e5a 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py +++ b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py @@ -54,9 +54,9 @@ def argument_passing( b, /, c, - d="", + d=arg4, *, - e="", + e=arg5, f, **g, ): @@ -70,8 +70,12 @@ def argument_passing( @expects(7) -def test_argument_passing(): +def test_argument_passing1(): argument_passing(arg1, *(arg2, arg3, arg4), e=arg5, **{"f": arg6, "g": arg7}) + +@expects(7) +def test_argument_passing2(): + argument_passing(arg1, arg2, arg3, f=arg6) def with_pos_only(a, /, b): From 35b0b6b4720fe4aa5872709a3b8db709f6ab41ad Mon Sep 17 00:00:00 2001 From: yoff Date: Wed, 7 Oct 2020 15:48:44 +0200 Subject: [PATCH 040/166] Update python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll Co-authored-by: Rasmus Wriedt Larsen --- .../ql/src/experimental/dataflow/internal/DataFlowPrivate.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index f422c001ec3..916aa66823d 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -270,8 +270,8 @@ private Node update(Node node) { * that represents the dictionary of extra keyword arguments. There is a store step from each * extra keyword argument to this node. * - * When a call contains a dictionary argument with entries corresponding to a keyword parameter, - * the value at such a key is unpacked and passed to the positional parameter. This is achieved + * When a call contains a dictionary unpacking argument, such as `func(**kwargs)`, with entries corresponding to a keyword parameter, + * the value at such a key is unpacked and passed to the parameter. This is achieved * by synthesising an argument node `TKwUnpacked(call, callable, name)` representing the unpacked * value. This is used as the argument passed to the matching keyword parameter. There is a read * step from the dictionary argument to the synthesized argument node. From 8196cfd21af2002fe051bf2f78181490ce44e26d Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Wed, 7 Oct 2020 15:56:35 +0200 Subject: [PATCH 041/166] Python: Attempt at clearer naming of parameters --- .../dataflow/internal/DataFlowPrivate.qll | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 916aa66823d..89e570f271e 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -273,7 +273,7 @@ private Node update(Node node) { * When a call contains a dictionary unpacking argument, such as `func(**kwargs)`, with entries corresponding to a keyword parameter, * the value at such a key is unpacked and passed to the parameter. This is achieved * by synthesising an argument node `TKwUnpacked(call, callable, name)` representing the unpacked - * value. This is used as the argument passed to the matching keyword parameter. There is a read + * value. This node is used as the argument passed to the matching keyword parameter. There is a read * step from the dictionary argument to the synthesized argument node. * * When a call contains a dictionary unpacking argument, such as `func(**kwargs)`, and the callee contains a doubly starred parameter, @@ -352,14 +352,14 @@ module ArgumentPassing { } /** - * Gets the argument to `call` that is passed to the `n`th parameter of `callable`. - * If it is a positional argument, it must appear at position `argNr`. - * `argNr` will differ from `n` for method- or class calls, where the first parameter + * Gets the argument to `call` that is passed to the parameter at position `paramNr` in `callable`. + * If it is a positional argument, it must appear at position `argNr` in `call`. + * `argNr` will differ from `paramNr` for method- or constructor calls, where the first parameter * is `self` and the first positional argument is passed to the second positional parameter. */ - Node getArg(CallNode call, int argNr, CallableValue callable, int n) { + Node getArg(CallNode call, int argNr, CallableValue callable, int paramNr) { connects(call, callable) and - n - argNr in [0, 1] and // constrain for now to limit the size of the predicate; we only use it to insert one argument (self). + paramNr - argNr in [0, 1] and // constrain for now to limit the size of the predicate; we only use it to insert one argument (self). ( // positional argument result = TCfgNode(call.getArg(argNr)) @@ -367,23 +367,23 @@ module ArgumentPassing { // keyword argument exists(Function f, string argName | f = callable.getScope() and - f.getArgName(n) = argName and + f.getArgName(paramNr) = argName and result = TCfgNode(call.getArgByName(argName)) ) or // a synthezised argument passed to the starred parameter (at position -1) callable.getScope().hasVarArg() and - n = -1 and + paramNr = -1 and result = TPosOverflowNode(call, callable) or // a synthezised argument passed to the doubly starred parameter (at position -2) callable.getScope().hasKwArg() and - n = -2 and + paramNr = -2 and result = TKwOverflowNode(call, callable) or // argument unpacked from dict exists(string name | - call_unpacks(call, argNr, callable, name, n) and + call_unpacks(call, argNr, callable, name, paramNr) and result = TKwUnpacked(call, callable, name) ) ) From cc0661bce17c28a3ce3ce43f9eda20ae77bcb3db Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 8 Oct 2020 10:11:00 +0200 Subject: [PATCH 042/166] Python: More/better comments --- .../dataflow/internal/DataFlowPrivate.qll | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 89e570f271e..c22131e7d89 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -315,7 +315,10 @@ private Node update(Node node) { */ module ArgumentPassing { /** - * Holds if `call` is a call to `callable`. + * Holds if `call` represents a `DataFlowCall` to a `DataFlowCallable` represented by `callable`. + * + * It _may not_ be the case that `call = callable.getACall()`, i.e. if `call` represents a `ClassCall`. + * * Used to limit the size of predicates. */ predicate connects(CallNode call, CallableValue callable) { @@ -352,38 +355,45 @@ module ArgumentPassing { } /** - * Gets the argument to `call` that is passed to the parameter at position `paramNr` in `callable`. - * If it is a positional argument, it must appear at position `argNr` in `call`. - * `argNr` will differ from `paramNr` for method- or constructor calls, where the first parameter + * Gets the node representing the argument to `call` that is passed to the parameter at + * (zero-based) index `paramN` in `callable`. If this is a positional argument, it must appear + * at index `argN` in `call`. + * + * `argN` will differ from `paramN` for method- or constructor calls, where the first parameter * is `self` and the first positional argument is passed to the second positional parameter. + * Similarly for classmethod calls, where the first parameter is `cls`. + * + * NOT SUPPORTED: Keyword-only parameters. */ - Node getArg(CallNode call, int argNr, CallableValue callable, int paramNr) { + Node getArg(CallNode call, int argN, CallableValue callable, int paramN) { connects(call, callable) and - paramNr - argNr in [0, 1] and // constrain for now to limit the size of the predicate; we only use it to insert one argument (self). + paramN - argN in [0, 1] and // constrain for now to limit the size of the predicate; we only use it to insert one argument (self). ( // positional argument - result = TCfgNode(call.getArg(argNr)) + result = TCfgNode(call.getArg(argN)) or // keyword argument + // TODO: Since `getArgName` have no results for keyword-only parameters, + // these are currently not supported. exists(Function f, string argName | f = callable.getScope() and - f.getArgName(paramNr) = argName and + f.getArgName(paramN) = argName and result = TCfgNode(call.getArgByName(argName)) ) or // a synthezised argument passed to the starred parameter (at position -1) callable.getScope().hasVarArg() and - paramNr = -1 and + paramN = -1 and result = TPosOverflowNode(call, callable) or // a synthezised argument passed to the doubly starred parameter (at position -2) callable.getScope().hasKwArg() and - paramNr = -2 and + paramN = -2 and result = TKwOverflowNode(call, callable) or // argument unpacked from dict exists(string name | - call_unpacks(call, argNr, callable, name, paramNr) and + call_unpacks(call, argN, callable, name, paramN) and result = TKwUnpacked(call, callable, name) ) ) @@ -425,6 +435,7 @@ module ArgumentPassing { not exists(call.getArg(argNr)) and // no positional arguement available name = f.getArgName(n) and // not exists(call.getArgByName(name)) and // only matches keyword arguments not preceded by ** + // TODO: make the below logic respect control flow splitting (by not going to the AST). not call.getNode().getANamedArg().(Keyword).getArg() = name and // no keyword argument available n >= 0 and n < f.getPositionalParameterCount() + f.getKeywordOnlyParameterCount() and @@ -519,6 +530,8 @@ class DataFlowModuleScope extends DataFlowCallable, TModule { * as the class call will synthesize an argument node to be mapped to the `self` parameter. * * A call corresponding to a special method call is handled by the corresponding `SpecialMethodCallNode`. + * + * TODO: Add `TClassMethodCall` mapping `cls` appropriately. */ newtype TDataFlowCall = TFunctionCall(CallNode call) { call = any(FunctionValue f).getAFunctionCall() } or From 19796a4c9c93cafb8e8be9d427fd9b3c1e588b3d Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 8 Oct 2020 10:35:01 +0200 Subject: [PATCH 043/166] Python: Improve tests and make `validTest` happy --- .../dataflow/coverage/argumentPassing.py | 28 ++++++++++++--- .../coverage/argumentRouting1.expected | 34 +++++++++---------- .../coverage/argumentRouting2.expected | 12 +++---- .../coverage/argumentRouting3.expected | 12 +++---- 4 files changed, 52 insertions(+), 34 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py index d48eb6a2e5a..f1cf1f09214 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py +++ b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py @@ -14,13 +14,21 @@ arg6 = "source6" arg7 = "source7" -def SINK(x, expected=arg): - if x == expected: +def SINK_TEST(x, test): + if test(x): print("OK") else: print("Unexpected flow", x) +def SINK(x, expected=arg): + SINK_TEST(x, test=lambda x: x == expected) + + +def SINK_F(x, unexpected=arg): + SINK_TEST(x, test=lambda x: x != unexpected) + + def SINK1(x): SINK(x, expected=arg1) @@ -29,6 +37,10 @@ def SINK2(x): SINK(x, expected=arg2) +def SINK2_F(x): + SINK_F(x, unexpected=arg2) + + def SINK3(x): SINK(x, expected=arg3) @@ -66,13 +78,17 @@ def argument_passing( SINK4(d) SINK5(e) SINK6(f) - SINK7(g["g"]) + try: + SINK7(g["g"]) + except: + print("OK") @expects(7) def test_argument_passing1(): argument_passing(arg1, *(arg2, arg3, arg4), e=arg5, **{"f": arg6, "g": arg7}) - + + @expects(7) def test_argument_passing2(): argument_passing(arg1, arg2, arg3, f=arg6) @@ -123,10 +139,12 @@ def grab_foo_bar_baz(foo, **kwargs): grab_bar_baz(**kwargs) +# It is not possible to pass `bar` into `kwargs`, +# since `bar` is a valid keyword argument. def grab_bar_baz(bar, **kwargs): SINK2(bar) try: - SINK2(kwargs["bar"]) + SINK2_F(kwargs["bar"]) except: print("OK") grab_baz(**kwargs) diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected index 6116db4fab2..7cc6b64db40 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting1.expected @@ -1,20 +1,20 @@ -| argumentPassing.py:74:22:74:25 | ControlFlowNode for arg1 | argumentPassing.py:63:11:63:11 | ControlFlowNode for a | -| argumentPassing.py:78:22:78:25 | ControlFlowNode for arg1 | argumentPassing.py:63:11:63:11 | ControlFlowNode for a | -| argumentPassing.py:88:19:88:22 | ControlFlowNode for arg1 | argumentPassing.py:82:11:82:11 | ControlFlowNode for a | -| argumentPassing.py:89:19:89:22 | ControlFlowNode for arg1 | argumentPassing.py:82:11:82:11 | ControlFlowNode for a | -| argumentPassing.py:90:19:90:22 | ControlFlowNode for arg1 | argumentPassing.py:82:11:82:11 | ControlFlowNode for a | -| argumentPassing.py:101:45:101:48 | ControlFlowNode for arg1 | argumentPassing.py:94:11:94:11 | ControlFlowNode for a | -| argumentPassing.py:102:27:102:30 | ControlFlowNode for arg1 | argumentPassing.py:94:11:94:11 | ControlFlowNode for a | -| argumentPassing.py:103:27:103:30 | ControlFlowNode for arg1 | argumentPassing.py:94:11:94:11 | ControlFlowNode for a | -| argumentPassing.py:115:28:115:31 | ControlFlowNode for arg1 | argumentPassing.py:107:11:107:11 | ControlFlowNode for a | -| argumentPassing.py:141:46:141:49 | ControlFlowNode for arg1 | argumentPassing.py:122:11:122:13 | ControlFlowNode for foo | -| argumentPassing.py:149:14:149:17 | ControlFlowNode for arg1 | argumentPassing.py:147:15:147:15 | ControlFlowNode for a | -| argumentPassing.py:156:19:156:22 | ControlFlowNode for arg1 | argumentPassing.py:154:15:154:15 | ControlFlowNode for a | -| argumentPassing.py:164:15:164:18 | ControlFlowNode for arg1 | argumentPassing.py:162:19:162:22 | ControlFlowNode for Subscript | -| argumentPassing.py:171:13:171:16 | ControlFlowNode for arg1 | argumentPassing.py:169:15:169:15 | ControlFlowNode for a | -| argumentPassing.py:178:16:178:19 | ControlFlowNode for arg1 | argumentPassing.py:176:15:176:15 | ControlFlowNode for a | -| argumentPassing.py:185:15:185:18 | ControlFlowNode for arg1 | argumentPassing.py:183:15:183:15 | ControlFlowNode for a | -| argumentPassing.py:192:23:192:26 | ControlFlowNode for arg1 | argumentPassing.py:190:15:190:20 | ControlFlowNode for Subscript | +| argumentPassing.py:89:22:89:25 | ControlFlowNode for arg1 | argumentPassing.py:75:11:75:11 | ControlFlowNode for a | +| argumentPassing.py:94:22:94:25 | ControlFlowNode for arg1 | argumentPassing.py:75:11:75:11 | ControlFlowNode for a | +| argumentPassing.py:104:19:104:22 | ControlFlowNode for arg1 | argumentPassing.py:98:11:98:11 | ControlFlowNode for a | +| argumentPassing.py:105:19:105:22 | ControlFlowNode for arg1 | argumentPassing.py:98:11:98:11 | ControlFlowNode for a | +| argumentPassing.py:106:19:106:22 | ControlFlowNode for arg1 | argumentPassing.py:98:11:98:11 | ControlFlowNode for a | +| argumentPassing.py:117:45:117:48 | ControlFlowNode for arg1 | argumentPassing.py:110:11:110:11 | ControlFlowNode for a | +| argumentPassing.py:118:27:118:30 | ControlFlowNode for arg1 | argumentPassing.py:110:11:110:11 | ControlFlowNode for a | +| argumentPassing.py:119:27:119:30 | ControlFlowNode for arg1 | argumentPassing.py:110:11:110:11 | ControlFlowNode for a | +| argumentPassing.py:131:28:131:31 | ControlFlowNode for arg1 | argumentPassing.py:123:11:123:11 | ControlFlowNode for a | +| argumentPassing.py:159:46:159:49 | ControlFlowNode for arg1 | argumentPassing.py:138:11:138:13 | ControlFlowNode for foo | +| argumentPassing.py:167:14:167:17 | ControlFlowNode for arg1 | argumentPassing.py:165:15:165:15 | ControlFlowNode for a | +| argumentPassing.py:174:19:174:22 | ControlFlowNode for arg1 | argumentPassing.py:172:15:172:15 | ControlFlowNode for a | +| argumentPassing.py:182:15:182:18 | ControlFlowNode for arg1 | argumentPassing.py:180:19:180:22 | ControlFlowNode for Subscript | +| argumentPassing.py:189:13:189:16 | ControlFlowNode for arg1 | argumentPassing.py:187:15:187:15 | ControlFlowNode for a | +| argumentPassing.py:196:16:196:19 | ControlFlowNode for arg1 | argumentPassing.py:194:15:194:15 | ControlFlowNode for a | +| argumentPassing.py:203:15:203:18 | ControlFlowNode for arg1 | argumentPassing.py:201:15:201:15 | ControlFlowNode for a | +| argumentPassing.py:210:23:210:26 | ControlFlowNode for arg1 | argumentPassing.py:208:15:208:20 | ControlFlowNode for Subscript | | classes.py:563:5:563:16 | SSA variable with_getitem | classes.py:557:15:557:18 | ControlFlowNode for self | | classes.py:578:5:578:16 | SSA variable with_setitem | classes.py:573:15:573:18 | ControlFlowNode for self | | classes.py:593:5:593:16 | SSA variable with_delitem | classes.py:588:15:588:18 | ControlFlowNode for self | diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected index 14e52744b25..de67bcf3e83 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting2.expected @@ -1,9 +1,9 @@ -| argumentPassing.py:78:28:78:31 | ControlFlowNode for arg2 | argumentPassing.py:64:11:64:11 | ControlFlowNode for b | -| argumentPassing.py:88:25:88:28 | ControlFlowNode for arg2 | argumentPassing.py:83:11:83:11 | ControlFlowNode for b | -| argumentPassing.py:89:27:89:30 | ControlFlowNode for arg2 | argumentPassing.py:83:11:83:11 | ControlFlowNode for b | -| argumentPassing.py:101:29:101:32 | ControlFlowNode for arg2 | argumentPassing.py:95:11:95:11 | ControlFlowNode for b | -| argumentPassing.py:116:30:116:33 | ControlFlowNode for arg2 | argumentPassing.py:108:11:108:11 | ControlFlowNode for b | -| argumentPassing.py:141:36:141:39 | ControlFlowNode for arg2 | argumentPassing.py:127:11:127:13 | ControlFlowNode for bar | +| argumentPassing.py:94:28:94:31 | ControlFlowNode for arg2 | argumentPassing.py:76:11:76:11 | ControlFlowNode for b | +| argumentPassing.py:104:25:104:28 | ControlFlowNode for arg2 | argumentPassing.py:99:11:99:11 | ControlFlowNode for b | +| argumentPassing.py:105:27:105:30 | ControlFlowNode for arg2 | argumentPassing.py:99:11:99:11 | ControlFlowNode for b | +| argumentPassing.py:117:29:117:32 | ControlFlowNode for arg2 | argumentPassing.py:111:11:111:11 | ControlFlowNode for b | +| argumentPassing.py:132:30:132:33 | ControlFlowNode for arg2 | argumentPassing.py:124:11:124:11 | ControlFlowNode for b | +| argumentPassing.py:159:36:159:39 | ControlFlowNode for arg2 | argumentPassing.py:145:11:145:13 | ControlFlowNode for bar | | classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:556:15:556:17 | ControlFlowNode for key | | classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:572:15:572:17 | ControlFlowNode for key | | classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:587:15:587:17 | ControlFlowNode for key | diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected index 7b0b55cf0e3..7673dcc4bf4 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected @@ -1,7 +1,7 @@ -| argumentPassing.py:78:34:78:37 | ControlFlowNode for arg3 | argumentPassing.py:65:11:65:11 | ControlFlowNode for c | -| argumentPassing.py:101:37:101:40 | ControlFlowNode for arg3 | argumentPassing.py:96:11:96:11 | ControlFlowNode for c | -| argumentPassing.py:102:43:102:46 | ControlFlowNode for arg3 | argumentPassing.py:96:11:96:11 | ControlFlowNode for c | -| argumentPassing.py:103:41:103:44 | ControlFlowNode for arg3 | argumentPassing.py:96:11:96:11 | ControlFlowNode for c | -| argumentPassing.py:117:36:117:39 | ControlFlowNode for arg3 | argumentPassing.py:109:11:109:11 | ControlFlowNode for c | -| argumentPassing.py:141:26:141:29 | ControlFlowNode for arg3 | argumentPassing.py:136:11:136:13 | ControlFlowNode for baz | +| argumentPassing.py:94:34:94:37 | ControlFlowNode for arg3 | argumentPassing.py:77:11:77:11 | ControlFlowNode for c | +| argumentPassing.py:117:37:117:40 | ControlFlowNode for arg3 | argumentPassing.py:112:11:112:11 | ControlFlowNode for c | +| argumentPassing.py:118:43:118:46 | ControlFlowNode for arg3 | argumentPassing.py:112:11:112:11 | ControlFlowNode for c | +| argumentPassing.py:119:41:119:44 | ControlFlowNode for arg3 | argumentPassing.py:112:11:112:11 | ControlFlowNode for c | +| argumentPassing.py:133:36:133:39 | ControlFlowNode for arg3 | argumentPassing.py:125:11:125:11 | ControlFlowNode for c | +| argumentPassing.py:159:26:159:29 | ControlFlowNode for arg3 | argumentPassing.py:154:11:154:13 | ControlFlowNode for baz | | classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:571:15:571:19 | ControlFlowNode for value | From 7d086b23ffe6f1806368a95783cd5e25a21c308c Mon Sep 17 00:00:00 2001 From: yoff Date: Thu, 8 Oct 2020 10:53:52 +0200 Subject: [PATCH 044/166] Update python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll Co-authored-by: Rasmus Wriedt Larsen --- .../ql/src/experimental/dataflow/internal/DataFlowPrivate.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index c22131e7d89..b3b8bfbdf06 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -565,7 +565,7 @@ abstract class DataFlowCall extends TDataFlowCall { } /** - * Represents a call to a callable (currently only callable values). + * Represents a call to a function/lambda. * This excludes calls to bound methods, classes, and special methods. * Bound method calls and class calls insert an argument for the explicit * `self` parameter, and special method calls have special argument passing. From d3e3c11fa6ed122f459cd2eab8d7f8b313ea3253 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 8 Oct 2020 21:16:40 +0200 Subject: [PATCH 045/166] add printAst query for printing JS/TS/JSON/YAML/HTML --- javascript/ql/src/printAst.ql | 28 + .../ql/src/semmle/javascript/Classes.qll | 56 +- .../src/semmle/javascript/ES2015Modules.qll | 8 + javascript/ql/src/semmle/javascript/Expr.qll | 74 +- javascript/ql/src/semmle/javascript/HTML.qll | 32 + javascript/ql/src/semmle/javascript/JSON.qll | 22 +- javascript/ql/src/semmle/javascript/JSX.qll | 14 +- .../ql/src/semmle/javascript/Locations.qll | 5 + .../ql/src/semmle/javascript/PrintAst.qll | 714 +++++ javascript/ql/src/semmle/javascript/Stmt.qll | 56 +- .../ql/src/semmle/javascript/Templates.qll | 6 + .../ql/src/semmle/javascript/TypeScript.qll | 104 +- .../ql/src/semmle/javascript/Variables.qll | 20 +- javascript/ql/src/semmle/javascript/YAML.qll | 14 +- .../library-tests/Arrays/printAst.expected | 954 ++++++ .../ql/test/library-tests/Arrays/printAst.ql | 2 + .../printAst.expected | 84 + .../HTMLElementAndHTMLAttribute/printAst.ql | 2 + .../test/library-tests/JSON/printAst.expected | 44 + .../ql/test/library-tests/JSON/printAst.ql | 2 + .../test/library-tests/JSX/printAst.expected | 308 ++ .../ql/test/library-tests/JSX/printAst.ql | 2 + .../TypeAnnotations/printAst.expected | 2608 +++++++++++++++++ .../TypeScript/TypeAnnotations/printAst.ql | 2 + .../TypeScript/Types/printAst.expected | 1305 +++++++++ .../TypeScript/Types/printAst.ql | 2 + .../test/library-tests/YAML/printAst.expected | 151 + .../ql/test/library-tests/YAML/printAst.ql | 2 + 28 files changed, 6592 insertions(+), 29 deletions(-) create mode 100644 javascript/ql/src/printAst.ql create mode 100644 javascript/ql/src/semmle/javascript/PrintAst.qll create mode 100644 javascript/ql/test/library-tests/Arrays/printAst.expected create mode 100644 javascript/ql/test/library-tests/Arrays/printAst.ql create mode 100644 javascript/ql/test/library-tests/HTML/HTMLElementAndHTMLAttribute/printAst.expected create mode 100644 javascript/ql/test/library-tests/HTML/HTMLElementAndHTMLAttribute/printAst.ql create mode 100644 javascript/ql/test/library-tests/JSON/printAst.expected create mode 100644 javascript/ql/test/library-tests/JSON/printAst.ql create mode 100644 javascript/ql/test/library-tests/JSX/printAst.expected create mode 100644 javascript/ql/test/library-tests/JSX/printAst.ql create mode 100644 javascript/ql/test/library-tests/TypeScript/TypeAnnotations/printAst.expected create mode 100644 javascript/ql/test/library-tests/TypeScript/TypeAnnotations/printAst.ql create mode 100644 javascript/ql/test/library-tests/TypeScript/Types/printAst.expected create mode 100644 javascript/ql/test/library-tests/TypeScript/Types/printAst.ql create mode 100644 javascript/ql/test/library-tests/YAML/printAst.expected create mode 100644 javascript/ql/test/library-tests/YAML/printAst.ql diff --git a/javascript/ql/src/printAst.ql b/javascript/ql/src/printAst.ql new file mode 100644 index 00000000000..564a5f15a53 --- /dev/null +++ b/javascript/ql/src/printAst.ql @@ -0,0 +1,28 @@ +/** + * @name Print AST + * @description Outputs a representation of a file's Abstract Syntax Tree. This + * query is used by the VS Code extension. + * @id js/print-ast + * @kind graph + * @tags ide-contextual-queries/print-ast + */ + +import javascript +import semmle.javascript.PrintAst +import definitions + +/** + * The source file to generate an AST from. + */ +external string selectedSourceFile(); + +class PrintAstConfigurationOverride extends PrintAstConfiguration { + /** + * Holds if the location matches the selected file in the VS Code extension and + * the element is not a synthetic constructor. + */ + override predicate shouldPrint(Locatable e, Location l) { + super.shouldPrint(e, l) and + l.getFile() = getEncodedFile(selectedSourceFile()) + } +} diff --git a/javascript/ql/src/semmle/javascript/Classes.qll b/javascript/ql/src/semmle/javascript/Classes.qll index 93eead18658..82e782c4690 100644 --- a/javascript/ql/src/semmle/javascript/Classes.qll +++ b/javascript/ql/src/semmle/javascript/Classes.qll @@ -256,6 +256,8 @@ class ClassDefinition extends @class_definition, ClassOrInterface, AST::ValueNod ClassDefinition getSuperClassDefinition() { result = getSuperClass().analyze().getAValue().(AbstractClass).getClass() } + + override string getAPrimaryQlClass() { result = "ClassDefinition" } } /** @@ -342,6 +344,8 @@ private class ClassInitializedMember extends MemberDeclaration { ClassInitializedMember() { this instanceof MethodDefinition or this.isStatic() } int getIndex() { properties(this, _, result, _, _) } + + override string getAPrimaryQlClass() { result = "ClassInitializedMember" } } /** @@ -361,6 +365,8 @@ class SuperExpr extends @super_expr, Expr { * which is the nearest enclosing non-arrow function. */ Function getBinder() { result = getEnclosingFunction().getThisBinder() } + + override string getAPrimaryQlClass() { result = "SuperExpr" } } /** @@ -412,6 +418,8 @@ class SuperPropAccess extends PropAccess { */ class NewTargetExpr extends @newtarget_expr, Expr { override predicate isImpure() { none() } + + override string getAPrimaryQlClass() { result = "NewTargetExpr" } } /** @@ -574,6 +582,8 @@ class MemberDeclaration extends @property, Documentable { * True if this is abstract, ambient, or an overload signature. */ predicate isSignature() { not isConcrete() } + + override string getAPrimaryQlClass() { result = "MemberDeclaration" } } /** @@ -748,7 +758,9 @@ private predicate hasOverloadedConstructorCallSignature(ClassOrInterface type) { * } * ``` */ -class MethodDefinition extends MethodDeclaration, MemberDefinition { } +class MethodDefinition extends MethodDeclaration, MemberDefinition { + override string getAPrimaryQlClass() { result = "MethodDefinition" } +} /** * A method signature declared in a class or interface, that is, a method without a function body. @@ -763,7 +775,9 @@ class MethodDefinition extends MethodDeclaration, MemberDefinition { } * * Note that TypeScript call signatures are not considered method signatures. */ -class MethodSignature extends MethodDeclaration, MemberSignature { } +class MethodSignature extends MethodDeclaration, MemberSignature { + override string getAPrimaryQlClass() { result = "MethodSignature" } +} /** * A constructor declaration in a class, either a concrete definition or a signature without a body. @@ -792,6 +806,8 @@ class ConstructorDeclaration extends MethodDeclaration { /** Holds if this is a synthetic default constructor. */ predicate isSynthetic() { getLocation().isEmpty() } + + override string getAPrimaryQlClass() { result = "ConstructorDeclaration" } } /** @@ -813,7 +829,9 @@ class ConstructorDeclaration extends MethodDeclaration { * } * ``` */ -class ConstructorDefinition extends ConstructorDeclaration, MethodDefinition { } +class ConstructorDefinition extends ConstructorDeclaration, MethodDefinition { + override string getAPrimaryQlClass() { result = "ConstructorDefinition" } +} /** * A constructor signature declared in a class, that is, a constructor without a function body. @@ -824,7 +842,9 @@ class ConstructorDefinition extends ConstructorDeclaration, MethodDefinition { } * } * ``` */ -class ConstructorSignature extends ConstructorDeclaration, MethodSignature { } +class ConstructorSignature extends ConstructorDeclaration, MethodSignature { + override string getAPrimaryQlClass() { result = "ConstructorSignature" } +} /** * A function generated by the extractor to implement a synthetic default constructor. @@ -925,7 +945,9 @@ abstract class AccessorMethodSignature extends MethodSignature, AccessorMethodDe * } * ``` */ -class GetterMethodDeclaration extends AccessorMethodDeclaration, @property_getter { } +class GetterMethodDeclaration extends AccessorMethodDeclaration, @property_getter { + override string getAPrimaryQlClass() { result = "GetterMethodDeclaration" } +} /** * A concrete getter method definition in a class, that is, a getter method with a function body. @@ -945,7 +967,9 @@ class GetterMethodDeclaration extends AccessorMethodDeclaration, @property_gette * } * ``` */ -class GetterMethodDefinition extends GetterMethodDeclaration, AccessorMethodDefinition { } +class GetterMethodDefinition extends GetterMethodDeclaration, AccessorMethodDefinition { + override string getAPrimaryQlClass() { result = "GetterMethodDefinition" } +} /** * A getter method signature declared in a class or interface, that is, a getter method without a function body. @@ -958,7 +982,9 @@ class GetterMethodDefinition extends GetterMethodDeclaration, AccessorMethodDefi * } * ``` */ -class GetterMethodSignature extends GetterMethodDeclaration, AccessorMethodSignature { } +class GetterMethodSignature extends GetterMethodDeclaration, AccessorMethodSignature { + override string getAPrimaryQlClass() { result = "GetterMethodSignature" } +} /** * A setter method declaration in a class or interface, either a concrete definition or a signature without a body. @@ -981,7 +1007,9 @@ class GetterMethodSignature extends GetterMethodDeclaration, AccessorMethodSigna * } * ``` */ -class SetterMethodDeclaration extends AccessorMethodDeclaration, @property_setter { } +class SetterMethodDeclaration extends AccessorMethodDeclaration, @property_setter { + override string getAPrimaryQlClass() { result = "SetterMethodDeclaration" } +} /** * A concrete setter method definition in a class, that is, a setter method with a function body @@ -1000,7 +1028,9 @@ class SetterMethodDeclaration extends AccessorMethodDeclaration, @property_sette * } * ``` */ -class SetterMethodDefinition extends SetterMethodDeclaration, AccessorMethodDefinition { } +class SetterMethodDefinition extends SetterMethodDeclaration, AccessorMethodDefinition { + override string getAPrimaryQlClass() { result = "SetterMethodDefinition" } +} /** * A setter method signature declared in a class or interface, that is, a setter method without a function body. @@ -1013,7 +1043,9 @@ class SetterMethodDefinition extends SetterMethodDeclaration, AccessorMethodDefi * } * ``` */ -class SetterMethodSignature extends SetterMethodDeclaration, AccessorMethodSignature { } +class SetterMethodSignature extends SetterMethodDeclaration, AccessorMethodSignature { + override string getAPrimaryQlClass() { result = "SetterMethodSignature" } +} /** * A field declaration in a class or interface, either a concrete definition or an abstract or ambient field signature. @@ -1047,6 +1079,8 @@ class FieldDeclaration extends MemberDeclaration, @field { /** Holds if this is a TypeScript field marked as definitely assigned with the `!` operator. */ predicate hasDefiniteAssignmentAssertion() { has_definite_assignment_assertion(this) } + + override string getAPrimaryQlClass() { result = "FieldDeclaration" } } /** @@ -1203,4 +1237,6 @@ class IndexSignature extends @index_signature, MemberSignature { override InterfaceDefinition getDeclaringType() { result = MemberSignature.super.getDeclaringType() } + + override string getAPrimaryQlClass() { result = "IndexSignature" } } diff --git a/javascript/ql/src/semmle/javascript/ES2015Modules.qll b/javascript/ql/src/semmle/javascript/ES2015Modules.qll index 47a8ff426ae..a8b189a85fd 100644 --- a/javascript/ql/src/semmle/javascript/ES2015Modules.qll +++ b/javascript/ql/src/semmle/javascript/ES2015Modules.qll @@ -79,6 +79,8 @@ class ImportDeclaration extends Stmt, Import, @import_declaration { /** Holds if this is declared with the `type` keyword, so it only imports types. */ predicate isTypeOnly() { has_type_keyword(this) } + + override string getAPrimaryQlClass() { result = "ImportDeclaration" } } /** A literal path expression appearing in an `import` declaration. */ @@ -129,6 +131,8 @@ class ImportSpecifier extends Expr, @import_specifier { /** Gets the local variable into which this specifier imports. */ VarDecl getLocal() { result = getChildExpr(1) } + + override string getAPrimaryQlClass() { result = "ImportSpecifier" } } /** @@ -262,6 +266,8 @@ abstract class ExportDeclaration extends Stmt, @export_declaration { /** Holds if is declared with the `type` keyword, so only types are exported. */ predicate isTypeOnly() { has_type_keyword(this) } + + override string getAPrimaryQlClass() { result = "ExportDeclaration" } } /** @@ -511,6 +517,8 @@ class ExportSpecifier extends Expr, @exportspecifier { * an exported name since it does not export a unique symbol. */ string getExportedName() { result = getExported().getName() } + + override string getAPrimaryQlClass() { result = "ExportSpecifier" } } /** diff --git a/javascript/ql/src/semmle/javascript/Expr.qll b/javascript/ql/src/semmle/javascript/Expr.qll index 016a740c785..6126931bc5b 100644 --- a/javascript/ql/src/semmle/javascript/Expr.qll +++ b/javascript/ql/src/semmle/javascript/Expr.qll @@ -273,6 +273,8 @@ private DataFlow::Node getCatchParameterFromStmt(Stmt stmt) { class Identifier extends @identifier, ExprOrType { /** Gets the name of this identifier. */ string getName() { literals(result, _, this) } + + override string getAPrimaryQlClass() { result = "Identifier" } } /** @@ -290,6 +292,8 @@ class Identifier extends @identifier, ExprOrType { */ class Label extends @label, Identifier, Expr { override predicate isImpure() { none() } + + override string getAPrimaryQlClass() { result = "Label" } } /** @@ -317,6 +321,8 @@ class Literal extends @literal, Expr { string getRawValue() { literals(_, result, this) } override predicate isImpure() { none() } + + override string getAPrimaryQlClass() { result = "Literal" } } /** @@ -341,6 +347,8 @@ class ParExpr extends @par_expr, Expr { override Expr getUnderlyingValue() { result = getExpression().getUnderlyingValue() } override Expr getUnderlyingReference() { result = getExpression().getUnderlyingReference() } + + override string getAPrimaryQlClass() { result = "ParExpr" } } /** @@ -462,6 +470,8 @@ class RegExpLiteral extends @regexp_literal, Literal, RegExpParent { /** Holds if this regular expression has an `s` flag. */ predicate isDotAll() { RegExp::isDotAll(getFlags()) } + + override string getAPrimaryQlClass() { result = "RegExpLiteral" } } /** @@ -491,6 +501,8 @@ class ThisExpr extends @this_expr, Expr { or result = getContainer().(TopLevel) } + + override string getAPrimaryQlClass() { result = "ThisExpr" } } /** @@ -528,6 +540,8 @@ class ArrayExpr extends @array_expr, Expr { predicate hasOmittedElement() { elementIsOmitted(_) } override predicate isImpure() { getAnElement().isImpure() } + + override string getAPrimaryQlClass() { result = "ArrayExpr" } } /** @@ -568,6 +582,8 @@ class ObjectExpr extends @obj_expr, Expr { predicate hasTrailingComma() { this.getLastToken().getPreviousToken().getValue() = "," } override predicate isImpure() { getAProperty().isImpure() } + + override string getAPrimaryQlClass() { result = "ObjectExpr" } } /** @@ -668,6 +684,8 @@ class Property extends @property, Documentable { * decorators `@A` and `@B`. */ Decorator getADecorator() { result = getDecorator(_) } + + override string getAPrimaryQlClass() { result = "Property" } } /** @@ -791,6 +809,8 @@ class FunctionExpr extends @function_expr, Expr, Function { override StmtContainer getEnclosingContainer() { result = Expr.super.getContainer() } override predicate isImpure() { none() } + + override string getAPrimaryQlClass() { result = "FunctionExpr" } } /** @@ -811,6 +831,8 @@ class ArrowFunctionExpr extends @arrow_function_expr, Expr, Function { override predicate isImpure() { none() } override Function getThisBinder() { result = getEnclosingContainer().(Function).getThisBinder() } + + override string getAPrimaryQlClass() { result = "ArrowFunctionExpr" } } /** @@ -838,6 +860,8 @@ class SeqExpr extends @seq_expr, Expr { override predicate isImpure() { getAnOperand().isImpure() } override Expr getUnderlyingValue() { result = getLastOperand().getUnderlyingValue() } + + override string getAPrimaryQlClass() { result = "SeqExpr" } } /** @@ -866,6 +890,8 @@ class ConditionalExpr extends @conditional_expr, Expr { getCondition().isImpure() or getABranch().isImpure() } + + override string getAPrimaryQlClass() { result = "ConditionalExpr" } } /** @@ -986,7 +1012,9 @@ class InvokeExpr extends @invokeexpr, Expr { * new Array(16) * ``` */ -class NewExpr extends @new_expr, InvokeExpr { } +class NewExpr extends @new_expr, InvokeExpr { + override string getAPrimaryQlClass() { result = "NewExpr" } +} /** * A function call expression. @@ -1005,6 +1033,8 @@ class CallExpr extends @call_expr, InvokeExpr { * is invoked, if any. */ Expr getReceiver() { result = getCallee().(PropAccess).getBase() } + + override string getAPrimaryQlClass() { result = "CallExpr" } } /** @@ -1037,6 +1067,8 @@ class MethodCallExpr extends CallExpr { /** Holds if this invocation calls method `m` on expression `base`. */ predicate calls(Expr base, string m) { getMethodRef().accesses(base, m) } + + override string getAPrimaryQlClass() { result = "MethodCallExpr" } } /** @@ -1103,6 +1135,8 @@ class DotExpr extends @dot_expr, PropAccess { Identifier getProperty() { result = getChildExpr(1) } override predicate isImpure() { getBase().isImpure() } + + override string getAPrimaryQlClass() { result = "DotExpr" } } /** @@ -1124,6 +1158,8 @@ class IndexExpr extends @index_expr, PropAccess { getBase().isImpure() or getIndex().isImpure() } + + override string getAPrimaryQlClass() { result = "IndexExpr" } } /** @@ -1148,6 +1184,8 @@ class UnaryExpr extends @unaryexpr, Expr { override ControlFlowNode getFirstControlFlowNode() { result = getOperand().getFirstControlFlowNode() } + + override string getAPrimaryQlClass() { result = "UnaryExpr" } } /** @@ -1258,6 +1296,8 @@ class DeleteExpr extends @delete_expr, UnaryExpr { */ class SpreadElement extends @spread_element, UnaryExpr { override string getOperator() { result = "..." } + + override string getAPrimaryQlClass() { result = "SpreadElement" } } /** @@ -1314,6 +1354,8 @@ class BinaryExpr extends @binaryexpr, Expr { result = c4 - c3 - 1 ) } + + override string getAPrimaryQlClass() { result = "BinaryExpr" } } /** @@ -1846,6 +1888,8 @@ class Assignment extends @assignment, Expr { */ class AssignExpr extends @assign_expr, Assignment { override Expr getUnderlyingValue() { result = getRhs().getUnderlyingValue() } + + override string getAPrimaryQlClass() { result = "AssignExpr" } } private class TCompoundAssignExpr = @@ -1864,7 +1908,9 @@ private class TCompoundAssignExpr = * x /= 2 * ``` */ -class CompoundAssignExpr extends TCompoundAssignExpr, Assignment { } +class CompoundAssignExpr extends TCompoundAssignExpr, Assignment { + override string getAPrimaryQlClass() { result = "CompoundAssignExpr" } +} /** * A compound add-assign expression. @@ -2056,6 +2102,8 @@ class UpdateExpr extends @updateexpr, Expr { override ControlFlowNode getFirstControlFlowNode() { result = getOperand().getFirstControlFlowNode() } + + override string getAPrimaryQlClass() { result = "UpdateExpr" } } /** @@ -2137,6 +2185,8 @@ class YieldExpr extends @yield_expr, Expr { or not exists(getOperand()) and result = this } + + override string getAPrimaryQlClass() { result = "YieldExpr" } } /** @@ -2194,6 +2244,8 @@ class ComprehensionExpr extends @comprehension_expr, Expr { predicate isPostfix() { exists(Token tk | tk = getFirstToken().getNextToken() | not tk.getValue().regexpMatch("if|for")) } + + override string getAPrimaryQlClass() { result = "ComprehensionExpr" } } /** @@ -2246,6 +2298,8 @@ class ComprehensionBlock extends @comprehension_block, Expr { getIterator().isImpure() or getDomain().isImpure() } + + override string getAPrimaryQlClass() { result = "ComprehensionBlock" } } /** @@ -2468,6 +2522,8 @@ class LegacyLetExpr extends Expr, @legacy_letexpr { /** Gets the expression this `let` expression scopes over. */ Expr getBody() { result = getChildExpr(-1) } + + override string getAPrimaryQlClass() { result = "LegacyLetExpr" } } /** @@ -2569,6 +2625,8 @@ class AwaitExpr extends @await_expr, Expr { override ControlFlowNode getFirstControlFlowNode() { result = getOperand().getFirstControlFlowNode() } + + override string getAPrimaryQlClass() { result = "AwaitExpr" } } /** @@ -2586,6 +2644,8 @@ class AwaitExpr extends @await_expr, Expr { */ class FunctionSentExpr extends @function_sent_expr, Expr { override predicate isImpure() { none() } + + override string getAPrimaryQlClass() { result = "FunctionSentExpr" } } /** @@ -2621,6 +2681,8 @@ class Decorator extends @decorator, Expr { override ControlFlowNode getFirstControlFlowNode() { result = getExpression().getFirstControlFlowNode() } + + override string getAPrimaryQlClass() { result = "Decorator" } } /** @@ -2687,6 +2749,8 @@ class FunctionBindExpr extends @bind_expr, Expr { or not exists(getObject()) and result = getCallee().getFirstControlFlowNode() } + + override string getAPrimaryQlClass() { result = "FunctionBindExpr" } } /** @@ -2711,6 +2775,8 @@ class DynamicImportExpr extends @dynamic_import, Expr, Import { override Module getEnclosingModule() { result = getTopLevel() } override DataFlow::Node getImportedModuleNode() { result = DataFlow::valueNode(this) } + + override string getAPrimaryQlClass() { result = "DynamicImportExpr" } } /** A literal path expression appearing in a dynamic import. */ @@ -2734,6 +2800,8 @@ private class LiteralDynamicImportPath extends PathExpr, ConstantString { */ class OptionalUse extends Expr, @optionalchainable { OptionalUse() { isOptionalChaining(this) } + + override string getAPrimaryQlClass() { result = "OptionalUse" } } private class ChainElem extends Expr, @optionalchainable { @@ -2775,4 +2843,6 @@ class OptionalChainRoot extends ChainElem { */ class ImportMetaExpr extends @import_meta_expr, Expr { override predicate isImpure() { none() } + + override string getAPrimaryQlClass() { result = "ImportMetaExpr" } } diff --git a/javascript/ql/src/semmle/javascript/HTML.qll b/javascript/ql/src/semmle/javascript/HTML.qll index c8418066b49..898c0d01697 100644 --- a/javascript/ql/src/semmle/javascript/HTML.qll +++ b/javascript/ql/src/semmle/javascript/HTML.qll @@ -80,6 +80,8 @@ module HTML { } override string toString() { result = "<" + getName() + ">..." } + + override string getAPrimaryQlClass() { result = "HTML::Element" } } /** @@ -124,6 +126,34 @@ module HTML { string getValue() { xmlAttrs(this, _, _, result, _, _) } override string toString() { result = getName() + "=" + getValue() } + + /** + * Gets the inline script of this attribute, if any. + */ + CodeInAttribute getCodeInAttribute() { + exists( + string f, Location l1, int sl1, int sc1, int el1, int ec1, Location l2, int sl2, int sc2, + int el2, int ec2 + | + l1 = getLocation() and + l2 = result.getLocation() and + l1.hasLocationInfo(f, sl1, sc1, el1, ec1) and + l2.hasLocationInfo(f, sl2, sc2, el2, ec2) + | + ( + sl1 = sl2 and sc1 < sc2 + or + sl1 < sl2 + ) and + ( + el1 = el2 and ec1 > ec2 + or + el1 > el2 + ) + ) + } + + override string getAPrimaryQlClass() { result = "HTML::Attribute" } } /** @@ -227,6 +257,8 @@ module HTML { result = getInlineScript() or result = resolveSource() } + + override string getAPrimaryQlClass() { result = "HTML::ScriptElement" } } /** diff --git a/javascript/ql/src/semmle/javascript/JSON.qll b/javascript/ql/src/semmle/javascript/JSON.qll index c67f293c401..f0f4f59e98e 100644 --- a/javascript/ql/src/semmle/javascript/JSON.qll +++ b/javascript/ql/src/semmle/javascript/JSON.qll @@ -40,6 +40,8 @@ class JSONValue extends @json_value, Locatable { result = loc.getFile() ) } + + override string getAPrimaryQlClass() { result = "JSONValue" } } /** @@ -72,7 +74,9 @@ abstract class JSONPrimitiveValue extends JSONValue { * null * ``` */ -class JSONNull extends @json_null, JSONPrimitiveValue { } +class JSONNull extends @json_null, JSONPrimitiveValue { + override string getAPrimaryQlClass() { result = "JSONNull" } +} /** * A JSON-encoded Boolean value. @@ -84,7 +88,9 @@ class JSONNull extends @json_null, JSONPrimitiveValue { } * false * ``` */ -class JSONBoolean extends @json_boolean, JSONPrimitiveValue { } +class JSONBoolean extends @json_boolean, JSONPrimitiveValue { + override string getAPrimaryQlClass() { result = "JSONBoolean" } +} /** * A JSON-encoded number. @@ -96,7 +102,9 @@ class JSONBoolean extends @json_boolean, JSONPrimitiveValue { } * 1.0 * ``` */ -class JSONNumber extends @json_number, JSONPrimitiveValue { } +class JSONNumber extends @json_number, JSONPrimitiveValue { + override string getAPrimaryQlClass() { result = "JSONNumber" } +} /** * A JSON-encoded string value. @@ -107,7 +115,9 @@ class JSONNumber extends @json_number, JSONPrimitiveValue { } * "a string" * ``` */ -class JSONString extends @json_string, JSONPrimitiveValue { } +class JSONString extends @json_string, JSONPrimitiveValue { + override string getAPrimaryQlClass() { result = "JSONString" } +} /** * A JSON-encoded array. @@ -124,6 +134,8 @@ class JSONArray extends @json_array, JSONValue { /** Gets the string value of the `i`th element of this array. */ string getElementStringValue(int i) { result = getElementValue(i).(JSONString).getValue() } + + override string getAPrimaryQlClass() { result = "JSONArray" } } /** @@ -141,6 +153,8 @@ class JSONObject extends @json_object, JSONValue { /** Gets the string value of property `name` of this object. */ string getPropStringValue(string name) { result = getPropValue(name).(JSONString).getValue() } + + override string getAPrimaryQlClass() { result = "JSONObject" } } /** diff --git a/javascript/ql/src/semmle/javascript/JSX.qll b/javascript/ql/src/semmle/javascript/JSX.qll index b73b720bf6c..3b5c2910218 100644 --- a/javascript/ql/src/semmle/javascript/JSX.qll +++ b/javascript/ql/src/semmle/javascript/JSX.qll @@ -26,6 +26,8 @@ class JSXNode extends Expr, @jsx_element { * Gets the parent JSX element or fragment of this element. */ JSXNode getJsxParent() { this = result.getABodyElement() } + + override string getAPrimaryQlClass() { result = "JSXNode" } } /** @@ -61,6 +63,8 @@ class JSXElement extends JSXNode { override ControlFlowNode getFirstControlFlowNode() { result = getNameExpr().getFirstControlFlowNode() } + + override string getAPrimaryQlClass() { result = "JSXElement" } } /** @@ -80,6 +84,8 @@ class JSXFragment extends JSXNode { or not exists(getABodyElement()) and result = this } + + override string getAPrimaryQlClass() { result = "JSXFragment" } } /** @@ -124,6 +130,8 @@ class JSXAttribute extends ASTNode, @jsx_attribute { } override string toString() { properties(this, _, _, _, result) } + + override string getAPrimaryQlClass() { result = "JSXAttribute" } } /** @@ -163,6 +171,8 @@ class JSXQualifiedName extends Expr, @jsx_qualified_name { override ControlFlowNode getFirstControlFlowNode() { result = getNamespace().getFirstControlFlowNode() } + + override string getAPrimaryQlClass() { result = "JSXQualifiedName" } } /** @@ -214,7 +224,9 @@ class JSXName extends Expr { * { /* TBD */ } * */ -class JSXEmptyExpr extends Expr, @jsx_empty_expr { } +class JSXEmptyExpr extends Expr, @jsx_empty_expr { + override string getAPrimaryQlClass() { result = "JSXEmptyExpr" } +} /** * A legacy `@jsx` pragma. diff --git a/javascript/ql/src/semmle/javascript/Locations.qll b/javascript/ql/src/semmle/javascript/Locations.qll index 5f6a0050359..a84bcd18308 100644 --- a/javascript/ql/src/semmle/javascript/Locations.qll +++ b/javascript/ql/src/semmle/javascript/Locations.qll @@ -131,6 +131,11 @@ class Locatable extends @locatable { // to be overridden by subclasses none() } + + /** + * Gets the primary QL class for the Locatable. + */ + string getAPrimaryQlClass() { result = "???" } } /** diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll new file mode 100644 index 00000000000..26b9faa9d67 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -0,0 +1,714 @@ +/** + * Provides queries to pretty-print a JavaScript AST as a graph. + * + * By default, this will print the AST for all elements in the database. To change this behavior, + * extend `PrintAstConfiguration` and override `shouldPrint` to hold for only the elements + * you wish to view the AST for. + */ + +import javascript + +private newtype TPrintAstConfiguration = MkPrintAstConfiguration() + +/** + * The query can extend this class to control which elements are printed. + */ +class PrintAstConfiguration extends TPrintAstConfiguration { + /** + * Gets a textual representation of this `PrintAstConfiguration`. + */ + string toString() { result = "PrintAstConfiguration" } + + /** + * Controls whether the `Element` should be considered for AST printing. + * By default it checks whether the `Element` `e` belongs to `Location` `l`. + */ + predicate shouldPrint(Locatable e, Location l) { l = e.getLocation() } +} + +private predicate shouldPrint(Locatable e, Location l) { + exists(PrintAstConfiguration config | config.shouldPrint(e, l)) +} + +/** Holds if the given element does not need to be rendered in the AST, due to being compiler-generated or being a `TopLevel`. */ +private predicate isNotNeeded(Locatable el) { + exists(ClassDefinition c, ConstructorDeclaration constructor | + constructor = c.getConstructor() and + constructor.isSynthetic() and + el = constructor + ) + or + el instanceof TopLevel and + el.getLocation().getStartLine() = 0 and + el.getLocation().getStartColumn() = 0 + or + exists(ASTNode parent | isNotNeeded(parent) and not parent instanceof TopLevel | + el = parent.getAChild() + ) + or + // relaxing aggresive type inference. + none() +} + +/** + * Retrieves the canonical QL class(es) for entity `el` + */ +private string getQlClass(Locatable el) { + result = "[" + concat(el.getAPrimaryQlClass(), ",") + "] " + // Alternative implementation -- do not delete. It is useful for QL class discovery. + // not el.getAPrimaryQlClass() = "???" and result = "[" + concat(el.getAPrimaryQlClass(), ",") + "] " or el.getAPrimaryQlClass() = "???" and result = "??[" + concat(el.getAQlClass(), ",") + "] " +} + +/** + * Printed nodes for different file types. + */ +private newtype TPrintAstNode = + // JavaScript / TypeScript + TElementNode(ASTNode el) { shouldPrint(el, _) and not isNotNeeded(el) } or + TParametersNode(Function f) { shouldPrint(f, _) and not isNotNeeded(f) } or + TTypeParametersNode(TypeParameterized f) { shouldPrint(f, _) and not isNotNeeded(f) } or + TJSXAttributesNode(JSXElement n) { shouldPrint(n, _) and not isNotNeeded(n) } or + TJSXBodyElementsNode(JSXNode n) { shouldPrint(n, _) and not isNotNeeded(n) } or + TInvokeArgumentsNode(InvokeExpr n) { shouldPrint(n, _) and not isNotNeeded(n) } or + TInvokeTypeArgumentsNode(InvokeExpr invk) { shouldPrint(invk, _) and not isNotNeeded(invk) } or + // JSON + TJSONNode(JSONValue value) { shouldPrint(value, _) and not isNotNeeded(value) } or + // YAML + TYAMLNode(YAMLNode n) { shouldPrint(n, _) and not isNotNeeded(n) } or + TYAMLMappingNode(YAMLMapping mapping, int i) { + shouldPrint(mapping, _) and not isNotNeeded(mapping) and exists(mapping.getKeyNode(i)) + } or + // HTML + THTMLElementNode(HTML::Element e) { shouldPrint(e, _) and not isNotNeeded(e) } or + THTMLAttributesNodes(HTML::Element e) { shouldPrint(e, _) and not isNotNeeded(e) } or + THTMLAttributeNode(HTML::Attribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } or + THTMLScript(Script script) { shouldPrint(script, _) and not isNotNeeded(script) } or + THTMLCodeInAttr(CodeInAttribute attr) { shouldPrint(attr, _) and not isNotNeeded(attr) } + +/** + * A node in the output tree. + */ +class PrintAstNode extends TPrintAstNode { + /** + * Gets a textual representation of this node in the PrintAst output tree. + */ + string toString() { none() } + + /** + * Gets the child node at index `childIndex`. Child indices must be unique, + * but need not be contiguous. + */ + PrintAstNode getChild(int childIndex) { none() } + + /** + * Gets a child of this node. + */ + final PrintAstNode getAChild() { result = getChild(_) } + + /** + * Gets the parent of this node, if any. + */ + final PrintAstNode getParent() { result.getAChild() = this } + + /** + * Gets the location of this node in the source code. + */ + Location getLocation() { none() } + + /** + * Gets the value of the property of this node, where the name of the property + * is `key`. + */ + string getProperty(string key) { + key = "semmle.label" and + result = toString() + } + + /** + * Gets the label for the edge from this node to the specified child. By + * default, this is just the index of the child, but subclasses can override + * this. + */ + string getChildEdgeLabel(int childIndex) { + exists(getChild(childIndex)) and + result = childIndex.toString() + } +} + +/** A top-level AST node. */ +class TopLevelPrintAstNode extends PrintAstNode { + TopLevelPrintAstNode() { not exists(this.getParent()) } + + private int getOrder() { + this = + rank[result](TopLevelPrintAstNode n, Location l | + l = n.getLocation() + | + n + order by + l.getFile().getRelativePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(), + l.getEndColumn() + ) + } + + override string getProperty(string key) { + result = super.getProperty(key) + or + key = "semmle.order" and + result = this.getOrder().toString() + } +} + +/** + * Classes for printing JavaScript AST. + */ +private module PrintJavaScript { + /** + * A print node representing an `ASTNode`. + * + * Provides a default implemention that works for some (but not all) ASTNode's. + * More specific subclasses can override this class to get more specific behavior. + * + * The more specific subclasses are mostly used aggregate the children of the `ASTNode`. + * For example by aggregating all the parameters of a function under a single child node. + */ + class ElementNode extends PrintAstNode, TElementNode { + ASTNode element; + + ElementNode() { + this = TElementNode(element) and + not element instanceof Script and // Handled in module `PrintHTML` + not element instanceof CodeInAttribute // Handled in module `PrintHTML` + } + + override string toString() { result = getQlClass(element) + element.toString() } + + override Location getLocation() { result = element.getLocation() } + + /** + * Gets the `ASTNode` represented by this node. + */ + final ASTNode getElement() { result = element } + + override PrintAstNode getChild(int childIndex) { + exists(ASTNode el | result.(ElementNode).getElement() = el | + el = this.getChildNode(childIndex) + ) + } + + /** + * Gets the `i`th child of `element`. + * Can be overriden in subclasses to get more specific behavior for `getChild()`. + */ + ASTNode getChildNode(int childIndex) { result = element.getChild(childIndex) } + } + + /** + * A print node for function invocations. + * + * The children of this node are split into 3. + * 1: The callee. + * 2: An aggregate node for all the arguments. + * 3: An aggregate node for all the type argument. + */ + class InvokeNode extends ElementNode { + override InvokeExpr element; + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and result.(ElementNode).getElement() = element.getCallee() + or + childIndex = 1 and + exists(element.getAnArgument()) and + result.(InvokeArgumentsNode).getInvokeExpr() = element + or + childIndex = 2 and + exists(element.getATypeArgument()) and + result.(InvokeTypeArgumentsNode).getInvokeExpr() = element + } + } + + /** + * An aggregate node representing all the arguments for an function invocation. + */ + class InvokeArgumentsNode extends PrintAstNode, TInvokeArgumentsNode { + InvokeExpr invk; + + InvokeArgumentsNode() { this = TInvokeArgumentsNode(invk) and exists(invk.getAnArgument()) } + + override string toString() { result = "(Arguments)" } + + /** + * Gets the `InvokeExpr` for which this node represents the arguments. + */ + InvokeExpr getInvokeExpr() { result = invk } + + override PrintAstNode getChild(int childIndex) { + result.(ElementNode).getElement() = invk.getArgument(childIndex) + } + + override Location getLocation() { result = invk.getLocation() } + } + + /** + * An aggregate node representing all the type-arguments for an function invocation. + */ + class InvokeTypeArgumentsNode extends PrintAstNode, TInvokeTypeArgumentsNode { + InvokeExpr invk; + + InvokeTypeArgumentsNode() { + this = TInvokeTypeArgumentsNode(invk) and exists(invk.getATypeArgument()) + } + + override string toString() { result = "(TypeArguments)" } + + /** + * Gets the `InvokeExpr` for which this node represents the type-arguments. + */ + InvokeExpr getInvokeExpr() { result = invk } + + override PrintAstNode getChild(int childIndex) { + result.(ElementNode).getElement() = invk.getTypeArgument(childIndex) + } + + override Location getLocation() { result = invk.getLocation() } + } + + /** + * A print node for JSX nodes. + * + * The children of this node are split into 3. + * 1: The name of the JSX node (for example `Name` in ``). + * 2: An aggregate node for all the attributes (for example `href={foo}` in ``). + * 3: An aggregate node for all the body element (for example `foo` in `foo`). + */ + class JSXNodeNode extends ElementNode { + override JSXNode element; + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and result.(ElementNode).getElement() = element.(JSXElement).getNameExpr() + or + childIndex = 1 and + exists(element.getABodyElement()) and + result.(JSXBodyElementsNode).getJSXNode() = element + or + childIndex = 2 and + exists(element.(JSXElement).getAttribute(_)) and + result.(JSXAttributesNode).getJSXElement() = element + } + } + + /** + * An aggregate node representing all the attributes in a `JSXNode`. + */ + class JSXAttributesNode extends PrintAstNode, TJSXAttributesNode { + JSXElement n; + + JSXAttributesNode() { this = TJSXAttributesNode(n) and exists(n.getAttribute(_)) } + + override string toString() { result = "(Attributes)" } + + /** + * Gets the `JSXElement` for which this node represents the attributes. + */ + JSXElement getJSXElement() { result = n } + + override PrintAstNode getChild(int childIndex) { + result.(ElementNode).getElement() = n.getAttribute(childIndex) + } + + override Location getLocation() { result = n.getLocation() } + } + + /** + * An aggregate node representing all the body elements in a `JSXNode`. + */ + class JSXBodyElementsNode extends PrintAstNode, TJSXBodyElementsNode { + JSXNode n; + + JSXBodyElementsNode() { this = TJSXBodyElementsNode(n) and exists(n.getBodyElement(_)) } + + override string toString() { result = "(Body)" } + + /** + * Gets the `JSXNode` for which this node represents the body elements. + */ + JSXNode getJSXNode() { result = n } + + override PrintAstNode getChild(int childIndex) { + result.(ElementNode).getElement() = n.getBodyElement(childIndex) + } + + override Location getLocation() { result = n.getLocation() } + } + + /** + * A node representing any `ASTNode` that has type-parameters. + * + * The first child of this node is an aggregate node representing all the type-parameters. + */ + class TypeParameterizedNode extends ElementNode { + override TypeParameterized element; + + override PrintAstNode getChild(int childIndex) { + childIndex = -100 and result.(TypeParametersNode).getTypeParameterized() = element + or + result = super.getChild(childIndex) and + not result.(ElementNode).getElement() = element.getATypeParameter() + } + } + + /** + * A `PrintAstNode` for functions. + * + * The children of this node is split into 6: + * - The identifier (name) of the function. + * - An aggregate node for all the parameters of the function. + * - An aggregate node for all the type parameters of the function. + * - The `this` type annotation. + * - The return type annotation. + * - The body + */ + class FunctionNode extends TypeParameterizedNode { + override Function element; + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and result.(ElementNode).getElement() = element.getIdentifier() + or + childIndex = 1 and + result.(ParametersNode).getFunction() = element and + exists(element.getAParameter()) + or + childIndex = 2 and + result.(TypeParametersNode).getTypeParameterized() = element and + exists(element.getATypeParameter()) + or + childIndex = 3 and result.(ElementNode).getElement() = element.getThisTypeAnnotation() + or + childIndex = 4 and result.(ElementNode).getElement() = element.getReturnTypeAnnotation() + or + childIndex = 5 and result.(ElementNode).getElement() = element.getBody() + } + } + + /** + * A `PrintAstNode` for parameters. + * + * This node puts the type-annotation and default value of a parameter as children of the parameter itself. + * Instead of the default behavior (from `ElementNode`) where they would be children of the function. + */ + class ParameterNode extends ElementNode { + override Parameter element; + + override ASTNode getChildNode(int childIndex) { + childIndex = 0 and result = element.getTypeAnnotation() + or + childIndex = 1 and result = element.getDefault() + } + } + + /** + * An aggregate node representing all the parameters in a function. + */ + class ParametersNode extends PrintAstNode, TParametersNode { + Function f; + + ParametersNode() { this = TParametersNode(f) and exists(f.getAParameter()) } + + override string toString() { result = "(Parameters)" } + + /** + * Gets the `Function` for which this node represents the parameters. + */ + Function getFunction() { result = f } + + override PrintAstNode getChild(int childIndex) { + result.(ElementNode).getElement() = f.getParameter(childIndex) + } + + override Location getLocation() { result = f.getLocation() } + } + + /** + * An aggregate node representing all the type parameters in a function. + */ + class TypeParametersNode extends PrintAstNode, TTypeParametersNode { + TypeParameterized f; + + TypeParametersNode() { this = TTypeParametersNode(f) and exists(f.getATypeParameter()) } + + override string toString() { result = "(TypeParameters)" } + + /** + * Gets the `Function` for which this node represents the type-parameters. + */ + TypeParameterized getTypeParameterized() { result = f } + + override PrintAstNode getChild(int childIndex) { + result.(ElementNode).getElement() = f.getTypeParameter(childIndex) + } + + override Location getLocation() { result = f.getLocation() } + } +} + +/** + * Classes for printing JSON AST. + */ +private module PrintJSON { + /** + * A print node representing a JSON value in a .json file. + */ + class JSONNode extends PrintAstNode, TJSONNode { + JSONValue value; + + JSONNode() { this = TJSONNode(value) } + + override string toString() { result = getQlClass(value) + value.toString() } + + override Location getLocation() { result = value.getLocation() } + + /** + * Gets the `JSONValue` represented by this node. + */ + final JSONValue getValue() { result = value } + + override PrintAstNode getChild(int childIndex) { + exists(JSONValue child | result.(JSONNode).getValue() = child | + child = value.getChild(childIndex) + ) + } + } +} + +/** + * Classes for printing YAML AST. + */ +module PrintYAML { + /** + * A print node representing a YAML value in a .yml file. + */ + class YAMLNodeNode extends PrintAstNode, TYAMLNode { + YAMLNode node; + + YAMLNodeNode() { this = TYAMLNode(node) } + + override string toString() { result = getQlClass(node) + node.toString() } + + override Location getLocation() { result = node.getLocation() } + + /** + * Gets the `YAMLNode` represented by this node. + */ + final YAMLNode getValue() { result = node } + + override PrintAstNode getChild(int childIndex) { + exists(YAMLNode child | result.(YAMLNodeNode).getValue() = child | + child = node.getChildNode(childIndex) + ) + } + } + + /** + * A print node representing a `YAMLMapping`. + * + * Each child of this node aggregates the key and value of a mapping. + */ + class YAMLMappingNode extends YAMLNodeNode { + override YAMLMapping node; + + override PrintAstNode getChild(int childIndex) { + exists(YAMLMappingMapNode map | map = result | map.maps(node, childIndex)) + } + } + + /** + * A print node representing the `i`th mapping in `mapping`. + */ + class YAMLMappingMapNode extends PrintAstNode, TYAMLMappingNode { + YAMLMapping mapping; + int i; + + YAMLMappingMapNode() { this = TYAMLMappingNode(mapping, i) } + + override string toString() { result = "(Mapping " + i + ")" } + + /** + * Holds if this print node represents the `index`th mapping of `m`. + */ + predicate maps(YAMLMapping m, int index) { + m = mapping and + index = i + } + + override Location getLocation() { result = mapping.getKeyNode(i).getLocation() } + + override PrintAstNode getChild(int childIndex) { + childIndex = 0 and result.(YAMLNodeNode).getValue() = mapping.getKeyNode(i) + or + childIndex = 1 and result.(YAMLNodeNode).getValue() = mapping.getValueNode(i) + } + } +} + +/** + * Classes for printing HTML AST. + */ +module PrintHTML { + /** + * A print node representing an HTML node in a .html file. + */ + class HTMLElementNode extends PrintAstNode, THTMLElementNode { + HTML::Element element; + + HTMLElementNode() { this = THTMLElementNode(element) } + + override string toString() { result = getQlClass(element) + element.toString() } + + override Location getLocation() { result = element.getLocation() } + + /** + * Gets the `HTML::Element` represented by this node. + */ + final HTML::Element getElement() { result = element } + + override PrintAstNode getChild(int childIndex) { + childIndex = -1 and result.(HTMLAttributesNodes).getElement() = element + or + exists(HTML::Element child | result.(HTMLElementNode).getElement() = child | + child = element.(HTML::Element).getChild(childIndex) + ) + } + } + + /** + * A print node representing an HTML node in a .html file. + */ + class HTMLScriptElementNode extends HTMLElementNode { + override HTML::ScriptElement element; + + override PrintAstNode getChild(int childIndex) { + childIndex = -200 and result.(HTMLScript).getScript() = element.getScript() + or + result = super.getChild(childIndex) + } + } + + /** + * A print node representing the code inside a ` -{% endblock %} +{% endblock %} \ No newline at end of file From dfb687fd47ba8f0145633f790c396c7a7e97026e Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 14 Oct 2020 18:02:45 -0400 Subject: [PATCH 098/166] C++: Add ability to dump local dataflow info in IR dumps This change adds a new module, `PrintIRLocalFlow.qll`, which can be imported into any query that uses both `PrintIR.qll` and the IR dataflow library. The IR dump printed by `PrintIR.qll` will be annotated with information about how each operand and instruction participates in dataflow. For each operand and instruction, the following propeties are displayed: - `flow`: Which local operands/instructions have flow to this node, and which local operands/instruction this node has flow to. - `source`: `true` if this node is a source - `sink`: `true` if this node is a sink - `barrier`: Lists which kinds of barrier this node is. Can be zero or more of `full`, `in`, `out`, and `guard`. If the node is a guard barrier, the IR of the guarding instruction is also printed. We already had a way to print additional properties for instructions and blocks, but not for operands. I added support for operand properties to `IRPropertyProvider`. These are now printed in a curly-brace-enclosed list immediately after the corresponding operand. When printing flow, instructions are identified by their result ID (e.g., `m128`). Operands are identified by both the result ID of their instruction and their kind (e.g., `r145.left`). For flow from an operand to its use instruction, it just prints `result` at the operand, and prints only the operand kind on the instruction. Example output: ``` # 344| m344_34(vector>) = Chi : total:m344_20{flow:def->@, @->result}, partial:m344_33{flow:def->@, @->result} # 344| flow = total->@, partial->@, +m344_33->@, @->+r347_3, @->v347_7.side_effect, @->m347_9.total, @->m344_20.1 ``` The `+` annotations indicate when the flow came from `isAdditionalFlowStep()`, rather than built-in local flow. --- .../ir/dataflow/internal/PrintIRLocalFlow.qll | 152 ++++++++++++++++++ .../cpp/ir/implementation/aliased_ssa/IR.qll | 5 + .../ir/implementation/aliased_ssa/Operand.qll | 11 ++ .../ir/implementation/aliased_ssa/PrintIR.qll | 42 ++++- .../ir/implementation/internal/OperandTag.qll | 58 ++++++- .../code/cpp/ir/implementation/raw/IR.qll | 5 + .../cpp/ir/implementation/raw/Operand.qll | 11 ++ .../cpp/ir/implementation/raw/PrintIR.qll | 42 ++++- .../ir/implementation/unaliased_ssa/IR.qll | 5 + .../implementation/unaliased_ssa/Operand.qll | 11 ++ .../implementation/unaliased_ssa/PrintIR.qll | 42 ++++- .../ir/implementation/internal/OperandTag.qll | 58 ++++++- .../experimental/ir/implementation/raw/IR.qll | 5 + .../ir/implementation/raw/Operand.qll | 11 ++ .../ir/implementation/raw/PrintIR.qll | 42 ++++- .../ir/implementation/unaliased_ssa/IR.qll | 5 + .../implementation/unaliased_ssa/Operand.qll | 11 ++ .../implementation/unaliased_ssa/PrintIR.qll | 42 ++++- 18 files changed, 537 insertions(+), 21 deletions(-) create mode 100644 cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll new file mode 100644 index 00000000000..edbb11db2f3 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll @@ -0,0 +1,152 @@ +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.dataflow.DataFlow +private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil + +/** + * Gets a short ID for an IR dataflow node. + * - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`). + * - For `Operand`s, this is the label of the operand, prefixed with the result ID of the + * instruction and a dot (e.g. `m128.left`). + * - For `Variable`s, this is the qualified name of the variable. + */ +private string nodeId(DataFlow::Node node, int order1, int order2) { + exists(Instruction instruction | instruction = node.asInstruction() | + result = instruction.getResultId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + exists(Operand operand, Instruction instruction | + operand = node.asOperand() and + instruction = operand.getUse() + | + result = instruction.getResultId() + "." + operand.getDumpId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + result = "var(" + node.asVariable().getQualifiedName() + ")" and + order1 = 1000000 and + order2 = 0 +} + +/** + * Gets the local dataflow from other nodes in the same function to this node. + */ +private string getFromFlow(DataFlow::Node useNode, int order1, int order2) { + exists(DataFlow::Node defNode, string prefix | + ( + simpleLocalFlowStep(defNode, useNode) and prefix = "" + or + any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and + defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and + prefix = "+" + ) and + if defNode.asInstruction() = useNode.asOperand().getAnyDef() + then + // Shorthand for flow from the def of this operand. + result = prefix + "def" and + order1 = -1 and + order2 = 0 + else + if defNode.asOperand().getUse() = useNode.asInstruction() + then + // Shorthand for flow from an operand of this instruction + result = prefix + defNode.asOperand().getDumpId() and + order1 = -1 and + order2 = defNode.asOperand().getDumpSortOrder() + else result = prefix + nodeId(defNode, order1, order2) + ) +} + +/** + * Gets the local dataflow from this node to other nodes in the same function. + */ +private string getToFlow(DataFlow::Node defNode, int order1, int order2) { + exists(DataFlow::Node useNode, string prefix | + ( + simpleLocalFlowStep(defNode, useNode) and prefix = "" + or + any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and + defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and + prefix = "+" + ) and + if useNode.asInstruction() = defNode.asOperand().getUse() + then + // Shorthand for flow to this operand's instruction. + result = prefix + "result" and + order1 = -1 and + order2 = 0 + else result = prefix + nodeId(useNode, order1, order2) + ) +} + +/** + * Gets the properties of the dataflow node `node`. + */ +private string getNodeProperty(DataFlow::Node node, string key) { + // List dataflow into and out of this node. Flow into this node is printed as `src->@`, and flow + // out of this node is printed as `@->dest`. + key = "flow" and + result = + strictconcat(string flow, boolean to, int order1, int order2 | + flow = getFromFlow(node, order1, order2) + "->@" and to = false + or + flow = "@->" + getToFlow(node, order1, order2) and to = true + | + flow, ", " order by to, order1, order2, flow + ) + or + // Is this node a dataflow sink? + key = "sink" and + any(DataFlow::Configuration cfg).isSink(node) and + result = "true" + or + // Is this node a dataflow source? + key = "source" and + any(DataFlow::Configuration cfg).isSource(node) and + result = "true" + or + // Is this node a dataflow barrier, and if so, what kind? + key = "barrier" and + result = + strictconcat(string kind | + any(DataFlow::Configuration cfg).isBarrier(node) and kind = "full" + or + any(DataFlow::Configuration cfg).isBarrierIn(node) and kind = "in" + or + any(DataFlow::Configuration cfg).isBarrierOut(node) and kind = "out" + or + exists(DataFlow::BarrierGuard guard | + any(DataFlow::Configuration cfg).isBarrierGuard(guard) and + node = guard.getAGuardedNode() and + kind = "guard(" + guard.getResultId() + ")" + ) + | + kind, ", " + ) +} + +/** + * Property provider for local IR dataflow. + */ +class LocalFlowPropertyProvider extends IRPropertyProvider { + override string getOperandProperty(Operand operand, string key) { + exists(DataFlow::Node node | + operand = node.asOperand() and + result = getNodeProperty(node, key) + ) + } + + override string getInstructionProperty(Instruction instruction, string key) { + exists(DataFlow::Node node | + instruction = node.asInstruction() and + result = getNodeProperty(node, key) + ) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll index 3fa0f1b78be..c96783fe6e8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll @@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index e476aec60af..d8ae610b2f0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -151,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -280,6 +285,8 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } /** @@ -477,6 +484,10 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { + result = getPredecessorBlock().getDisplayIndex().toString() + } + /** * Gets the predecessor block from which this value comes. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll index b3e3a5b1195..b26cf972ce4 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll @@ -50,6 +50,32 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ") +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -190,7 +216,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -210,6 +236,20 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll index ac284440648..274b6510bd6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll @@ -40,7 +40,19 @@ abstract class OperandTag extends TOperandTag { /** * Gets a label that will appear before the operand when the IR is printed. */ - string getLabel() { result = "" } + final string getLabel() { + if alwaysPrintLabel() then result = getId() + ":" else result = "" + } + + /** + * Gets an identifier that uniquely identifies this operand within its instruction. + */ + abstract string getId(); + + /** + * Holds if the operand should always be prefixed with its label in the dump of its instruction. + */ + predicate alwaysPrintLabel() { none() } } /** @@ -69,7 +81,9 @@ class AddressOperandTag extends RegisterOperandTag, TAddressOperand { final override int getSortOrder() { result = 0 } - final override string getLabel() { result = "&:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "&" } } AddressOperandTag addressOperand() { result = TAddressOperand() } @@ -82,6 +96,8 @@ class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand { final override string toString() { result = "BufferSize" } final override int getSortOrder() { result = 1 } + + final override string getId() { result = "size" } } BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() } @@ -93,6 +109,8 @@ class SideEffectOperandTag extends TypedOperandTag, TSideEffectOperand { final override string toString() { result = "SideEffect" } final override int getSortOrder() { result = 2 } + + final override string getId() { result = "side_effect" } } SideEffectOperandTag sideEffectOperand() { result = TSideEffectOperand() } @@ -105,6 +123,8 @@ class LoadOperandTag extends TypedOperandTag, TLoadOperand { final override string toString() { result = "Load" } final override int getSortOrder() { result = 3 } + + final override string getId() { result = "load" } } LoadOperandTag loadOperand() { result = TLoadOperand() } @@ -116,6 +136,8 @@ class StoreValueOperandTag extends RegisterOperandTag, TStoreValueOperand { final override string toString() { result = "StoreValue" } final override int getSortOrder() { result = 4 } + + final override string getId() { result = "store" } } StoreValueOperandTag storeValueOperand() { result = TStoreValueOperand() } @@ -127,6 +149,8 @@ class UnaryOperandTag extends RegisterOperandTag, TUnaryOperand { final override string toString() { result = "Unary" } final override int getSortOrder() { result = 5 } + + final override string getId() { result = "unary" } } UnaryOperandTag unaryOperand() { result = TUnaryOperand() } @@ -138,6 +162,8 @@ class LeftOperandTag extends RegisterOperandTag, TLeftOperand { final override string toString() { result = "Left" } final override int getSortOrder() { result = 6 } + + final override string getId() { result = "left" } } LeftOperandTag leftOperand() { result = TLeftOperand() } @@ -149,6 +175,8 @@ class RightOperandTag extends RegisterOperandTag, TRightOperand { final override string toString() { result = "Right" } final override int getSortOrder() { result = 7 } + + final override string getId() { result = "right" } } RightOperandTag rightOperand() { result = TRightOperand() } @@ -160,6 +188,8 @@ class ConditionOperandTag extends RegisterOperandTag, TConditionOperand { final override string toString() { result = "Condition" } final override int getSortOrder() { result = 8 } + + final override string getId() { result = "cond" } } ConditionOperandTag conditionOperand() { result = TConditionOperand() } @@ -172,7 +202,9 @@ class CallTargetOperandTag extends RegisterOperandTag, TCallTargetOperand { final override int getSortOrder() { result = 10 } - final override string getLabel() { result = "func:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "func" } } CallTargetOperandTag callTargetOperand() { result = TCallTargetOperand() } @@ -195,7 +227,9 @@ class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand { final override int getSortOrder() { result = 11 } - final override string getLabel() { result = "this:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "this" } } ThisArgumentOperandTag thisArgumentOperand() { result = TThisArgumentOperand() } @@ -212,9 +246,11 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag, TPositionalArgume final override int getSortOrder() { result = 12 + argIndex } - final override string getLabel() { result = argIndex.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } final int getArgIndex() { result = argIndex } + + final override string getId() { result = argIndex.toString() } } PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) { @@ -228,7 +264,9 @@ class ChiTotalOperandTag extends ChiOperandTag, TChiTotalOperand { final override int getSortOrder() { result = 13 } - final override string getLabel() { result = "total:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "total" } } ChiTotalOperandTag chiTotalOperand() { result = TChiTotalOperand() } @@ -238,7 +276,9 @@ class ChiPartialOperandTag extends ChiOperandTag, TChiPartialOperand { final override int getSortOrder() { result = 14 } - final override string getLabel() { result = "partial:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "partial" } } ChiPartialOperandTag chiPartialOperand() { result = TChiPartialOperand() } @@ -252,7 +292,9 @@ class AsmOperandTag extends RegisterOperandTag, TAsmOperand { final override int getSortOrder() { result = 15 + index } - final override string getLabel() { result = index.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = index.toString() } } AsmOperandTag asmOperand(int index) { result = TAsmOperand(index) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll index 3fa0f1b78be..c96783fe6e8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll @@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index e476aec60af..d8ae610b2f0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -151,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -280,6 +285,8 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } /** @@ -477,6 +484,10 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { + result = getPredecessorBlock().getDisplayIndex().toString() + } + /** * Gets the predecessor block from which this value comes. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll index b3e3a5b1195..b26cf972ce4 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll @@ -50,6 +50,32 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ") +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -190,7 +216,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -210,6 +236,20 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll index 3fa0f1b78be..c96783fe6e8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll @@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index e476aec60af..d8ae610b2f0 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -151,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -280,6 +285,8 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } /** @@ -477,6 +484,10 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { + result = getPredecessorBlock().getDisplayIndex().toString() + } + /** * Gets the predecessor block from which this value comes. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll index b3e3a5b1195..b26cf972ce4 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll @@ -50,6 +50,32 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ") +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -190,7 +216,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -210,6 +236,20 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { diff --git a/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll b/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll index ac284440648..274b6510bd6 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll @@ -40,7 +40,19 @@ abstract class OperandTag extends TOperandTag { /** * Gets a label that will appear before the operand when the IR is printed. */ - string getLabel() { result = "" } + final string getLabel() { + if alwaysPrintLabel() then result = getId() + ":" else result = "" + } + + /** + * Gets an identifier that uniquely identifies this operand within its instruction. + */ + abstract string getId(); + + /** + * Holds if the operand should always be prefixed with its label in the dump of its instruction. + */ + predicate alwaysPrintLabel() { none() } } /** @@ -69,7 +81,9 @@ class AddressOperandTag extends RegisterOperandTag, TAddressOperand { final override int getSortOrder() { result = 0 } - final override string getLabel() { result = "&:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "&" } } AddressOperandTag addressOperand() { result = TAddressOperand() } @@ -82,6 +96,8 @@ class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand { final override string toString() { result = "BufferSize" } final override int getSortOrder() { result = 1 } + + final override string getId() { result = "size" } } BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() } @@ -93,6 +109,8 @@ class SideEffectOperandTag extends TypedOperandTag, TSideEffectOperand { final override string toString() { result = "SideEffect" } final override int getSortOrder() { result = 2 } + + final override string getId() { result = "side_effect" } } SideEffectOperandTag sideEffectOperand() { result = TSideEffectOperand() } @@ -105,6 +123,8 @@ class LoadOperandTag extends TypedOperandTag, TLoadOperand { final override string toString() { result = "Load" } final override int getSortOrder() { result = 3 } + + final override string getId() { result = "load" } } LoadOperandTag loadOperand() { result = TLoadOperand() } @@ -116,6 +136,8 @@ class StoreValueOperandTag extends RegisterOperandTag, TStoreValueOperand { final override string toString() { result = "StoreValue" } final override int getSortOrder() { result = 4 } + + final override string getId() { result = "store" } } StoreValueOperandTag storeValueOperand() { result = TStoreValueOperand() } @@ -127,6 +149,8 @@ class UnaryOperandTag extends RegisterOperandTag, TUnaryOperand { final override string toString() { result = "Unary" } final override int getSortOrder() { result = 5 } + + final override string getId() { result = "unary" } } UnaryOperandTag unaryOperand() { result = TUnaryOperand() } @@ -138,6 +162,8 @@ class LeftOperandTag extends RegisterOperandTag, TLeftOperand { final override string toString() { result = "Left" } final override int getSortOrder() { result = 6 } + + final override string getId() { result = "left" } } LeftOperandTag leftOperand() { result = TLeftOperand() } @@ -149,6 +175,8 @@ class RightOperandTag extends RegisterOperandTag, TRightOperand { final override string toString() { result = "Right" } final override int getSortOrder() { result = 7 } + + final override string getId() { result = "right" } } RightOperandTag rightOperand() { result = TRightOperand() } @@ -160,6 +188,8 @@ class ConditionOperandTag extends RegisterOperandTag, TConditionOperand { final override string toString() { result = "Condition" } final override int getSortOrder() { result = 8 } + + final override string getId() { result = "cond" } } ConditionOperandTag conditionOperand() { result = TConditionOperand() } @@ -172,7 +202,9 @@ class CallTargetOperandTag extends RegisterOperandTag, TCallTargetOperand { final override int getSortOrder() { result = 10 } - final override string getLabel() { result = "func:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "func" } } CallTargetOperandTag callTargetOperand() { result = TCallTargetOperand() } @@ -195,7 +227,9 @@ class ThisArgumentOperandTag extends ArgumentOperandTag, TThisArgumentOperand { final override int getSortOrder() { result = 11 } - final override string getLabel() { result = "this:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "this" } } ThisArgumentOperandTag thisArgumentOperand() { result = TThisArgumentOperand() } @@ -212,9 +246,11 @@ class PositionalArgumentOperandTag extends ArgumentOperandTag, TPositionalArgume final override int getSortOrder() { result = 12 + argIndex } - final override string getLabel() { result = argIndex.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } final int getArgIndex() { result = argIndex } + + final override string getId() { result = argIndex.toString() } } PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) { @@ -228,7 +264,9 @@ class ChiTotalOperandTag extends ChiOperandTag, TChiTotalOperand { final override int getSortOrder() { result = 13 } - final override string getLabel() { result = "total:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "total" } } ChiTotalOperandTag chiTotalOperand() { result = TChiTotalOperand() } @@ -238,7 +276,9 @@ class ChiPartialOperandTag extends ChiOperandTag, TChiPartialOperand { final override int getSortOrder() { result = 14 } - final override string getLabel() { result = "partial:" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = "partial" } } ChiPartialOperandTag chiPartialOperand() { result = TChiPartialOperand() } @@ -252,7 +292,9 @@ class AsmOperandTag extends RegisterOperandTag, TAsmOperand { final override int getSortOrder() { result = 15 + index } - final override string getLabel() { result = index.toString() + ":" } + final override predicate alwaysPrintLabel() { any() } + + final override string getId() { result = index.toString() } } AsmOperandTag asmOperand(int index) { result = TAsmOperand(index) } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/IR.qll b/csharp/ql/src/experimental/ir/implementation/raw/IR.qll index 3fa0f1b78be..c96783fe6e8 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/IR.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/IR.qll @@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll index e476aec60af..d8ae610b2f0 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll @@ -151,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -280,6 +285,8 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } /** @@ -477,6 +484,10 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { + result = getPredecessorBlock().getDisplayIndex().toString() + } + /** * Gets the predecessor block from which this value comes. */ diff --git a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll index b3e3a5b1195..b26cf972ce4 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll @@ -50,6 +50,32 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ") +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -190,7 +216,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -210,6 +236,20 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll index 3fa0f1b78be..c96783fe6e8 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll @@ -72,4 +72,9 @@ class IRPropertyProvider extends TIRPropertyProvider { * Gets the value of the property named `key` for the specified block. */ string getBlockProperty(IRBlock block, string key) { none() } + + /** + * Gets the value of the property named `key` for the specified operand. + */ + string getOperandProperty(Operand operand, string key) { none() } } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll index e476aec60af..d8ae610b2f0 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll @@ -151,6 +151,11 @@ class Operand extends TOperand { */ string getDumpLabel() { result = "" } + /** + * Gets a string that uniquely identifies this operand on its use instruction. + */ + string getDumpId() { result = "" } + /** * Gets a string describing this operand, suitable for display in IR dumps. This consists of the * result ID of the instruction consumed by the operand, plus a label identifying the operand @@ -280,6 +285,8 @@ class NonPhiOperand extends Operand { final override string getDumpLabel() { result = tag.getLabel() } + final override string getDumpId() { result = tag.getId() } + final override int getDumpSortOrder() { result = tag.getSortOrder() } /** @@ -477,6 +484,10 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } + final override string getDumpId() { + result = getPredecessorBlock().getDisplayIndex().toString() + } + /** * Gets the predecessor block from which this value comes. */ diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll index b3e3a5b1195..b26cf972ce4 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll @@ -50,6 +50,32 @@ private string getAdditionalBlockProperty(IRBlock block, string key) { exists(IRPropertyProvider provider | result = provider.getBlockProperty(block, key)) } +/** + * Gets the properties of an operand from any active property providers. + */ +private string getAdditionalOperandProperty(Operand operand, string key) { + exists(IRPropertyProvider provider | result = provider.getOperandProperty(operand, key)) +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. If the + * operand has no properties, this predicate has no result. + */ +private string getOperandPropertyListString(Operand operand) { + result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ") +} + +/** + * Gets a string listing the properties of the operand and their corresponding values. The list is + * surrounded by curly braces. If the operand has no properties, this predicate returns an empty + * string. + */ +private string getOperandPropertyString(Operand operand) { + result = "{" + getOperandPropertyListString(operand) + "}" + or + not exists(getOperandPropertyListString(operand)) and result = "" +} + private newtype TPrintableIRNode = TPrintableIRFunction(IRFunction irFunc) { shouldPrintFunction(irFunc.getFunction()) } or TPrintableIRBlock(IRBlock block) { shouldPrintFunction(block.getEnclosingFunction()) } or @@ -190,7 +216,7 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio | resultString = instr.getResultString() and operationString = instr.getOperationString() and - operandsString = instr.getOperandsString() and + operandsString = getOperandsString() and columnWidths(block, resultWidth, operationWidth) and result = resultString + getPaddingString(resultWidth - resultString.length()) + " = " + @@ -210,6 +236,20 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio result = PrintableIRNode.super.getProperty(key) or result = getAdditionalInstructionProperty(instr, key) } + + /** + * Gets the string representation of the operand list. This is the same as + * `Instruction::getOperandsString()`, except that each operand is annotated with any properties + * provided by active `IRPropertyProvider` instances. + */ + private string getOperandsString() { + result = + concat(Operand operand | + operand = instr.getAnOperand() + | + operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder() + ) + } } private predicate columnWidths(IRBlock block, int resultWidth, int operationWidth) { From 74243d39aa0dbd1add37d88d522df1dd2337eb19 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 15 Oct 2020 09:48:55 +0200 Subject: [PATCH 099/166] remove location for arguments/parameters print node --- javascript/ql/src/semmle/javascript/PrintAst.qll | 8 -------- 1 file changed, 8 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 26b9faa9d67..6ad7afb8683 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -245,8 +245,6 @@ private module PrintJavaScript { override PrintAstNode getChild(int childIndex) { result.(ElementNode).getElement() = invk.getArgument(childIndex) } - - override Location getLocation() { result = invk.getLocation() } } /** @@ -269,8 +267,6 @@ private module PrintJavaScript { override PrintAstNode getChild(int childIndex) { result.(ElementNode).getElement() = invk.getTypeArgument(childIndex) } - - override Location getLocation() { result = invk.getLocation() } } /** @@ -424,8 +420,6 @@ private module PrintJavaScript { override PrintAstNode getChild(int childIndex) { result.(ElementNode).getElement() = f.getParameter(childIndex) } - - override Location getLocation() { result = f.getLocation() } } /** @@ -446,8 +440,6 @@ private module PrintJavaScript { override PrintAstNode getChild(int childIndex) { result.(ElementNode).getElement() = f.getTypeParameter(childIndex) } - - override Location getLocation() { result = f.getLocation() } } } From ab10c28cc4d7db9a653468a8fa02a2d354cc8bc4 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 15 Oct 2020 09:53:52 +0200 Subject: [PATCH 100/166] change the default sorting order for print children to be location based --- javascript/ql/src/semmle/javascript/PrintAst.qll | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 6ad7afb8683..886463c2854 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -200,7 +200,18 @@ private module PrintJavaScript { * Gets the `i`th child of `element`. * Can be overriden in subclasses to get more specific behavior for `getChild()`. */ - ASTNode getChildNode(int childIndex) { result = element.getChild(childIndex) } + ASTNode getChildNode(int childIndex) { result = getLocationSortedChild(element, childIndex) } + } + + private ASTNode getLocationSortedChild(ASTNode parent, int i) { + result = + rank[i](ASTNode child, int childIndex | + child = parent.getChild(childIndex) + | + child + order by + child.getLocation().getStartLine(), child.getLocation().getStartColumn(), childIndex + ) } /** From c033ae9b7f4d0771bc9876e591d554adfd52f813 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 15 Oct 2020 10:05:07 +0200 Subject: [PATCH 101/166] add one more case to `getAPrimaryQlClass` --- javascript/ql/src/semmle/javascript/TypeScript.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/TypeScript.qll b/javascript/ql/src/semmle/javascript/TypeScript.qll index 2020c2a8608..9792baeaf11 100644 --- a/javascript/ql/src/semmle/javascript/TypeScript.qll +++ b/javascript/ql/src/semmle/javascript/TypeScript.qll @@ -230,6 +230,8 @@ class ExternalModuleReference extends Expr, Import, @external_module_reference { } override DataFlow::Node getImportedModuleNode() { result = DataFlow::valueNode(this) } + + override string getAPrimaryQlClass() { result = "ExternalModuleReference" } } /** A literal path expression appearing in an external module reference. */ From 872801732869f2618712f4cf19856287c9cd2ad0 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 15 Oct 2020 10:40:19 +0200 Subject: [PATCH 102/166] C#: Increase `fieldFlowBranchLimit` in test 68014fd3bf662453f1cd9a44a8b05008e79474e2 means that more accessors are properly extracted, and consequently the calls to `get_Item` in the test have more dispatch targets. Increasing `fieldFlowBranchLimit` makes the test pass again. --- .../test/library-tests/dataflow/collections/CollectionFlow.ql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql index 7c2c322f47f..7d02ffa7bcb 100644 --- a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql +++ b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.ql @@ -16,6 +16,8 @@ class Conf extends DataFlow::Configuration { mc.getAnArgument() = sink.asExpr() ) } + + override int fieldFlowBranchLimit() { result = 10 } } from DataFlow::PathNode source, DataFlow::PathNode sink, Conf conf From 1b908ce0304c6b6cc2f593c945c9b56628687ca0 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 15 Oct 2020 10:43:32 +0200 Subject: [PATCH 103/166] improve printing of DeclStmt, and remove escaped whitespace chars from printed output --- .../ql/src/semmle/javascript/PrintAst.qll | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 886463c2854..19767bb87cc 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -181,7 +181,7 @@ private module PrintJavaScript { not element instanceof CodeInAttribute // Handled in module `PrintHTML` } - override string toString() { result = getQlClass(element) + element.toString() } + override string toString() { result = getQlClass(element) + PrettyPrinting::print(element) } override Location getLocation() { result = element.getLocation() } @@ -203,6 +203,46 @@ private module PrintJavaScript { ASTNode getChildNode(int childIndex) { result = getLocationSortedChild(element, childIndex) } } + /** Provides predicates for pretty printing `ASTNode`s. */ + private module PrettyPrinting { + /** + * Gets a pretty string representation of `element`. + * Either the result is `ASTNode::toString`, or a custom made string representation of `element`. + */ + string print(ASTNode element) { + result = element.toString().regexpReplaceAll("(\\\\n|\\\\r|\\\\t| )+", " ") and + not exists(repr(element)) + or + result = repr(element) + } + + /** + * Gets a string representing `a`. + */ + private string repr(ASTNode a) { + exists(DeclStmt decl | decl = a | + result = + getDeclarationKeyword(decl) + " " + + strictconcat(string name, int i | + name = decl.getDecl(i).getBindingPattern().getName() + | + name, ", " order by i + ) + " = ..." + ) + } + + /** + * Gets "var" or "const" or "let" depending on what type of declaration `decl` is. + */ + private string getDeclarationKeyword(DeclStmt decl) { + decl instanceof VarDeclStmt and result = "var" + or + decl instanceof ConstDeclStmt and result = "const" + or + decl instanceof LetStmt and result = "let" + } + } + private ASTNode getLocationSortedChild(ASTNode parent, int i) { result = rank[i](ASTNode child, int childIndex | From 3e2d266343688416cf2e173865b9051bd82c7287 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 15 Oct 2020 10:49:37 +0200 Subject: [PATCH 104/166] improve YAMLMapping printing --- javascript/ql/src/semmle/javascript/PrintAst.qll | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 19767bb87cc..10dc9192194 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -573,7 +573,11 @@ module PrintYAML { YAMLMappingMapNode() { this = TYAMLMappingNode(mapping, i) } - override string toString() { result = "(Mapping " + i + ")" } + override string toString() { + result = "(Mapping " + i + ")" and not exists(mapping.getKeyNode(i).(YAMLScalar).getValue()) + or + result = "(Mapping " + i + ") " + mapping.getKeyNode(i).(YAMLScalar).getValue() + ":" + } /** * Holds if this print node represents the `index`th mapping of `m`. From 1ebd49b0eb7e1b4023d433f97155c1ba6b149f6e Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 15 Oct 2020 10:51:34 +0200 Subject: [PATCH 105/166] remove location from "mapping i" print node --- javascript/ql/src/semmle/javascript/PrintAst.qll | 2 -- 1 file changed, 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 10dc9192194..8170c145e03 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -587,8 +587,6 @@ module PrintYAML { index = i } - override Location getLocation() { result = mapping.getKeyNode(i).getLocation() } - override PrintAstNode getChild(int childIndex) { childIndex = 0 and result.(YAMLNodeNode).getValue() = mapping.getKeyNode(i) or From ab7542c0d29974ffc56a26dfbf03e2d4d45e560d Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 15 Oct 2020 11:05:22 +0200 Subject: [PATCH 106/166] improve printing of JSON values --- .../ql/src/semmle/javascript/PrintAst.qll | 55 +++++++++++++++++-- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 8170c145e03..adf19edd89f 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -210,16 +210,20 @@ private module PrintJavaScript { * Either the result is `ASTNode::toString`, or a custom made string representation of `element`. */ string print(ASTNode element) { - result = element.toString().regexpReplaceAll("(\\\\n|\\\\r|\\\\t| )+", " ") and - not exists(repr(element)) - or - result = repr(element) + shouldPrint(element, _) and + ( + result = element.toString().regexpReplaceAll("(\\\\n|\\\\r|\\\\t| )+", " ") and + not exists(repr(element)) + or + result = repr(element) + ) } /** * Gets a string representing `a`. */ private string repr(ASTNode a) { + shouldPrint(a, _) and exists(DeclStmt decl | decl = a | result = getDeclarationKeyword(decl) + " " + @@ -506,7 +510,7 @@ private module PrintJSON { JSONNode() { this = TJSONNode(value) } - override string toString() { result = getQlClass(value) + value.toString() } + override string toString() { result = getQlClass(value) + PrettyPrinting::print(value) } override Location getLocation() { result = value.getLocation() } @@ -521,6 +525,47 @@ private module PrintJSON { ) } } + + /** Provied predicates for pretty printing JSON. */ + private module PrettyPrinting { + /** + * Gets a string representation of `n`. + * Either using the default `JSONValue::toString`, or a custom printing of the JSON value. + */ + string print(JSONValue n) { + shouldPrint(n, _) and + ( + result = n.toString().regexpReplaceAll("(\\\\n|\\\\r|\\\\t| )+", " ") and + not exists(repr(n)) + or + result = repr(n) + ) + } + + /** Gets a string representing `n`. */ + private string repr(JSONValue n) { + shouldPrint(n, _) and + ( + exists(JSONObject obj, string name, JSONValue prop | obj = n | + prop = obj.getPropValue(name) and + prop = obj.getChild(0) and + result = "{" + name + ": ...}" + ) + or + n instanceof JSONObject and not exists(n.getChild(_)) and result = "{}" + or + result = n.(JSONPrimitiveValue).getRawValue() + or + exists(JSONArray arr | arr = n | + result = "[]" and not exists(arr.getChild(_)) + or + result = "[" + repr(arr.getChild(0)) + "]" and not exists(arr.getChild(1)) + or + result = "[" + repr(arr.getChild(0)) + ", ...]" and exists(arr.getChild(1)) + ) + ) + } + } } /** From 9c8e968cba7998af6955c3ea3ba3bfd685948a37 Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 15 Oct 2020 11:47:34 +0200 Subject: [PATCH 107/166] Python: Fix bad merge --- .../src/experimental/dataflow/internal/DataFlowPrivate.qll | 7 +++++++ .../dataflow/coverage/argumentRouting3.expected | 1 + 2 files changed, 8 insertions(+) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll index 152e7801338..3a6bcb32113 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll @@ -155,6 +155,13 @@ module EssaFlow { or // If expressions nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand() + or + // Overflow keyword argument + exists(CallNode call, CallableValue callable | + call = callable.getACall() and + nodeTo = TKwOverflowNode(call, callable) and + nodeFrom.asCfgNode() = call.getNode().getKwargs().getAFlowNode() + ) } predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) { diff --git a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected index c3f1e6d7394..68686aabd4e 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected +++ b/python/ql/test/experimental/dataflow/coverage/argumentRouting3.expected @@ -4,4 +4,5 @@ | argumentPassing.py:119:41:119:44 | ControlFlowNode for arg3 | argumentPassing.py:112:11:112:11 | ControlFlowNode for c | | argumentPassing.py:120:50:120:53 | ControlFlowNode for arg3 | argumentPassing.py:112:11:112:11 | ControlFlowNode for c | | argumentPassing.py:134:36:134:39 | ControlFlowNode for arg3 | argumentPassing.py:126:11:126:11 | ControlFlowNode for c | +| argumentPassing.py:160:26:160:29 | ControlFlowNode for arg3 | argumentPassing.py:155:11:155:13 | ControlFlowNode for baz | | classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:571:15:571:19 | ControlFlowNode for value | From a0193129535a4d2b99df8dc62ef02bb88a5b78a8 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 15 Oct 2020 11:47:45 +0200 Subject: [PATCH 108/166] improve printing of JS object literals --- .../ql/src/semmle/javascript/PrintAst.qll | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index adf19edd89f..8991075035d 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -224,14 +224,24 @@ private module PrintJavaScript { */ private string repr(ASTNode a) { shouldPrint(a, _) and - exists(DeclStmt decl | decl = a | - result = - getDeclarationKeyword(decl) + " " + - strictconcat(string name, int i | - name = decl.getDecl(i).getBindingPattern().getName() - | - name, ", " order by i - ) + " = ..." + ( + exists(DeclStmt decl | decl = a | + result = + getDeclarationKeyword(decl) + " " + + strictconcat(string name, int i | + name = decl.getDecl(i).getBindingPattern().getName() + | + name, ", " order by i + ) + " = ..." + ) + or + exists(ObjectExpr obj | obj = a | result = "{" + obj.getProperty(0).getName() + ": ...}") + or + result = a.(Property).getName() + ": " + repr(a.(Property).getInit()) + or + result = a.(Literal).getRawValue() + or + result = a.(Identifier).getName() ) } From 7848c5f54dcea11d1ec31cdbb9c3f8ddf1de6e8f Mon Sep 17 00:00:00 2001 From: Remco Vermeulen Date: Thu, 15 Oct 2020 11:49:18 +0200 Subject: [PATCH 109/166] Fix qldoc for getIncludeText The '<' was HTML encoded for some reason. --- cpp/ql/src/semmle/code/cpp/Include.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/semmle/code/cpp/Include.qll b/cpp/ql/src/semmle/code/cpp/Include.qll index 11702ce1bf6..f21edb2651d 100644 --- a/cpp/ql/src/semmle/code/cpp/Include.qll +++ b/cpp/ql/src/semmle/code/cpp/Include.qll @@ -21,7 +21,7 @@ class Include extends PreprocessorDirective, @ppd_include { /** * Gets the token which occurs after `#include`, for example `"filename"` - * or `<filename>`. + * or ``. */ string getIncludeText() { result = getHead() } From 2bb8b78a295c2cad9a1d7fc40cc3875ab95fbee3 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 15 Oct 2020 11:56:00 +0200 Subject: [PATCH 110/166] remove "" from the end when printing HTML --- javascript/ql/src/semmle/javascript/PrintAst.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 8991075035d..c5bb333842a 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -662,7 +662,7 @@ module PrintHTML { HTMLElementNode() { this = THTMLElementNode(element) } - override string toString() { result = getQlClass(element) + element.toString() } + override string toString() { result = getQlClass(element) + "<" + element.getName() + " ..." } override Location getLocation() { result = element.getLocation() } From f9f29f53cfc5862237876a05ac5bbd94c5d4394c Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Thu, 15 Oct 2020 11:59:51 +0200 Subject: [PATCH 111/166] remove locations where we have no exact location --- javascript/ql/src/semmle/javascript/PrintAst.qll | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index c5bb333842a..7f451cd7341 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -376,8 +376,6 @@ private module PrintJavaScript { override PrintAstNode getChild(int childIndex) { result.(ElementNode).getElement() = n.getAttribute(childIndex) } - - override Location getLocation() { result = n.getLocation() } } /** @@ -398,8 +396,6 @@ private module PrintJavaScript { override PrintAstNode getChild(int childIndex) { result.(ElementNode).getElement() = n.getBodyElement(childIndex) } - - override Location getLocation() { result = n.getLocation() } } /** @@ -719,7 +715,7 @@ module PrintHTML { } /** - * A print node representing the code inside a ` -{% endblock %} \ No newline at end of file +{% endblock %} From bd0c577ffdb9bbe21454db50fef3814d4838eee8 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Fri, 12 Jun 2020 17:22:39 +0000 Subject: [PATCH 119/166] Unsafe resource loading in Android webview --- .../CWE/CWE-749/UnsafeAndroidAccess.java | 66 + .../CWE/CWE-749/UnsafeAndroidAccess.qhelp | 31 + .../CWE/CWE-749/UnsafeAndroidAccess.ql | 139 ++ .../CWE-749/UnsafeAndroidAccess.expected | 1 + .../security/CWE-749/UnsafeAndroidAccess.java | 31 + .../CWE-749/UnsafeAndroidAccess.qlref | 1 + .../query-tests/security/CWE-749/options | 1 + .../android/app/Activity.java | 1144 ++++++++++ .../android/content/Context.java | 816 +++++++ .../android/content/Intent.java | 1999 +++++++++++++++++ .../google-android-9.0.0/android/net/Uri.java | 544 +++++ .../android/os/BaseBundle.java | 677 ++++++ .../android/os/Bundle.java | 499 ++++ .../android/os/Parcel.java | 669 ++++++ .../android/os/Parcelable.java | 86 + .../android/view/View.java | 682 ++++++ .../android/webkit/WebResourceRequest.java | 72 + .../android/webkit/WebResourceResponse.java | 174 ++ .../android/webkit/WebSettings.java | 1382 ++++++++++++ .../android/webkit/WebView.java | 602 +++++ .../android/webkit/WebViewClient.java | 234 ++ 21 files changed, 9850 insertions(+) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-749/options create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/content/Context.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/net/Uri.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/view/View.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java new file mode 100644 index 00000000000..1ba5e94590a --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java @@ -0,0 +1,66 @@ +public class UnsafeAndroidAccess extends Activity { + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.webview); + + // BAD: Have both JavaScript and universal resource access enabled in webview while + // taking remote user inputs + { + WebView wv = (WebView) findViewById(R.id.my_webview); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowUniversalAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getExtras().getString("url"); // dangerous remote input + //String thisUrl = getIntent().getStringExtra("url"); //An alternative way to load dangerous remote input + wv.loadUrl(thisUrl); + } + + // GOOD: Have JavaScript and universal resource access disabled while taking + // remote user inputs + { + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(false); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getExtras().getString("url"); // remote input + wv.loadUrl(thisUrl); + } + + // GOOD: Have JavaScript enabled in webview but remote user input is not allowed + { + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + wv.loadUrl("https://www.mycorp.com"); + } + } +} \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp new file mode 100644 index 00000000000..fcae00945a2 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp @@ -0,0 +1,31 @@ + + + + +

    Android WebViews that allow loading urls controlled by external inputs and whose JavaScript interface is enabled are potentially vulnerable to cross-site scripting and sensitive resource disclosure attacks.

    +

    WebSettings of WebViews with setAllowFileAccessFromFileURLs or setAllowUniversalAccessFromFileURLs enabled must not load any untrusted web content.

    +

    Enabling this setting allows malicious scripts loaded in a file:// context to launch cross-site scripting attacks, either accessing arbitrary local files including WebView cookies, session tokens, private app data or even credentials used on arbitrary web sites.

    +

    This query detects the following two scenarios:

    +
      +
    1. Vulnerability introduced by WebViews with JavaScript enabled and remote inputs allowed.
    2. +
    3. High precision vulnerability when allowing universal resource access is also enabled. The setting was just deprecated in API level 30 (Android 11) thus most devices are still affected given that Android phones don't get timely version updates like iPhones.
    4. +
    +
    + + +

    Only allow trusted web contents to be displayed in WebViews when JavaScript is enabled. And disallow universal resource access in WebSetting to reduce the attack surface .

    +
    + + +

    The following example shows both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, setting is enabled and JavaScript is enabled while urls are loaded from externally controlled inputs. In the 'GOOD' configuration, JavaScript is disabled or only trusted web contents are allowed to be loaded.

    + +
    + + +
  • + CWE-749 + Fixing a File-based XSS Vulnerability + OWASP - Testing WebView Protocol Handlers (MSTG-PLATFORM-5 and MSTG-PLATFORM-6) +
  • +
    +
    \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql new file mode 100644 index 00000000000..1f40975d9af --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql @@ -0,0 +1,139 @@ +/** + * @name Unsafe resource loading in Android webview + * @description JavaScript rendered inside WebViews can access any protected application file and web resource from any origin + * @kind path-problem + * @tags security + * external/cwe/cwe-749 + * external/cwe/cwe-079 + */ + +import java +import semmle.code.java.frameworks.android.Intent +import semmle.code.java.frameworks.android.WebView +import semmle.code.java.dataflow.FlowSources + +/** + * Allow universal access methods in the WebSettings class + */ +class AllowUniversalAccessMethod extends Method { + AllowUniversalAccessMethod() { + this.getDeclaringType() instanceof TypeWebSettings and + ( + this.hasName("setAllowUniversalAccessFromFileURLs") or + this.hasName("setAllowFileAccessFromFileURLs") + ) + } +} + +/** + * `setJavaScriptEnabled` method for the webview + */ +class AllowJavaScriptMethod extends Method { + AllowJavaScriptMethod() { + this.getDeclaringType() instanceof TypeWebSettings and + this.hasName("setJavaScriptEnabled") + } +} + +/** + * Check whether JavaScript is enabled in the webview with universal resource access + */ +predicate isJSEnabled(MethodAccess ma) { + exists(VarAccess va, MethodAccess jsa | + ma.getQualifier() = va and + jsa.getQualifier() = va.getVariable().getAnAccess() and + jsa.getMethod() instanceof AllowJavaScriptMethod and + jsa.getArgument(0).(BooleanLiteral).getBooleanValue() = true + ) +} + +/** + * Load URL method call on the `android.webkit.WebView` object + */ +class LoadResourceMethodAccess extends MethodAccess { + LoadResourceMethodAccess() { + this.getMethod().getDeclaringType() instanceof TypeWebView and + ( + this.getMethod().hasName("loadUrl") or + this.getMethod().hasName("postUrl") + ) + } +} + +/** + * Method access to external inputs of `android.content.Intent` object + */ +class IntentGetExtraMethodAccess extends MethodAccess { + IntentGetExtraMethodAccess() { + this.getMethod().getName().regexpMatch("get\\w+Extra") and + this.getMethod().getDeclaringType() instanceof TypeIntent + or + this.getMethod().getName().regexpMatch("get\\w+") and + this.getQualifier().(MethodAccess).getMethod().hasName("getExtras") and + this.getQualifier().(MethodAccess).getMethod().getDeclaringType() instanceof TypeIntent + } +} + +/** + * Source of loading urls + */ +class UntrustedResourceSource extends RemoteFlowSource { + UntrustedResourceSource() { + exists(MethodAccess ma | + ma instanceof IntentGetExtraMethodAccess and + this.asExpr().(VarAccess).getVariable().getAnAssignedValue() = ma + ) + } + + override string getSourceType() { + result = "user input vulnerable to XSS and sensitive resource disclosure attacks" and + exists(MethodAccess ma | + ma.getMethod() instanceof AllowUniversalAccessMethod and //High precision match of unsafe resource loading + ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true + ) + or + result = "user input potentially vulnerable to XSS and sensitive resource disclosure attacks" and + not exists(MethodAccess ma | + ma.getMethod() instanceof AllowUniversalAccessMethod and //High precision match of unsafe resource loading + ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true + ) + } +} + +/** + * load externally controlled data from loadUntrustedResource + */ +predicate loadUntrustedResource(MethodAccess ma, Expr sink) { + ma instanceof LoadResourceMethodAccess and + sink = ma.getArgument(0) +} + +/** + * Sink of loading urls + */ +class UntrustedResourceSink extends DataFlow::ExprNode { + UntrustedResourceSink() { loadUntrustedResource(_, this.getExpr()) } + + MethodAccess getMethodAccess() { loadUntrustedResource(result, this.getExpr()) } +} + +class LoadUntrustedResourceConfiguration extends TaintTracking::Configuration { + LoadUntrustedResourceConfiguration() { this = "LoadUntrustedResourceConfiguration" } + + override predicate isSource(DataFlow::Node source) { source instanceof UntrustedResourceSource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof UntrustedResourceSink } +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, LoadUntrustedResourceConfiguration conf +where + exists(VarAccess webviewVa, MethodAccess getSettingsMa, MethodAccess ma | + conf.hasFlowPath(source, sink) and + sink.getNode().(UntrustedResourceSink).getMethodAccess().getQualifier() = webviewVa and + webviewVa.getVariable().getAnAccess() = getSettingsMa.getQualifier() and + ma.getQualifier().(VarAccess).getVariable().getAnAssignedValue() = getSettingsMa and + isJSEnabled(ma) + ) +select sink.getNode().(UntrustedResourceSink).getMethodAccess(), source, sink, + "Unsafe resource loading in Android webview due to $@.", source.getNode(), + source.getNode().(UntrustedResourceSource).getSourceType() diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected new file mode 100644 index 00000000000..f1795e649cd --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected @@ -0,0 +1 @@ +| UnsafeAndroidAccess.java:29:3:29:21 | loadUrl(...) | UnsafeAndroidAccess.java:29:14:29:20 | thisUrl | UnsafeAndroidAccess.java:29:14:29:20 | thisUrl | Unsafe resource loading in Android webview due to $@. | UnsafeAndroidAccess.java:29:14:29:20 | thisUrl | user input vulnerable to XSS and sensitive resource disclosure attacks | diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java new file mode 100644 index 00000000000..01c28912729 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java @@ -0,0 +1,31 @@ +import android.app.Activity; + +import android.os.Bundle; + +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +public class UnsafeAndroidAccess extends Activity { + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowFileAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getExtras().getString("url"); + wv.loadUrl(thisUrl); + } +} \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.qlref b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.qlref new file mode 100644 index 00000000000..3f41abfe566 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/options b/java/ql/test/experimental/query-tests/security/CWE-749/options new file mode 100644 index 00000000000..43e25f608b6 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-749/options @@ -0,0 +1 @@ +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0 diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java new file mode 100644 index 00000000000..19a2662b96c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Activity.java @@ -0,0 +1,1144 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app; + +import android.content.Intent; +import android.os.Bundle; +import android.view.View; + +/** + * An activity is a single, focused thing that the user can do. Almost all + * activities interact with the user, so the Activity class takes care of + * creating a window for you in which you can place your UI with + * {@link #setContentView}. While activities are often presented to the user as + * full-screen windows, they can also be used in other ways: as floating windows + * (via a theme with {@link android.R.attr#windowIsFloating} set) or embedded + * inside of another activity (using {@link ActivityGroup}). + * + * There are two methods almost all subclasses of Activity will implement: + * + *
      + *
    • {@link #onCreate} is where you initialize your activity. Most + * importantly, here you will usually call {@link #setContentView(int)} with a + * layout resource defining your UI, and using {@link #findViewById} to retrieve + * the widgets in that UI that you need to interact with programmatically. + * + *
    • {@link #onPause} is where you deal with the user leaving your activity. + * Most importantly, any changes made by the user should at this point be + * committed (usually to the {@link android.content.ContentProvider} holding the + * data). + *
    + * + *

    + * To be of use with {@link android.content.Context#startActivity + * Context.startActivity()}, all activity classes must have a corresponding + * {@link android.R.styleable#AndroidManifestActivity <activity>} + * declaration in their package's AndroidManifest.xml. + *

    + * + *

    + * Topics covered here: + *

      + *
    1. Fragments + *
    2. Activity Lifecycle + *
    3. Configuration Changes + *
    4. Starting Activities and Getting Results + *
    5. Saving Persistent State + *
    6. Permissions + *
    7. Process Lifecycle + *
    + * + *
    + *

    Developer Guides

    + *

    + * The Activity class is an important part of an application's overall + * lifecycle, and the way activities are launched and put together is a + * fundamental part of the platform's application model. For a detailed + * perspective on the structure of an Android application and how activities + * behave, please read the + * Application + * Fundamentals and + * Tasks and Back + * Stack developer guides. + *

    + * + *

    + * You can also find a detailed discussion about how to create activities in the + * Activities developer + * guide. + *

    + *
    + * + * + *

    Fragments

    + * + *

    + * The {@link android.support.v4.app.FragmentActivity} subclass can make use of + * the {@link android.support.v4.app.Fragment} class to better modularize their + * code, build more sophisticated user interfaces for larger screens, and help + * scale their application between small and large screens. + *

    + * + *

    + * For more information about using fragments, read the + * Fragments developer + * guide. + *

    + * + * + *

    Activity Lifecycle

    + * + *

    + * Activities in the system are managed as an activity stack. When a + * new activity is started, it is placed on the top of the stack and becomes the + * running activity -- the previous activity always remains below it in the + * stack, and will not come to the foreground again until the new activity + * exits. + *

    + * + *

    + * An activity has essentially four states: + *

    + *
      + *
    • If an activity is in the foreground of the screen (at the top of the + * stack), it is active or running.
    • + *
    • If an activity has lost focus but is still visible (that is, a new + * non-full-sized or transparent activity has focus on top of your activity), it + * is paused. A paused activity is completely alive (it maintains all + * state and member information and remains attached to the window manager), but + * can be killed by the system in extreme low memory situations. + *
    • If an activity is completely obscured by another activity, it is + * stopped. It still retains all state and member information, however, + * it is no longer visible to the user so its window is hidden and it will often + * be killed by the system when memory is needed elsewhere.
    • + *
    • If an activity is paused or stopped, the system can drop the activity + * from memory by either asking it to finish, or simply killing its process. + * When it is displayed again to the user, it must be completely restarted and + * restored to its previous state.
    • + *
    + * + *

    + * The following diagram shows the important state paths of an Activity. The + * square rectangles represent callback methods you can implement to perform + * operations when the Activity moves between states. The colored ovals are + * major states the Activity can be in. + *

    + * + *

    + * State diagram for an
+ * Android Activity Lifecycle. + *

    + * + *

    + * There are three key loops you may be interested in monitoring within your + * activity: + * + *

      + *
    • The entire lifetime of an activity happens between the first call + * to {@link android.app.Activity#onCreate} through to a single final call to + * {@link android.app.Activity#onDestroy}. An activity will do all setup of + * "global" state in onCreate(), and release all remaining resources in + * onDestroy(). For example, if it has a thread running in the background to + * download data from the network, it may create that thread in onCreate() and + * then stop the thread in onDestroy(). + * + *
    • The visible lifetime of an activity happens between a call to + * {@link android.app.Activity#onStart} until a corresponding call to + * {@link android.app.Activity#onStop}. During this time the user can see the + * activity on-screen, though it may not be in the foreground and interacting + * with the user. Between these two methods you can maintain resources that are + * needed to show the activity to the user. For example, you can register a + * {@link android.content.BroadcastReceiver} in onStart() to monitor for changes + * that impact your UI, and unregister it in onStop() when the user no longer + * sees what you are displaying. The onStart() and onStop() methods can be + * called multiple times, as the activity becomes visible and hidden to the + * user. + * + *
    • The foreground lifetime of an activity happens between a call to + * {@link android.app.Activity#onResume} until a corresponding call to + * {@link android.app.Activity#onPause}. During this time the activity is in + * front of all other activities and interacting with the user. An activity can + * frequently go between the resumed and paused states -- for example when the + * device goes to sleep, when an activity result is delivered, when a new intent + * is delivered -- so the code in these methods should be fairly lightweight. + *
    + * + *

    + * The entire lifecycle of an activity is defined by the following Activity + * methods. All of these are hooks that you can override to do appropriate work + * when the activity changes state. All activities will implement + * {@link android.app.Activity#onCreate} to do their initial setup; many will + * also implement {@link android.app.Activity#onPause} to commit changes to data + * and otherwise prepare to stop interacting with the user. You should always + * call up to your superclass when implementing these methods. + *

    + * + *

    + * + *
    + * public class Activity extends ApplicationContext {
    + *     protected void onCreate(Bundle savedInstanceState);
    + *
    + *     protected void onStart();
    + *
    + *     protected void onRestart();
    + *
    + *     protected void onResume();
    + *
    + *     protected void onPause();
    + *
    + *     protected void onStop();
    + *
    + *     protected void onDestroy();
    + * }
    + * 
    + * + *

    + * In general the movement through an activity's lifecycle looks like this: + *

    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    MethodDescriptionKillable?Next
    {@link android.app.Activity#onCreate + * onCreate()}Called when the activity is first created. This is where you should do + * all of your normal static set up: create views, bind data to lists, etc. This + * method also provides you with a Bundle containing the activity's previously + * frozen state, if there was one. + *

    + * Always followed by onStart().

    NoonStart()
        {@link android.app.Activity#onRestart + * onRestart()}Called after your activity has been stopped, prior to it being started + * again. + *

    + * Always followed by onStart()

    NoonStart()
    {@link android.app.Activity#onStart + * onStart()}Called when the activity is becoming visible to the user. + *

    + * Followed by onResume() if the activity comes to the foreground, + * or onStop() if it becomes hidden.

    NoonResume() or onStop()
        {@link android.app.Activity#onResume + * onResume()}Called when the activity will start interacting with the user. At this + * point your activity is at the top of the activity stack, with user input + * going to it. + *

    + * Always followed by onPause().

    NoonPause()
    {@link android.app.Activity#onPause + * onPause()}Called when the system is about to start resuming a previous activity. + * This is typically used to commit unsaved changes to persistent data, stop + * animations and other things that may be consuming CPU, etc. Implementations + * of this method must be very quick because the next activity will not be + * resumed until this method returns. + *

    + * Followed by either onResume() if the activity returns back to + * the front, or onStop() if it becomes invisible to the user.

    Pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB}onResume() or
    + * onStop()
    {@link android.app.Activity#onStop + * onStop()}Called when the activity is no longer visible to the user, because + * another activity has been resumed and is covering this one. This may happen + * either because a new activity is being started, an existing one is being + * brought in front of this one, or this one is being destroyed. + *

    + * Followed by either onRestart() if this activity is coming back + * to interact with the user, or onDestroy() if this activity is + * going away.

    YesonRestart() or
    + * onDestroy()
    {@link android.app.Activity#onDestroy + * onDestroy()}The final call you receive before your activity is destroyed. This can + * happen either because the activity is finishing (someone called + * {@link Activity#finish} on it, or because the system is temporarily + * destroying this instance of the activity to save space. You can distinguish + * between these two scenarios with the {@link Activity#isFinishing} + * method.Yesnothing
    + * + *

    + * Note the "Killable" column in the above table -- for those methods that are + * marked as being killable, after that method returns the process hosting the + * activity may be killed by the system at any time without another + * line of its code being executed. Because of this, you should use the + * {@link #onPause} method to write any persistent data (such as user edits) to + * storage. In addition, the method {@link #onSaveInstanceState(Bundle)} is + * called before placing the activity in such a background state, allowing you + * to save away any dynamic instance state in your activity into the given + * Bundle, to be later received in {@link #onCreate} if the activity needs to be + * re-created. See the Process Lifecycle section + * for more information on how the lifecycle of a process is tied to the + * activities it is hosting. Note that it is important to save persistent data + * in {@link #onPause} instead of {@link #onSaveInstanceState} because the + * latter is not part of the lifecycle callbacks, so will not be called in every + * situation as described in its documentation. + *

    + * + *

    + * Be aware that these semantics will change slightly between applications + * targeting platforms starting with + * {@link android.os.Build.VERSION_CODES#HONEYCOMB} vs. those targeting prior + * platforms. Starting with Honeycomb, an application is not in the killable + * state until its {@link #onStop} has returned. This impacts when + * {@link #onSaveInstanceState(Bundle)} may be called (it may be safely called + * after {@link #onPause()}) and allows an application to safely wait until + * {@link #onStop()} to save persistent state. + *

    + * + *

    + * For applications targeting platforms starting with + * {@link android.os.Build.VERSION_CODES#P} {@link #onSaveInstanceState(Bundle)} + * will always be called after {@link #onStop}, so an application may safely + * perform fragment transactions in {@link #onStop} and will be able to save + * persistent state later. + *

    + * + *

    + * For those methods that are not marked as being killable, the activity's + * process will not be killed by the system starting from the time the method is + * called and continuing after it returns. Thus an activity is in the killable + * state, for example, between after onPause() to the start of + * onResume(). + *

    + * + * + *

    Configuration Changes

    + * + *

    + * If the configuration of the device (as defined by the {@link Configuration + * Resources.Configuration} class) changes, then anything displaying a user + * interface will need to update to match that configuration. Because Activity + * is the primary mechanism for interacting with the user, it includes special + * support for handling configuration changes. + *

    + * + *

    + * Unless you specify otherwise, a configuration change (such as a change in + * screen orientation, language, input devices, etc) will cause your current + * activity to be destroyed, going through the normal activity + * lifecycle process of {@link #onPause}, {@link #onStop}, and + * {@link #onDestroy} as appropriate. If the activity had been in the foreground + * or visible to the user, once {@link #onDestroy} is called in that instance + * then a new instance of the activity will be created, with whatever + * savedInstanceState the previous instance had generated from + * {@link #onSaveInstanceState}. + *

    + * + *

    + * This is done because any application resource, including layout files, can + * change based on any configuration value. Thus the only safe way to handle a + * configuration change is to re-retrieve all resources, including layouts, + * drawables, and strings. Because activities must already know how to save + * their state and re-create themselves from that state, this is a convenient + * way to have an activity restart itself with a new configuration. + *

    + * + *

    + * In some special cases, you may want to bypass restarting of your activity + * based on one or more types of configuration changes. This is done with the + * {@link android.R.attr#configChanges android:configChanges} attribute in its + * manifest. For any types of configuration changes you say that you handle + * there, you will receive a call to your current activity's + * {@link #onConfigurationChanged} method instead of being restarted. If a + * configuration change involves any that you do not handle, however, the + * activity will still be restarted and {@link #onConfigurationChanged} will not + * be called. + *

    + * + * + *

    Starting Activities and Getting Results

    + * + *

    + * The {@link android.app.Activity#startActivity} method is used to start a new + * activity, which will be placed at the top of the activity stack. It takes a + * single argument, an {@link android.content.Intent Intent}, which describes + * the activity to be executed. + *

    + * + *

    + * Sometimes you want to get a result back from an activity when it ends. For + * example, you may start an activity that lets the user pick a person in a list + * of contacts; when it ends, it returns the person that was selected. To do + * this, you call the + * {@link android.app.Activity#startActivityForResult(Intent, int)} version with + * a second integer parameter identifying the call. The result will come back + * through your {@link android.app.Activity#onActivityResult} method. + *

    + * + *

    + * When an activity exits, it can call + * {@link android.app.Activity#setResult(int)} to return data back to its + * parent. It must always supply a result code, which can be the standard + * results RESULT_CANCELED, RESULT_OK, or any custom values starting at + * RESULT_FIRST_USER. In addition, it can optionally return back an Intent + * containing any additional data it wants. All of this information appears back + * on the parent's Activity.onActivityResult(), along with the + * integer identifier it originally supplied. + *

    + * + *

    + * If a child activity fails for any reason (such as crashing), the parent + * activity will receive a result with the code RESULT_CANCELED. + *

    + * + *
    + * public class MyActivity extends Activity {
    + *     ...
    + *
    + *     static final int PICK_CONTACT_REQUEST = 0;
    + *
    + *     public boolean onKeyDown(int keyCode, KeyEvent event) {
    + *         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
    + *             // When the user center presses, let them pick a contact.
    + *             startActivityForResult(
    + *                 new Intent(Intent.ACTION_PICK,
    + *                 new Uri("content://contacts")),
    + *                 PICK_CONTACT_REQUEST);
    + *            return true;
    + *         }
    + *         return false;
    + *     }
    + *
    + *     protected void onActivityResult(int requestCode, int resultCode,
    + *             Intent data) {
    + *         if (requestCode == PICK_CONTACT_REQUEST) {
    + *             if (resultCode == RESULT_OK) {
    + *                 // A contact was picked.  Here we will just display it
    + *                 // to the user.
    + *                 startActivity(new Intent(Intent.ACTION_VIEW, data));
    + *             }
    + *         }
    + *     }
    + * }
    + * 
    + * + * + *

    Saving Persistent State

    + * + *

    + * There are generally two kinds of persistent state than an activity will deal + * with: shared document-like data (typically stored in a SQLite database using + * a {@linkplain android.content.ContentProvider content provider}) and internal + * state such as user preferences. + *

    + * + *

    + * For content provider data, we suggest that activities use a "edit in place" + * user model. That is, any edits a user makes are effectively made immediately + * without requiring an additional confirmation step. Supporting this model is + * generally a simple matter of following two rules: + *

    + * + *
      + *
    • + *

      + * When creating a new document, the backing database entry or file for it is + * created immediately. For example, if the user chooses to write a new email, a + * new entry for that email is created as soon as they start entering data, so + * that if they go to any other activity after that point this email will now + * appear in the list of drafts. + *

      + *
    • + *

      + * When an activity's onPause() method is called, it should commit + * to the backing content provider or file any changes the user has made. This + * ensures that those changes will be seen by any other activity that is about + * to run. You will probably want to commit your data even more aggressively at + * key times during your activity's lifecycle: for example before starting a new + * activity, before finishing your own activity, when the user switches between + * input fields, etc. + *

      + *
    + * + *

    + * This model is designed to prevent data loss when a user is navigating between + * activities, and allows the system to safely kill an activity (because system + * resources are needed somewhere else) at any time after it has been paused. + * Note this implies that the user pressing BACK from your activity does + * not mean "cancel" -- it means to leave the activity with its current + * contents saved away. Canceling edits in an activity must be provided through + * some other mechanism, such as an explicit "revert" or "undo" option. + *

    + * + *

    + * See the {@linkplain android.content.ContentProvider content package} for more + * information about content providers. These are a key aspect of how different + * activities invoke and propagate data between themselves. + *

    + * + *

    + * The Activity class also provides an API for managing internal persistent + * state associated with an activity. This can be used, for example, to remember + * the user's preferred initial display in a calendar (day view or week view) or + * the user's default home page in a web browser. + *

    + * + *

    + * Activity persistent state is managed with the method {@link #getPreferences}, + * allowing you to retrieve and modify a set of name/value pairs associated with + * the activity. To use preferences that are shared across multiple application + * components (activities, receivers, services, providers), you can use the + * underlying {@link Context#getSharedPreferences + * Context.getSharedPreferences()} method to retrieve a preferences object + * stored under a specific name. (Note that it is not possible to share settings + * data across application packages -- for that you will need a content + * provider.) + *

    + * + *

    + * Here is an excerpt from a calendar activity that stores the user's preferred + * view mode in its persistent settings: + *

    + * + *
    + * public class CalendarActivity extends Activity {
    + *     ...
    + *
    + *     static final int DAY_VIEW_MODE = 0;
    + *     static final int WEEK_VIEW_MODE = 1;
    + *
    + *     private SharedPreferences mPrefs;
    + *     private int mCurViewMode;
    + *
    + *     protected void onCreate(Bundle savedInstanceState) {
    + *         super.onCreate(savedInstanceState);
    + *
    + *         SharedPreferences mPrefs = getSharedPreferences();
    + *         mCurViewMode = mPrefs.getInt("view_mode", DAY_VIEW_MODE);
    + *     }
    + *
    + *     protected void onPause() {
    + *         super.onPause();
    + *
    + *         SharedPreferences.Editor ed = mPrefs.edit();
    + *         ed.putInt("view_mode", mCurViewMode);
    + *         ed.commit();
    + *     }
    + * }
    + * 
    + * + * + *

    Permissions

    + * + *

    + * The ability to start a particular Activity can be enforced when it is + * declared in its manifest's {@link android.R.styleable#AndroidManifestActivity + * <activity>} tag. By doing so, other applications will need to declare a + * corresponding {@link android.R.styleable#AndroidManifestUsesPermission + * <uses-permission>} element in their own manifest to be able to start + * that activity. + * + *

    + * When starting an Activity you can set + * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION + * Intent.FLAG_GRANT_READ_URI_PERMISSION} and/or + * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION + * Intent.FLAG_GRANT_WRITE_URI_PERMISSION} on the Intent. This will grant the + * Activity access to the specific URIs in the Intent. Access will remain until + * the Activity has finished (it will remain across the hosting process being + * killed and other temporary destruction). As of + * {@link android.os.Build.VERSION_CODES#GINGERBREAD}, if the Activity was + * already created and a new Intent is being delivered to + * {@link #onNewIntent(Intent)}, any newly granted URI permissions will be added + * to the existing ones it holds. + * + *

    + * See the Security and + * Permissions document for more information on permissions and security in + * general. + * + * + *

    Process Lifecycle

    + * + *

    + * The Android system attempts to keep an application process around for as long + * as possible, but eventually will need to remove old processes when memory + * runs low. As described in Activity + * Lifecycle, the decision about which process to remove is intimately tied + * to the state of the user's interaction with it. In general, there are four + * states a process can be in based on the activities running in it, listed here + * in order of importance. The system will kill less important processes (the + * last ones) before it resorts to killing more important processes (the first + * ones). + * + *

      + *
    1. + *

      + * The foreground activity (the activity at the top of the screen that + * the user is currently interacting with) is considered the most important. Its + * process will only be killed as a last resort, if it uses more memory than is + * available on the device. Generally at this point the device has reached a + * memory paging state, so this is required in order to keep the user interface + * responsive. + *

    2. + *

      + * A visible activity (an activity that is visible to the user but not in + * the foreground, such as one sitting behind a foreground dialog) is considered + * extremely important and will not be killed unless that is required to keep + * the foreground activity running. + *

    3. + *

      + * A background activity (an activity that is not visible to the user and + * has been paused) is no longer critical, so the system may safely kill its + * process to reclaim memory for other foreground or visible processes. If its + * process needs to be killed, when the user navigates back to the activity + * (making it visible on the screen again), its {@link #onCreate} method will be + * called with the savedInstanceState it had previously supplied in + * {@link #onSaveInstanceState} so that it can restart itself in the same state + * as the user last left it. + *

    4. + *

      + * An empty process is one hosting no activities or other application + * components (such as {@link Service} or + * {@link android.content.BroadcastReceiver} classes). These are killed very + * quickly by the system as memory becomes low. For this reason, any background + * operation you do outside of an activity must be executed in the context of an + * activity BroadcastReceiver or Service to ensure that the system knows it + * needs to keep your process around. + *

    + * + *

    + * Sometimes an Activity may need to do a long-running operation that exists + * independently of the activity lifecycle itself. An example may be a camera + * application that allows you to upload a picture to a web site. The upload may + * take a long time, and the application should allow the user to leave the + * application while it is executing. To accomplish this, your Activity should + * start a {@link Service} in which the upload takes place. This allows the + * system to properly prioritize your process (considering it to be more + * important than other non-visible applications) for the duration of the + * upload, independent of whether the original activity is paused, stopped, or + * finished. + */ +public class Activity { + /** Return the intent that started this activity. */ + public Intent getIntent() { + return null; + } + + /** + * Change the intent returned by {@link #getIntent}. This holds a reference to + * the given intent; it does not copy it. Often used in conjunction with + * {@link #onNewIntent}. + * + * @param newIntent The new Intent object to return from getIntent + * + * @see #getIntent + * @see #onNewIntent + */ + public void setIntent(Intent newIntent) { + } + + /** + * Called when the activity is starting. This is where most initialization + * should go: calling {@link #setContentView(int)} to inflate the activity's UI, + * using {@link #findViewById} to programmatically interact with widgets in the + * UI, calling + * {@link #managedQuery(android.net.Uri , String[], String, String[], String)} + * to retrieve cursors for data being displayed, etc. + * + *

    + * You can call {@link #finish} from within this function, in which case + * onDestroy() will be immediately called after {@link #onCreate} without any of + * the rest of the activity lifecycle ({@link #onStart}, {@link #onResume}, + * {@link #onPause}, etc) executing. + * + *

    + * Derived classes must call through to the super class's implementation of + * this method. If they do not, an exception will be thrown. + *

    + * + * @param savedInstanceState If the activity is being re-initialized after + * previously being shut down then this Bundle + * contains the data it most recently supplied in + * {@link #onSaveInstanceState}. Note: Otherwise + * it is null. + * + * @see #onStart + * @see #onSaveInstanceState + * @see #onRestoreInstanceState + * @see #onPostCreate + */ + protected void onCreate(Bundle savedInstanceState) { + } + + /** + * This method is called after {@link #onStart} when the activity is being + * re-initialized from a previously saved state, given here in + * savedInstanceState. Most implementations will simply use + * {@link #onCreate} to restore their state, but it is sometimes convenient to + * do it here after all of the initialization has been done or to allow + * subclasses to decide whether to use your default implementation. The default + * implementation of this method performs a restore of any view state that had + * previously been frozen by {@link #onSaveInstanceState}. + * + *

    + * This method is called between {@link #onStart} and {@link #onPostCreate}. + * + * @param savedInstanceState the data most recently supplied in + * {@link #onSaveInstanceState}. + * + * @see #onCreate + * @see #onPostCreate + * @see #onResume + * @see #onSaveInstanceState + */ + protected void onRestoreInstanceState(Bundle savedInstanceState) { + } + + /** + * Called when activity start-up is complete (after {@link #onStart} and + * {@link #onRestoreInstanceState} have been called). Applications will + * generally not implement this method; it is intended for system classes to do + * final initialization after application code has run. + * + *

    + * Derived classes must call through to the super class's implementation of + * this method. If they do not, an exception will be thrown. + *

    + * + * @param savedInstanceState If the activity is being re-initialized after + * previously being shut down then this Bundle + * contains the data it most recently supplied in + * {@link #onSaveInstanceState}. Note: Otherwise + * it is null. + * @see #onCreate + */ + protected void onPostCreate(Bundle savedInstanceState) { + } + + /** + * Called after {@link #onCreate} — or after {@link #onRestart} when the + * activity had been stopped, but is now again being displayed to the user. It + * will be followed by {@link #onResume}. + * + *

    + * Derived classes must call through to the super class's implementation of + * this method. If they do not, an exception will be thrown. + *

    + * + * @see #onCreate + * @see #onStop + * @see #onResume + */ + protected void onStart() { + } + + /** + * Called after {@link #onRestoreInstanceState}, {@link #onRestart}, or + * {@link #onPause}, for your activity to start interacting with the user. This + * is a good place to begin animations, open exclusive-access devices (such as + * the camera), etc. + * + *

    + * Keep in mind that onResume is not the best indicator that your activity is + * visible to the user; a system window such as the keyguard may be in front. + * Use {@link #onWindowFocusChanged} to know for certain that your activity is + * visible to the user (for example, to resume a game). + * + *

    + * Derived classes must call through to the super class's implementation of + * this method. If they do not, an exception will be thrown. + *

    + * + * @see #onRestoreInstanceState + * @see #onRestart + * @see #onPostResume + * @see #onPause + */ + protected void onResume() { + } + + /** + * Called when activity resume is complete (after {@link #onResume} has been + * called). Applications will generally not implement this method; it is + * intended for system classes to do final setup after application resume code + * has run. + * + *

    + * Derived classes must call through to the super class's implementation of + * this method. If they do not, an exception will be thrown. + *

    + * + * @see #onResume + */ + protected void onPostResume() { + } + + /** + * This is called for activities that set launchMode to "singleTop" in their + * package, or if a client used the {@link Intent#FLAG_ACTIVITY_SINGLE_TOP} flag + * when calling {@link #startActivity}. In either case, when the activity is + * re-launched while at the top of the activity stack instead of a new instance + * of the activity being started, onNewIntent() will be called on the existing + * instance with the Intent that was used to re-launch it. + * + *

    + * An activity will always be paused before receiving a new intent, so you can + * count on {@link #onResume} being called after this method. + * + *

    + * Note that {@link #getIntent} still returns the original Intent. You can use + * {@link #setIntent} to update it to this new Intent. + * + * @param intent The new intent that was started for the activity. + * + * @see #getIntent + * @see #setIntent + * @see #onResume + */ + protected void onNewIntent(Intent intent) { + } + + /** + * Called to retrieve per-instance state from an activity before being killed so + * that the state can be restored in {@link #onCreate} or + * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method + * will be passed to both). + * + *

    + * This method is called before an activity may be killed so that when it comes + * back some time in the future it can restore its state. For example, if + * activity B is launched in front of activity A, and at some point activity A + * is killed to reclaim resources, activity A will have a chance to save the + * current state of its user interface via this method so that when the user + * returns to activity A, the state of the user interface can be restored via + * {@link #onCreate} or {@link #onRestoreInstanceState}. + * + *

    + * Do not confuse this method with activity lifecycle callbacks such as + * {@link #onPause}, which is always called when an activity is being placed in + * the background or on its way to destruction, or {@link #onStop} which is + * called before destruction. One example of when {@link #onPause} and + * {@link #onStop} is called and not this method is when a user navigates back + * from activity B to activity A: there is no need to call + * {@link #onSaveInstanceState} on B because that particular instance will never + * be restored, so the system avoids calling it. An example when + * {@link #onPause} is called and not {@link #onSaveInstanceState} is when + * activity B is launched in front of activity A: the system may avoid calling + * {@link #onSaveInstanceState} on activity A if it isn't killed during the + * lifetime of B since the state of the user interface of A will stay intact. + * + *

    + * The default implementation takes care of most of the UI per-instance state + * for you by calling {@link android.view.View#onSaveInstanceState()} on each + * view in the hierarchy that has an id, and by saving the id of the currently + * focused view (all of which is restored by the default implementation of + * {@link #onRestoreInstanceState}). If you override this method to save + * additional information not captured by each individual view, you will likely + * want to call through to the default implementation, otherwise be prepared to + * save all of the state of each view yourself. + * + *

    + * If called, this method will occur after {@link #onStop} for applications + * targeting platforms starting with {@link android.os.Build.VERSION_CODES#P}. + * For applications targeting earlier platform versions this method will occur + * before {@link #onStop} and there are no guarantees about whether it will + * occur before or after {@link #onPause}. + * + * @param outState Bundle in which to place your saved state. + * + * @see #onCreate + * @see #onRestoreInstanceState + * @see #onPause + */ + protected void onSaveInstanceState(Bundle outState) { + } + + /** + * Called as part of the activity lifecycle when an activity is going into the + * background, but has not (yet) been killed. The counterpart to + * {@link #onResume}. + * + *

    + * When activity B is launched in front of activity A, this callback will be + * invoked on A. B will not be created until A's {@link #onPause} returns, so be + * sure to not do anything lengthy here. + * + *

    + * This callback is mostly used for saving any persistent state the activity is + * editing, to present a "edit in place" model to the user and making sure + * nothing is lost if there are not enough resources to start the new activity + * without first killing this one. This is also a good place to do things like + * stop animations and other things that consume a noticeable amount of CPU in + * order to make the switch to the next activity as fast as possible, or to + * close resources that are exclusive access such as the camera. + * + *

    + * In situations where the system needs more memory it may kill paused processes + * to reclaim resources. Because of this, you should be sure that all of your + * state is saved by the time you return from this function. In general + * {@link #onSaveInstanceState} is used to save per-instance state in the + * activity and this method is used to store global persistent data (in content + * providers, files, etc.) + * + *

    + * After receiving this call you will usually receive a following call to + * {@link #onStop} (after the next activity has been resumed and displayed), + * however in some cases there will be a direct call back to {@link #onResume} + * without going through the stopped state. + * + *

    + * Derived classes must call through to the super class's implementation of + * this method. If they do not, an exception will be thrown. + *

    + * + * @see #onResume + * @see #onSaveInstanceState + * @see #onStop + */ + protected void onPause() { + } + + /** + * Called when you are no longer visible to the user. You will next receive + * either {@link #onRestart}, {@link #onDestroy}, or nothing, depending on later + * user activity. + * + *

    + * Derived classes must call through to the super class's implementation of + * this method. If they do not, an exception will be thrown. + *

    + * + * @see #onRestart + * @see #onResume + * @see #onSaveInstanceState + * @see #onDestroy + */ + protected void onStop() { + } + + /** + * Perform any final cleanup before an activity is destroyed. This can happen + * either because the activity is finishing (someone called {@link #finish} on + * it, or because the system is temporarily destroying this instance of the + * activity to save space. You can distinguish between these two scenarios with + * the {@link #isFinishing} method. + * + *

    + * Note: do not count on this method being called as a place for saving + * data! For example, if an activity is editing data in a content provider, + * those edits should be committed in either {@link #onPause} or + * {@link #onSaveInstanceState}, not here. This method is usually + * implemented to free resources like threads that are associated with an + * activity, so that a destroyed activity does not leave such things around + * while the rest of its application is still running. There are situations + * where the system will simply kill the activity's hosting process without + * calling this method (or any others) in it, so it should not be used to do + * things that are intended to remain around after the process goes away. + * + *

    + * Derived classes must call through to the super class's implementation of + * this method. If they do not, an exception will be thrown. + *

    + * + * @see #onPause + * @see #onStop + * @see #finish + * @see #isFinishing + */ + protected void onDestroy() { + } + + /** + * Finds a view that was identified by the {@code android:id} XML attribute that + * was processed in {@link #onCreate}. + *

    + * Note: In most cases -- depending on compiler support -- the + * resulting view is automatically cast to the target class type. If the target + * class type is unconstrained, an explicit cast may be necessary. + * + * @param id the ID to search for + * @return a view with given ID if found, or {@code null} otherwise + * @see View#findViewById(int) + * @see Activity#requireViewById(int) + */ + public T findViewById(int id) { + } + + /** + * Set the activity content from a layout resource. The resource will be + * inflated, adding all top-level views to the activity. + * + * @param layoutResID Resource ID to be inflated. + * + * @see #setContentView(android.view.View) + * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) + */ + public void setContentView(int layoutResID) { + } + + /** + * Set the activity content to an explicit view. This view is placed directly + * into the activity's view hierarchy. It can itself be a complex view + * hierarchy. When calling this method, the layout parameters of the specified + * view are ignored. Both the width and the height of the view are set by + * default to {@link ViewGroup.LayoutParams#MATCH_PARENT}. To use your own + * layout parameters, invoke + * {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)} + * instead. + * + * @param view The desired content to display. + * + * @see #setContentView(int) + * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) + */ + public void setContentView(View view) { + } + + /** + * Same as calling {@link #startActivityForResult(Intent, int, Bundle)} with no + * options. + * + * @param intent The intent to start. + * @param requestCode If >= 0, this code will be returned in onActivityResult() + * when the activity exits. + * + * @throws android.content.ActivityNotFoundException + * + * @see #startActivity + */ + public void startActivityForResult(Intent intent, int requestCode) { + } + + /** + * Same as {@link #startActivity(Intent, Bundle)} with no options specified. + * + * @param intent The intent to start. + * + * @throws android.content.ActivityNotFoundException + * + * @see #startActivity(Intent, Bundle) + * @see #startActivityForResult + */ + public void startActivity(Intent intent) { + } + + /** + * Launch a new activity. You will not receive any information about when the + * activity exits. This implementation overrides the base version, providing + * information about the activity performing the launch. Because of this + * additional information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag + * is not required; if not specified, the new activity will be added to the task + * of the caller. + * + *

    + * This method throws {@link android.content.ActivityNotFoundException} if there + * was no Activity found to run the given Intent. + * + * @param intent The intent to start. + * @param options Additional options for how the Activity should be started. See + * {@link android.content.Context#startActivity(Intent, Bundle)} + * Context.startActivity(Intent, Bundle)} for more details. + * + * @throws android.content.ActivityNotFoundException + * + * @see #startActivity(Intent) + * @see #startActivityForResult + */ + public void startActivity(Intent intent, Bundle options) { + } + + /** + * Same as {@link #startActivities(Intent[], Bundle)} with no options specified. + * + * @param intents The intents to start. + * + * @throws android.content.ActivityNotFoundException + * + * @see #startActivities(Intent[], Bundle) + * @see #startActivityForResult + */ + public void startActivities(Intent[] intents) { + } + + /** + * Launch a new activity. You will not receive any information about when the + * activity exits. This implementation overrides the base version, providing + * information about the activity performing the launch. Because of this + * additional information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag + * is not required; if not specified, the new activity will be added to the task + * of the caller. + * + *

    + * This method throws {@link android.content.ActivityNotFoundException} if there + * was no Activity found to run the given Intent. + * + * @param intents The intents to start. + * @param options Additional options for how the Activity should be started. See + * {@link android.content.Context#startActivity(Intent, Bundle)} + * Context.startActivity(Intent, Bundle)} for more details. + * + * @throws android.content.ActivityNotFoundException + * + * @see #startActivities(Intent[]) + * @see #startActivityForResult + */ + public void startActivities(Intent[] intents, Bundle options) { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java new file mode 100644 index 00000000000..63709ae41b6 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java @@ -0,0 +1,816 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content; + +import java.io.File; +import android.os.Bundle; + +/** + * Interface to global information about an application environment. This is an + * abstract class whose implementation is provided by the Android system. It + * allows access to application-specific resources and classes, as well as + * up-calls for application-level operations such as launching activities, + * broadcasting and receiving intents, etc. + */ +public abstract class Context { + /** + * Return the context of the single, global Application object of the current + * process. This generally should only be used if you need a Context whose + * lifecycle is separate from the current context, that is tied to the lifetime + * of the process rather than the current component. + * + *

    + * Consider for example how this interacts with + * {@link #registerReceiver(BroadcastReceiver, IntentFilter)}: + *

      + *
    • + *

      + * If used from an Activity context, the receiver is being registered within + * that activity. This means that you are expected to unregister before the + * activity is done being destroyed; in fact if you do not do so, the framework + * will clean up your leaked registration as it removes the activity and log an + * error. Thus, if you use the Activity context to register a receiver that is + * static (global to the process, not associated with an Activity instance) then + * that registration will be removed on you at whatever point the activity you + * used is destroyed. + *

    • + *

      + * If used from the Context returned here, the receiver is being registered with + * the global state associated with your application. Thus it will never be + * unregistered for you. This is necessary if the receiver is associated with + * static data, not a particular component. However using the ApplicationContext + * elsewhere can easily lead to serious leaks if you forget to unregister, + * unbind, etc. + *

    + */ + public abstract Context getApplicationContext(); + + /** + * Returns the absolute path on the filesystem where a file created with + * {@link #openFileOutput} is stored. + *

    + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + * + * @param name The name of the file for which you would like to get its path. + * + * @return An absolute path to the given file. + * + * @see #openFileOutput + * @see #getFilesDir + * @see #getDir + */ + public abstract File getFileStreamPath(String name); + + /** + * Returns the absolute path on the filesystem where a file created with + * {@link #getSharedPreferences(String, int)} is stored. + *

    + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + * + * @param name The name of the shared preferences for which you would like to + * get a path. + * @return An absolute path to the given file. + * @see #getSharedPreferences(String, int) + * @removed + */ + public abstract File getSharedPreferencesPath(String name); + + /** + * Returns the absolute path to the directory on the filesystem where all + * private files belonging to this app are stored. Apps should not use this path + * directly; they should instead use {@link #getFilesDir()}, + * {@link #getCacheDir()}, {@link #getDir(String, int)}, or other storage APIs + * on this class. + *

    + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

    + * No additional permissions are required for the calling app to read or write + * files under the returned path. + * + * @see ApplicationInfo#dataDir + */ + public abstract File getDataDir(); + + /** + * Returns the absolute path to the directory on the filesystem where files + * created with {@link #openFileOutput} are stored. + *

    + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

    + * No additional permissions are required for the calling app to read or write + * files under the returned path. + * + * @return The path of the directory holding application files. + * @see #openFileOutput + * @see #getFileStreamPath + * @see #getDir + */ + public abstract File getFilesDir(); + + /** + * Returns the absolute path to the directory on the filesystem similar to + * {@link #getFilesDir()}. The difference is that files placed under this + * directory will be excluded from automatic backup to remote storage. See + * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion of + * the automatic backup mechanism in Android. + *

    + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

    + * No additional permissions are required for the calling app to read or write + * files under the returned path. + * + * @return The path of the directory holding application files that will not be + * automatically backed up to remote storage. + * @see #openFileOutput + * @see #getFileStreamPath + * @see #getDir + * @see android.app.backup.BackupAgent + */ + public abstract File getNoBackupFilesDir(); + + /** + * Returns the absolute path to the directory on the primary shared/external + * storage device where the application can place persistent files it owns. + * These files are internal to the applications, and not typically visible to + * the user as media. + *

    + * This is like {@link #getFilesDir()} in that these files will be deleted when + * the application is uninstalled, however there are some important differences: + *

      + *
    • Shared storage may not always be available, since removable media can be + * ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
    • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these + * files. + *
    + *

    + * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little benefit + * to storing data here instead of the private directories returned by + * {@link #getFilesDir()}, etc. + *

    + * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions are + * required to read or write to the returned path; it's always accessible to the + * calling app. This only applies to paths generated for package name of the + * calling application. To access paths belonging to other packages, + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. + *

    + * On devices with multiple users (as described by {@link UserManager}), each + * user has their own isolated shared storage. Applications only have access to + * the shared storage for the user they're running as. + *

    + * The returned path may change over time if different shared storage media is + * inserted, so only relative paths should be persisted. + *

    + * Here is an example of typical code to manipulate a file in an application's + * shared storage: + *

    + * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java + * private_file} + *

    + * If you supply a non-null type to this function, the returned file + * will be a path to a sub-directory of the given type. Though these files are + * not automatically scanned by the media scanner, you can explicitly add them + * to the media database with + * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], android.media.MediaScannerConnection.OnScanCompletedListener) + * MediaScannerConnection.scanFile}. Note that this is not the same as + * {@link android.os.Environment#getExternalStoragePublicDirectory + * Environment.getExternalStoragePublicDirectory()}, which provides directories + * of media shared by all applications. The directories returned here are owned + * by the application, and their contents will be removed when the application + * is uninstalled. Unlike + * {@link android.os.Environment#getExternalStoragePublicDirectory + * Environment.getExternalStoragePublicDirectory()}, the directory returned here + * will be automatically created for you. + *

    + * Here is an example of typical code to manipulate a picture in an + * application's shared storage and add it to the media database: + *

    + * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java + * private_picture} + * + * @param type The type of files directory to return. May be {@code null} for + * the root of the files directory or one of the following constants + * for a subdirectory: + * {@link android.os.Environment#DIRECTORY_MUSIC}, + * {@link android.os.Environment#DIRECTORY_PODCASTS}, + * {@link android.os.Environment#DIRECTORY_RINGTONES}, + * {@link android.os.Environment#DIRECTORY_ALARMS}, + * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, + * {@link android.os.Environment#DIRECTORY_PICTURES}, or + * {@link android.os.Environment#DIRECTORY_MOVIES}. + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. + * @see #getFilesDir + * @see #getExternalFilesDirs(String) + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + public abstract File getExternalFilesDir(String type); + + /** + * Returns absolute paths to application-specific directories on all + * shared/external storage devices where the application can place persistent + * files it owns. These files are internal to the application, and not typically + * visible to the user as media. + *

    + * This is like {@link #getFilesDir()} in that these files will be deleted when + * the application is uninstalled, however there are some important differences: + *

      + *
    • Shared storage may not always be available, since removable media can be + * ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
    • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these + * files. + *
    + *

    + * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little benefit + * to storing data here instead of the private directories returned by + * {@link #getFilesDir()}, etc. + *

    + * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The returned + * paths do not include transient devices, such as USB flash drives connected to + * handheld devices. + *

    + * An application may store data on any or all of the returned devices. For + * example, an app may choose to store large files on the device with the most + * available space, as measured by {@link StatFs}. + *

    + * No additional permissions are required for the calling app to read or write + * files under the returned path. Write access outside of these paths on + * secondary external storage devices is not available. + *

    + * The returned path may change over time if different shared storage media is + * inserted, so only relative paths should be persisted. + * + * @param type The type of files directory to return. May be {@code null} for + * the root of the files directory or one of the following constants + * for a subdirectory: + * {@link android.os.Environment#DIRECTORY_MUSIC}, + * {@link android.os.Environment#DIRECTORY_PODCASTS}, + * {@link android.os.Environment#DIRECTORY_RINGTONES}, + * {@link android.os.Environment#DIRECTORY_ALARMS}, + * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, + * {@link android.os.Environment#DIRECTORY_PICTURES}, or + * {@link android.os.Environment#DIRECTORY_MOVIES}. + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is not + * currently available. The first path returned is the same as + * {@link #getExternalFilesDir(String)}. + * @see #getExternalFilesDir(String) + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + public abstract File[] getExternalFilesDirs(String type); + + /** + * Return the primary shared/external storage directory where this application's + * OBB files (if there are any) can be found. Note if the application does not + * have any OBB files, this directory may not exist. + *

    + * This is like {@link #getFilesDir()} in that these files will be deleted when + * the application is uninstalled, however there are some important differences: + *

      + *
    • Shared storage may not always be available, since removable media can be + * ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
    • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these + * files. + *
    + *

    + * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions are + * required to read or write to the path that this method returns. However, + * starting from {@link android.os.Build.VERSION_CODES#M}, to read the OBB + * expansion files, you must declare the + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission in the + * app manifest and ask for permission at runtime as follows: + *

    + *

    + * {@code } + *

    + *

    + * Starting from {@link android.os.Build.VERSION_CODES#N}, + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission is not + * required, so don’t ask for this permission at runtime. To handle both cases, + * your app must first try to read the OBB file, and if it fails, you must + * request {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission + * at runtime. + *

    + * + *

    + * The following code snippet shows how to do this: + *

    + * + *
    +     * File obb = new File(obb_filename);
    +     * boolean open_failed = false;
    +     *
    +     * try {
    +     *     BufferedReader br = new BufferedReader(new FileReader(obb));
    +     *     open_failed = false;
    +     *     ReadObbFile(br);
    +     * } catch (IOException e) {
    +     *     open_failed = true;
    +     * }
    +     *
    +     * if (open_failed) {
    +     *     // request READ_EXTERNAL_STORAGE permission before reading OBB file
    +     *     ReadObbFileWithPermission();
    +     * }
    +     * 
    + * + * On devices with multiple users (as described by {@link UserManager}), + * multiple users may share the same OBB storage location. Applications should + * ensure that multiple instances running under different users don't interfere + * with each other. + * + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. + * @see #getObbDirs() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + public abstract File getObbDir(); + + /** + * Returns absolute paths to application-specific directories on all + * shared/external storage devices where the application's OBB files (if there + * are any) can be found. Note if the application does not have any OBB files, + * these directories may not exist. + *

    + * This is like {@link #getFilesDir()} in that these files will be deleted when + * the application is uninstalled, however there are some important differences: + *

      + *
    • Shared storage may not always be available, since removable media can be + * ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
    • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these + * files. + *
    + *

    + * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The returned + * paths do not include transient devices, such as USB flash drives connected to + * handheld devices. + *

    + * An application may store data on any or all of the returned devices. For + * example, an app may choose to store large files on the device with the most + * available space, as measured by {@link StatFs}. + *

    + * No additional permissions are required for the calling app to read or write + * files under the returned path. Write access outside of these paths on + * secondary external storage devices is not available. + * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is not + * currently available. The first path returned is the same as + * {@link #getObbDir()} + * @see #getObbDir() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + public abstract File[] getObbDirs(); + + /** + * Returns the absolute path to the application specific cache directory on the + * filesystem. + *

    + * The system will automatically delete files in this directory as disk space is + * needed elsewhere on the device. The system will always delete older files + * first, as reported by {@link File#lastModified()}. If desired, you can exert + * more control over how files are deleted using + * {@link StorageManager#setCacheBehaviorGroup(File, boolean)} and + * {@link StorageManager#setCacheBehaviorTombstone(File, boolean)}. + *

    + * Apps are strongly encouraged to keep their usage of cache space below the + * quota returned by {@link StorageManager#getCacheQuotaBytes(java.util.UUID)}. + * If your app goes above this quota, your cached files will be some of the + * first to be deleted when additional disk space is needed. Conversely, if your + * app stays under this quota, your cached files will be some of the last to be + * deleted when additional disk space is needed. + *

    + * Note that your cache quota will change over time depending on how frequently + * the user interacts with your app, and depending on how much system-wide disk + * space is used. + *

    + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

    + * Apps require no extra permissions to read or write to the returned path, + * since this path lives in their private storage. + * + * @return The path of the directory holding application cache files. + * @see #openFileOutput + * @see #getFileStreamPath + * @see #getDir + * @see #getExternalCacheDir + */ + public abstract File getCacheDir(); + + /** + * Returns the absolute path to the application specific cache directory on the + * filesystem designed for storing cached code. + *

    + * The system will delete any files stored in this location both when your + * specific application is upgraded, and when the entire platform is upgraded. + *

    + * This location is optimal for storing compiled or optimized code generated by + * your application at runtime. + *

    + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

    + * Apps require no extra permissions to read or write to the returned path, + * since this path lives in their private storage. + * + * @return The path of the directory holding application code cache files. + */ + public abstract File getCodeCacheDir(); + + /** + * Returns absolute path to application-specific directory on the primary + * shared/external storage device where the application can place cache files it + * owns. These files are internal to the application, and not typically visible + * to the user as media. + *

    + * This is like {@link #getCacheDir()} in that these files will be deleted when + * the application is uninstalled, however there are some important differences: + *

      + *
    • The platform does not always monitor the space available in shared + * storage, and thus may not automatically delete these files. Apps should + * always manage the maximum space used in this location. Currently the only + * time files here will be deleted by the platform is when running on + * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and + * {@link Environment#isExternalStorageEmulated(File)} returns true. + *
    • Shared storage may not always be available, since removable media can be + * ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
    • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these + * files. + *
    + *

    + * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), its contents are backed + * by a private user data partition, which means there is little benefit to + * storing data here instead of the private directory returned by + * {@link #getCacheDir()}. + *

    + * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions are + * required to read or write to the returned path; it's always accessible to the + * calling app. This only applies to paths generated for package name of the + * calling application. To access paths belonging to other packages, + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. + *

    + * On devices with multiple users (as described by {@link UserManager}), each + * user has their own isolated shared storage. Applications only have access to + * the shared storage for the user they're running as. + *

    + * The returned path may change over time if different shared storage media is + * inserted, so only relative paths should be persisted. + * + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. + * @see #getCacheDir + * @see #getExternalCacheDirs() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + public abstract File getExternalCacheDir(); + + /** + * Returns absolute path to application-specific directory in the preloaded + * cache. + *

    + * Files stored in the cache directory can be deleted when the device runs low + * on storage. There is no guarantee when these files will be deleted. + * + * @hide + */ + public abstract File getPreloadsFileCache(); + + /** + * Returns absolute paths to application-specific directories on all + * shared/external storage devices where the application can place cache files + * it owns. These files are internal to the application, and not typically + * visible to the user as media. + *

    + * This is like {@link #getCacheDir()} in that these files will be deleted when + * the application is uninstalled, however there are some important differences: + *

      + *
    • The platform does not always monitor the space available in shared + * storage, and thus may not automatically delete these files. Apps should + * always manage the maximum space used in this location. Currently the only + * time files here will be deleted by the platform is when running on + * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and + * {@link Environment#isExternalStorageEmulated(File)} returns true. + *
    • Shared storage may not always be available, since removable media can be + * ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
    • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these + * files. + *
    + *

    + * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little benefit + * to storing data here instead of the private directory returned by + * {@link #getCacheDir()}. + *

    + * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The returned + * paths do not include transient devices, such as USB flash drives connected to + * handheld devices. + *

    + * An application may store data on any or all of the returned devices. For + * example, an app may choose to store large files on the device with the most + * available space, as measured by {@link StatFs}. + *

    + * No additional permissions are required for the calling app to read or write + * files under the returned path. Write access outside of these paths on + * secondary external storage devices is not available. + *

    + * The returned paths may change over time if different shared storage media is + * inserted, so only relative paths should be persisted. + * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is not + * currently available. The first path returned is the same as + * {@link #getExternalCacheDir()}. + * @see #getExternalCacheDir() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + public abstract File[] getExternalCacheDirs(); + + /** + * Returns absolute paths to application-specific directories on all + * shared/external storage devices where the application can place media files. + * These files are scanned and made available to other apps through + * {@link MediaStore}. + *

    + * This is like {@link #getExternalFilesDirs} in that these files will be + * deleted when the application is uninstalled, however there are some important + * differences: + *

      + *
    • Shared storage may not always be available, since removable media can be + * ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
    • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to these + * files. + *
    + *

    + * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The returned + * paths do not include transient devices, such as USB flash drives connected to + * handheld devices. + *

    + * An application may store data on any or all of the returned devices. For + * example, an app may choose to store large files on the device with the most + * available space, as measured by {@link StatFs}. + *

    + * No additional permissions are required for the calling app to read or write + * files under the returned path. Write access outside of these paths on + * secondary external storage devices is not available. + *

    + * The returned paths may change over time if different shared storage media is + * inserted, so only relative paths should be persisted. + * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is not + * currently available. + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) + */ + public abstract File[] getExternalMediaDirs(); + + /** + * Returns an array of strings naming the private files associated with this + * Context's application package. + * + * @return Array of strings naming the private files. + * + * @see #openFileInput + * @see #openFileOutput + * @see #deleteFile + */ + public abstract String[] fileList(); + + /** + * Retrieve, creating if needed, a new directory in which the application can + * place its own custom data files. You can use the returned File object to + * create and access files in this directory. Note that files created through a + * File object will only be accessible by your own application; you can only set + * the mode of the entire directory, not of individual files. + *

    + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

    + * Apps require no extra permissions to read or write to the returned path, + * since this path lives in their private storage. + * + * @param name Name of the directory to retrieve. This is a directory that is + * created as part of your application data. + * @param mode Operating mode. + * + * @return A {@link File} object for the requested directory. The directory will + * have been created if it does not already exist. + * + * @see #openFileOutput(String, int) + */ + public abstract File getDir(String name, int mode); + + /** + * Same as {@link #startActivity(Intent, Bundle)} with no options specified. + * + * @param intent The description of the activity to start. + * + * @throws ActivityNotFoundException   ` + * @see #startActivity(Intent, Bundle) + * @see PackageManager#resolveActivity + */ + public abstract void startActivity(Intent intent); + + /** + * Launch a new activity. You will not receive any information about when the + * activity exits. + * + *

    + * Note that if this method is being called from outside of an + * {@link android.app.Activity} Context, then the Intent must include the + * {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag. This is because, without + * being started from an existing Activity, there is no existing task in which + * to place the new activity and thus it needs to be placed in its own separate + * task. + * + *

    + * This method throws {@link ActivityNotFoundException} if there was no Activity + * found to run the given Intent. + * + * @param intent The description of the activity to start. + * @param options Additional options for how the Activity should be started. May + * be null if there are no options. See + * {@link android.app.ActivityOptions} for how to build the + * Bundle supplied here; there are no supported definitions for + * building it manually. + * + * @throws ActivityNotFoundException   + * + * @see #startActivity(Intent) + * @see PackageManager#resolveActivity + */ + public abstract void startActivity(Intent intent, Bundle options); + + /** + * Identifies whether this Context instance will be able to process calls to + * {@link #startActivityForResult(String, Intent, int, Bundle)}. + * + * @hide + */ + public boolean canStartActivityForResult() { + return false; + } + + /** + * Same as {@link #startActivities(Intent[], Bundle)} with no options specified. + * + * @param intents An array of Intents to be started. + * + * @throws ActivityNotFoundException   + * + * @see #startActivities(Intent[], Bundle) + * @see PackageManager#resolveActivity + */ + public abstract void startActivities(Intent[] intents); + + /** + * Launch multiple new activities. This is generally the same as calling + * {@link #startActivity(Intent)} for the first Intent in the array, that + * activity during its creation calling {@link #startActivity(Intent)} for the + * second entry, etc. Note that unlike that approach, generally none of the + * activities except the last in the array will be created at this point, but + * rather will be created when the user first visits them (due to pressing back + * from the activity on top). + * + *

    + * This method throws {@link ActivityNotFoundException} if there was no Activity + * found for any given Intent. In this case the state of the activity + * stack is undefined (some Intents in the list may be on it, some not), so you + * probably want to avoid such situations. + * + * @param intents An array of Intents to be started. + * @param options Additional options for how the Activity should be started. See + * {@link android.content.Context#startActivity(Intent, Bundle)} + * Context.startActivity(Intent, Bundle)} for more details. + * + * @throws ActivityNotFoundException   + * + * @see #startActivities(Intent[]) + * @see PackageManager#resolveActivity + */ + public abstract void startActivities(Intent[] intents, Bundle options); + + /** + * Broadcast the given intent to all interested BroadcastReceivers. This call is + * asynchronous; it returns immediately, and you will continue executing while + * the receivers are run. No results are propagated from receivers and receivers + * can not abort the broadcast. If you want to allow receivers to propagate + * results or abort the broadcast, you must send an ordered broadcast using + * {@link #sendOrderedBroadcast(Intent, String)}. + * + *

    + * See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching this Intent + * will receive the broadcast. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, + * String, Bundle) + */ + public abstract void sendBroadcast(Intent intent); + + /** + * Broadcast the given intent to all interested BroadcastReceivers, allowing an + * optional required permission to be enforced. This call is asynchronous; it + * returns immediately, and you will continue executing while the receivers are + * run. No results are propagated from receivers and receivers can not abort the + * broadcast. If you want to allow receivers to propagate results or abort the + * broadcast, you must send an ordered broadcast using + * {@link #sendOrderedBroadcast(Intent, String)}. + * + *

    + * See {@link BroadcastReceiver} for more information on Intent broadcasts. + * + * @param intent The Intent to broadcast; all receivers matching + * this Intent will receive the broadcast. + * @param receiverPermission (optional) String naming a permission that a + * receiver must hold in order to receive your + * broadcast. If null, no permission is required. + * + * @see android.content.BroadcastReceiver + * @see #registerReceiver + * @see #sendBroadcast(Intent) + * @see #sendOrderedBroadcast(Intent, String) + * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, + * String, Bundle) + */ + public abstract void sendBroadcast(Intent intent, String receiverPermission); + + /** + * Like {@link #sendBroadcast(Intent, String)}, but also allows specification of + * an associated app op as per {@link android.app.AppOpsManager}. + * + * @hide + */ + public abstract void sendBroadcast(Intent intent, String receiverPermission, int appOp); +} \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java new file mode 100644 index 00000000000..e3ef6277dba --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java @@ -0,0 +1,1999 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content; + +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Serializable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Set; + +/** + * An intent is an abstract description of an operation to be performed. It can + * be used with {@link Context#startActivity(Intent) startActivity} to launch an + * {@link android.app.Activity}, + * {@link android.content.Context#sendBroadcast(Intent) broadcastIntent} to send + * it to any interested {@link BroadcastReceiver BroadcastReceiver} components, + * and {@link android.content.Context#startService} or + * {@link android.content.Context#bindService} to communicate with a background + * {@link android.app.Service}. + * + *

    + * An Intent provides a facility for performing late runtime binding between the + * code in different applications. Its most significant use is in the launching + * of activities, where it can be thought of as the glue between activities. It + * is basically a passive data structure holding an abstract description of an + * action to be performed. + *

    + * + *
    + *

    Developer Guides

    + *

    + * For information about how to create and resolve intents, read the + * Intents and + * Intent Filters developer guide. + *

    + *
    + * + * + *

    Intent Structure

    + *

    + * The primary pieces of information in an intent are: + *

    + * + *
      + *
    • + *

      + * action -- The general action to be performed, such as + * {@link #ACTION_VIEW}, {@link #ACTION_EDIT}, {@link #ACTION_MAIN}, etc. + *

      + *
    • + *
    • + *

      + * data -- The data to operate on, such as a person record in the + * contacts database, expressed as a {@link android.net.Uri}. + *

      + *
    • + *
    + * + * + *

    + * Some examples of action/data pairs are: + *

    + * + *
      + *
    • + *

      + * {@link #ACTION_VIEW} content://contacts/people/1 -- Display + * information about the person whose identifier is "1". + *

      + *
    • + *
    • + *

      + * {@link #ACTION_DIAL} content://contacts/people/1 -- Display the + * phone dialer with the person filled in. + *

      + *
    • + *
    • + *

      + * {@link #ACTION_VIEW} tel:123 -- Display the phone dialer with + * the given number filled in. Note how the VIEW action does what is considered + * the most reasonable thing for a particular URI. + *

      + *
    • + *
    • + *

      + * {@link #ACTION_DIAL} tel:123 -- Display the phone dialer with + * the given number filled in. + *

      + *
    • + *
    • + *

      + * {@link #ACTION_EDIT} content://contacts/people/1 -- Edit + * information about the person whose identifier is "1". + *

      + *
    • + *
    • + *

      + * {@link #ACTION_VIEW} content://contacts/people/ -- Display a + * list of people, which the user can browse through. This example is a typical + * top-level entry into the Contacts application, showing you the list of + * people. Selecting a particular person to view would result in a new intent { + * {@link #ACTION_VIEW} content://contacts/people/N } being used + * to start an activity to display that person. + *

      + *
    • + *
    + * + *

    + * In addition to these primary attributes, there are a number of secondary + * attributes that you can also include with an intent: + *

    + * + *
      + *
    • + *

      + * category -- Gives additional information about the action to execute. + * For example, {@link #CATEGORY_LAUNCHER} means it should appear in the + * Launcher as a top-level application, while {@link #CATEGORY_ALTERNATIVE} + * means it should be included in a list of alternative actions the user can + * perform on a piece of data. + *

      + *
    • + *

      + * type -- Specifies an explicit type (a MIME type) of the intent data. + * Normally the type is inferred from the data itself. By setting this + * attribute, you disable that evaluation and force an explicit type. + *

      + *
    • + *

      + * component -- Specifies an explicit name of a component class to use + * for the intent. Normally this is determined by looking at the other + * information in the intent (the action, data/type, and categories) and + * matching that with a component that can handle it. If this attribute is set + * then none of the evaluation is performed, and this component is used exactly + * as is. By specifying this attribute, all of the other Intent attributes + * become optional. + *

      + *
    • + *

      + * extras -- This is a {@link Bundle} of any additional information. This + * can be used to provide extended information to the component. For example, if + * we have a action to send an e-mail message, we could also include extra + * pieces of data here to supply a subject, body, etc. + *

      + *
    + * + *

    + * Here are some examples of other operations you can specify as intents using + * these additional parameters: + *

    + * + *
      + *
    • + *

      + * {@link #ACTION_MAIN} with category {@link #CATEGORY_HOME} -- Launch + * the home screen. + *

      + *
    • + *
    • + *

      + * {@link #ACTION_GET_CONTENT} with MIME type + * {@link android.provider.Contacts.Phones#CONTENT_URI + * vnd.android.cursor.item/phone} -- Display the list of people's phone + * numbers, allowing the user to browse through them and pick one and return it + * to the parent activity. + *

      + *
    • + *
    • + *

      + * {@link #ACTION_GET_CONTENT} with MIME type *{@literal /}* and + * category {@link #CATEGORY_OPENABLE} -- Display all pickers for data that + * can be opened with {@link ContentResolver#openInputStream(Uri) + * ContentResolver.openInputStream()}, allowing the user to pick one of them and + * then some data inside of it and returning the resulting URI to the caller. + * This can be used, for example, in an e-mail application to allow the user to + * pick some data to include as an attachment. + *

      + *
    • + *
    + * + *

    + * There are a variety of standard Intent action and category constants defined + * in the Intent class, but applications can also define their own. These + * strings use Java-style scoping, to ensure they are unique -- for example, the + * standard {@link #ACTION_VIEW} is called "android.intent.action.VIEW". + *

    + * + *

    + * Put together, the set of actions, data types, categories, and extra data + * defines a language for the system allowing for the expression of phrases such + * as "call john smith's cell". As applications are added to the system, they + * can extend this language by adding new actions, types, and categories, or + * they can modify the behavior of existing phrases by supplying their own + * activities that handle them. + *

    + * + * + *

    Intent Resolution

    + * + *

    + * There are two primary forms of intents you will use. + * + *

      + *
    • + *

      + * Explicit Intents have specified a component (via {@link #setComponent} + * or {@link #setClass}), which provides the exact class to be run. Often these + * will not include any other information, simply being a way for an application + * to launch various internal activities it has as the user interacts with the + * application. + * + *

    • + *

      + * Implicit Intents have not specified a component; instead, they must + * include enough information for the system to determine which of the available + * components is best to run for that intent. + *

    + * + *

    + * When using implicit intents, given such an arbitrary intent we need to know + * what to do with it. This is handled by the process of Intent + * resolution, which maps an Intent to an {@link android.app.Activity}, + * {@link BroadcastReceiver}, or {@link android.app.Service} (or sometimes two + * or more activities/receivers) that can handle it. + *

    + * + *

    + * The intent resolution mechanism basically revolves around matching an Intent + * against all of the <intent-filter> descriptions in the installed + * application packages. (Plus, in the case of broadcasts, any + * {@link BroadcastReceiver} objects explicitly registered with + * {@link Context#registerReceiver}.) More details on this can be found in the + * documentation on the {@link IntentFilter} class. + *

    + * + *

    + * There are three pieces of information in the Intent that are used for + * resolution: the action, type, and category. Using this information, a query + * is done on the {@link PackageManager} for a component that can handle the + * intent. The appropriate component is determined based on the intent + * information supplied in the AndroidManifest.xml file as follows: + *

    + * + *
      + *
    • + *

      + * The action, if given, must be listed by the component as one it + * handles. + *

      + *
    • + *

      + * The type is retrieved from the Intent's data, if not already supplied + * in the Intent. Like the action, if a type is included in the intent (either + * explicitly or implicitly in its data), then this must be listed by the + * component as one it handles. + *

      + *
    • For data that is not a content: URI and where no explicit + * type is included in the Intent, instead the scheme of the intent data + * (such as http: or mailto:) is considered. Again + * like the action, if we are matching a scheme it must be listed by the + * component as one it can handle. + *
    • + *

      + * The categories, if supplied, must all be listed by the + * activity as categories it handles. That is, if you include the categories + * {@link #CATEGORY_LAUNCHER} and {@link #CATEGORY_ALTERNATIVE}, then you will + * only resolve to components with an intent that lists both of those + * categories. Activities will very often need to support the + * {@link #CATEGORY_DEFAULT} so that they can be found by + * {@link Context#startActivity Context.startActivity()}. + *

      + *
    + * + *

    + * For example, consider the Note Pad sample application that allows a user to + * browse through a list of notes data and view details about individual items. + * Text in italics indicates places where you would replace a name with one + * specific to your own package. + *

    + * + *
    + *  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    + *       package="com.android.notepad">
    + *     <application android:icon="@drawable/app_notes"
    + *             android:label="@string/app_name">
    + *
    + *         <provider class=".NotePadProvider"
    + *                 android:authorities="com.google.provider.NotePad" />
    + *
    + *         <activity class=".NotesList" android:label="@string/title_notes_list">
    + *             <intent-filter>
    + *                 <action android:name="android.intent.action.MAIN" />
    + *                 <category android:name="android.intent.category.LAUNCHER" />
    + *             </intent-filter>
    + *             <intent-filter>
    + *                 <action android:name="android.intent.action.VIEW" />
    + *                 <action android:name="android.intent.action.EDIT" />
    + *                 <action android:name="android.intent.action.PICK" />
    + *                 <category android:name="android.intent.category.DEFAULT" />
    + *                 <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
    + *             </intent-filter>
    + *             <intent-filter>
    + *                 <action android:name="android.intent.action.GET_CONTENT" />
    + *                 <category android:name="android.intent.category.DEFAULT" />
    + *                 <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    + *             </intent-filter>
    + *         </activity>
    + *
    + *         <activity class=".NoteEditor" android:label="@string/title_note">
    + *             <intent-filter android:label="@string/resolve_edit">
    + *                 <action android:name="android.intent.action.VIEW" />
    + *                 <action android:name="android.intent.action.EDIT" />
    + *                 <category android:name="android.intent.category.DEFAULT" />
    + *                 <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    + *             </intent-filter>
    + *
    + *             <intent-filter>
    + *                 <action android:name="android.intent.action.INSERT" />
    + *                 <category android:name="android.intent.category.DEFAULT" />
    + *                 <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
    + *             </intent-filter>
    + *
    + *         </activity>
    + *
    + *         <activity class=".TitleEditor" android:label="@string/title_edit_title"
    + *                 android:theme="@android:style/Theme.Dialog">
    + *             <intent-filter android:label="@string/resolve_title">
    + *                 <action android:name="com.android.notepad.action.EDIT_TITLE" />
    + *                 <category android:name="android.intent.category.DEFAULT" />
    + *                 <category android:name="android.intent.category.ALTERNATIVE" />
    + *                 <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
    + *                 <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    + *             </intent-filter>
    + *         </activity>
    + *
    + *     </application>
    + * </manifest>
    + * 
    + * + *

    + * The first activity, com.android.notepad.NotesList, serves as our + * main entry into the app. It can do three things as described by its three + * intent templates: + *

      + *
    1. + * + *
      + * <intent-filter>
      + *     <action android:name="{@link #ACTION_MAIN android.intent.action.MAIN}" />
      + *     <category android:name="{@link #CATEGORY_LAUNCHER android.intent.category.LAUNCHER}" />
      + * </intent-filter>
      + * 
      + *

      + * This provides a top-level entry into the NotePad application: the standard + * MAIN action is a main entry point (not requiring any other information in the + * Intent), and the LAUNCHER category says that this entry point should be + * listed in the application launcher. + *

      + *
    2. + * + *
      + * <intent-filter>
      + *     <action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" />
      + *     <action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" />
      + *     <action android:name="{@link #ACTION_PICK android.intent.action.PICK}" />
      + *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      + *     <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
      + * </intent-filter>
      + * 
      + *

      + * This declares the things that the activity can do on a directory of notes. + * The type being supported is given with the <type> tag, where + * vnd.android.cursor.dir/vnd.google.note is a URI from which a + * Cursor of zero or more items (vnd.android.cursor.dir) can be + * retrieved which holds our note pad data (vnd.google.note). The + * activity allows the user to view or edit the directory of data (via the VIEW + * and EDIT actions), or to pick a particular note and return it to the caller + * (via the PICK action). Note also the DEFAULT category supplied here: this is + * required for the {@link Context#startActivity Context.startActivity} + * method to resolve your activity when its component name is not explicitly + * specified. + *

      + *
    3. + * + *
      + * <intent-filter>
      + *     <action android:name="{@link #ACTION_GET_CONTENT android.intent.action.GET_CONTENT}" />
      + *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      + *     <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
      + * </intent-filter>
      + * 
      + *

      + * This filter describes the ability to return to the caller a note selected by + * the user without needing to know where it came from. The data type + * vnd.android.cursor.item/vnd.google.note is a URI from which a + * Cursor of exactly one (vnd.android.cursor.item) item can be + * retrieved which contains our note pad data (vnd.google.note). + * The GET_CONTENT action is similar to the PICK action, where the activity will + * return to its caller a piece of data selected by the user. Here, however, the + * caller specifies the type of data they desire instead of the type of data the + * user will be picking from. + *

      + *
    + * + *

    + * Given these capabilities, the following intents will resolve to the NotesList + * activity: + *

    + * + *
      + *
    • + *

      + * { action=android.app.action.MAIN } matches all of the activities that + * can be used as top-level entry points into an application. + *

      + *
    • + *

      + * { action=android.app.action.MAIN, category=android.app.category.LAUNCHER + * } is the actual intent used by the Launcher to populate its top-level + * list. + *

      + *
    • + *

      + * { action=android.intent.action.VIEW + * data=content://com.google.provider.NotePad/notes } displays a list of all + * the notes under "content://com.google.provider.NotePad/notes", which the user + * can browse through and see the details on. + *

      + *
    • + *

      + * { action=android.app.action.PICK + * data=content://com.google.provider.NotePad/notes } provides a list of the + * notes under "content://com.google.provider.NotePad/notes", from which the + * user can pick a note whose data URL is returned back to the caller. + *

      + *
    • + *

      + * { action=android.app.action.GET_CONTENT + * type=vnd.android.cursor.item/vnd.google.note } is similar to the pick + * action, but allows the caller to specify the kind of data they want back so + * that the system can find the appropriate activity to pick something of that + * data type. + *

      + *
    + * + *

    + * The second activity, com.android.notepad.NoteEditor, shows the + * user a single note entry and allows them to edit it. It can do two things as + * described by its two intent templates: + *

      + *
    1. + * + *
      + * <intent-filter android:label="@string/resolve_edit">
      + *     <action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" />
      + *     <action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" />
      + *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      + *     <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
      + * </intent-filter>
      + * 
      + *

      + * The first, primary, purpose of this activity is to let the user interact with + * a single note, as decribed by the MIME type + * vnd.android.cursor.item/vnd.google.note. The activity can either + * VIEW a note or allow the user to EDIT it. Again we support the DEFAULT + * category to allow the activity to be launched without explicitly specifying + * its component. + *

      + *
    2. + * + *
      + * <intent-filter>
      + *     <action android:name="{@link #ACTION_INSERT android.intent.action.INSERT}" />
      + *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
      + *     <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
      + * </intent-filter>
      + * 
      + *

      + * The secondary use of this activity is to insert a new note entry into an + * existing directory of notes. This is used when the user creates a new note: + * the INSERT action is executed on the directory of notes, causing this + * activity to run and have the user create the new note data which it then adds + * to the content provider. + *

      + *
    + * + *

    + * Given these capabilities, the following intents will resolve to the + * NoteEditor activity: + *

    + * + *
      + *
    • + *

      + * { action=android.intent.action.VIEW + * data=content://com.google.provider.NotePad/notes/{ID} } shows + * the user the content of note {ID}. + *

      + *
    • + *

      + * { action=android.app.action.EDIT + * data=content://com.google.provider.NotePad/notes/{ID} } allows + * the user to edit the content of note {ID}. + *

      + *
    • + *

      + * { action=android.app.action.INSERT + * data=content://com.google.provider.NotePad/notes } creates a new, empty + * note in the notes list at "content://com.google.provider.NotePad/notes" and + * allows the user to edit it. If they keep their changes, the URI of the newly + * created note is returned to the caller. + *

      + *
    + * + *

    + * The last activity, com.android.notepad.TitleEditor, allows the + * user to edit the title of a note. This could be implemented as a class that + * the application directly invokes (by explicitly setting its component in the + * Intent), but here we show a way you can publish alternative operations on + * existing data: + *

    + * + *
    + * <intent-filter android:label="@string/resolve_title">
    + *     <action android:name="com.android.notepad.action.EDIT_TITLE" />
    + *     <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" />
    + *     <category android:name="{@link #CATEGORY_ALTERNATIVE android.intent.category.ALTERNATIVE}" />
    + *     <category android:name="{@link #CATEGORY_SELECTED_ALTERNATIVE android.intent.category.SELECTED_ALTERNATIVE}" />
    + *     <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    + * </intent-filter>
    + * 
    + * + *

    + * In the single intent template here, we have created our own private action + * called com.android.notepad.action.EDIT_TITLE which means to edit + * the title of a note. It must be invoked on a specific note (data type + * vnd.android.cursor.item/vnd.google.note) like the previous view + * and edit actions, but here displays and edits the title contained in the note + * data. + * + *

    + * In addition to supporting the default category as usual, our title editor + * also supports two other standard categories: ALTERNATIVE and + * SELECTED_ALTERNATIVE. Implementing these categories allows others to find the + * special action it provides without directly knowing about it, through the + * {@link android.content.pm.PackageManager#queryIntentActivityOptions} method, + * or more often to build dynamic menu items with + * {@link android.view.Menu#addIntentOptions}. Note that in the intent template + * here was also supply an explicit name for the template (via + * android:label="@string/resolve_title") to better control what + * the user sees when presented with this activity as an alternative action to + * the data they are viewing. + * + *

    + * Given these capabilities, the following intent will resolve to the + * TitleEditor activity: + *

    + * + *
      + *
    • + *

      + * { action=com.android.notepad.action.EDIT_TITLE + * data=content://com.google.provider.NotePad/notes/{ID} } + * displays and allows the user to edit the title associated with note + * {ID}. + *

      + *
    + * + *

    Standard Activity Actions

    + * + *

    + * These are the current standard actions that Intent defines for launching + * activities (usually through {@link Context#startActivity}. The most + * important, and by far most frequently used, are {@link #ACTION_MAIN} and + * {@link #ACTION_EDIT}. + * + *

      + *
    • {@link #ACTION_MAIN} + *
    • {@link #ACTION_VIEW} + *
    • {@link #ACTION_ATTACH_DATA} + *
    • {@link #ACTION_EDIT} + *
    • {@link #ACTION_PICK} + *
    • {@link #ACTION_CHOOSER} + *
    • {@link #ACTION_GET_CONTENT} + *
    • {@link #ACTION_DIAL} + *
    • {@link #ACTION_CALL} + *
    • {@link #ACTION_SEND} + *
    • {@link #ACTION_SENDTO} + *
    • {@link #ACTION_ANSWER} + *
    • {@link #ACTION_INSERT} + *
    • {@link #ACTION_DELETE} + *
    • {@link #ACTION_RUN} + *
    • {@link #ACTION_SYNC} + *
    • {@link #ACTION_PICK_ACTIVITY} + *
    • {@link #ACTION_SEARCH} + *
    • {@link #ACTION_WEB_SEARCH} + *
    • {@link #ACTION_FACTORY_TEST} + *
    + * + *

    Standard Broadcast Actions

    + * + *

    + * These are the current standard actions that Intent defines for receiving + * broadcasts (usually through {@link Context#registerReceiver} or a + * <receiver> tag in a manifest). + * + *

      + *
    • {@link #ACTION_TIME_TICK} + *
    • {@link #ACTION_TIME_CHANGED} + *
    • {@link #ACTION_TIMEZONE_CHANGED} + *
    • {@link #ACTION_BOOT_COMPLETED} + *
    • {@link #ACTION_PACKAGE_ADDED} + *
    • {@link #ACTION_PACKAGE_CHANGED} + *
    • {@link #ACTION_PACKAGE_REMOVED} + *
    • {@link #ACTION_PACKAGE_RESTARTED} + *
    • {@link #ACTION_PACKAGE_DATA_CLEARED} + *
    • {@link #ACTION_PACKAGES_SUSPENDED} + *
    • {@link #ACTION_PACKAGES_UNSUSPENDED} + *
    • {@link #ACTION_UID_REMOVED} + *
    • {@link #ACTION_BATTERY_CHANGED} + *
    • {@link #ACTION_POWER_CONNECTED} + *
    • {@link #ACTION_POWER_DISCONNECTED} + *
    • {@link #ACTION_SHUTDOWN} + *
    + * + *

    Standard Categories

    + * + *

    + * These are the current standard categories that can be used to further clarify + * an Intent via {@link #addCategory}. + * + *

      + *
    • {@link #CATEGORY_DEFAULT} + *
    • {@link #CATEGORY_BROWSABLE} + *
    • {@link #CATEGORY_TAB} + *
    • {@link #CATEGORY_ALTERNATIVE} + *
    • {@link #CATEGORY_SELECTED_ALTERNATIVE} + *
    • {@link #CATEGORY_LAUNCHER} + *
    • {@link #CATEGORY_INFO} + *
    • {@link #CATEGORY_HOME} + *
    • {@link #CATEGORY_PREFERENCE} + *
    • {@link #CATEGORY_TEST} + *
    • {@link #CATEGORY_CAR_DOCK} + *
    • {@link #CATEGORY_DESK_DOCK} + *
    • {@link #CATEGORY_LE_DESK_DOCK} + *
    • {@link #CATEGORY_HE_DESK_DOCK} + *
    • {@link #CATEGORY_CAR_MODE} + *
    • {@link #CATEGORY_APP_MARKET} + *
    • {@link #CATEGORY_VR_HOME} + *
    + * + *

    Standard Extra Data

    + * + *

    + * These are the current standard fields that can be used as extra data via + * {@link #putExtra}. + * + *

      + *
    • {@link #EXTRA_ALARM_COUNT} + *
    • {@link #EXTRA_BCC} + *
    • {@link #EXTRA_CC} + *
    • {@link #EXTRA_CHANGED_COMPONENT_NAME} + *
    • {@link #EXTRA_DATA_REMOVED} + *
    • {@link #EXTRA_DOCK_STATE} + *
    • {@link #EXTRA_DOCK_STATE_HE_DESK} + *
    • {@link #EXTRA_DOCK_STATE_LE_DESK} + *
    • {@link #EXTRA_DOCK_STATE_CAR} + *
    • {@link #EXTRA_DOCK_STATE_DESK} + *
    • {@link #EXTRA_DOCK_STATE_UNDOCKED} + *
    • {@link #EXTRA_DONT_KILL_APP} + *
    • {@link #EXTRA_EMAIL} + *
    • {@link #EXTRA_INITIAL_INTENTS} + *
    • {@link #EXTRA_INTENT} + *
    • {@link #EXTRA_KEY_EVENT} + *
    • {@link #EXTRA_ORIGINATING_URI} + *
    • {@link #EXTRA_PHONE_NUMBER} + *
    • {@link #EXTRA_REFERRER} + *
    • {@link #EXTRA_REMOTE_INTENT_TOKEN} + *
    • {@link #EXTRA_REPLACING} + *
    • {@link #EXTRA_SHORTCUT_ICON} + *
    • {@link #EXTRA_SHORTCUT_ICON_RESOURCE} + *
    • {@link #EXTRA_SHORTCUT_INTENT} + *
    • {@link #EXTRA_STREAM} + *
    • {@link #EXTRA_SHORTCUT_NAME} + *
    • {@link #EXTRA_SUBJECT} + *
    • {@link #EXTRA_TEMPLATE} + *
    • {@link #EXTRA_TEXT} + *
    • {@link #EXTRA_TITLE} + *
    • {@link #EXTRA_UID} + *
    + * + *

    Flags

    + * + *

    + * These are the possible flags that can be used in the Intent via + * {@link #setFlags} and {@link #addFlags}. See {@link #setFlags} for a list of + * all possible flags. + */ +public class Intent implements Parcelable, Cloneable { + + /** + * Create an empty intent. + */ + public Intent() { + } + + /** + * Copy constructor. + */ + public Intent(Intent o) { + } + + /** + * Create an intent with a given action. All other fields (data, type, class) + * are null. Note that the action must be in a namespace because + * Intents are used globally in the system -- for example the system VIEW action + * is android.intent.action.VIEW; an application's custom action would be + * something like com.google.app.myapp.CUSTOM_ACTION. + * + * @param action The Intent action, such as ACTION_VIEW. + */ + public Intent(String action) { + } + + /** + * Create an intent with a given action and for a given data url. Note that the + * action must be in a namespace because Intents are used globally in + * the system -- for example the system VIEW action is + * android.intent.action.VIEW; an application's custom action would be something + * like com.google.app.myapp.CUSTOM_ACTION. + * + *

    + * Note: scheme and host name matching in the Android framework is + * case-sensitive, unlike the formal RFC. As a result, you should always ensure + * that you write your Uri with these elements using lower case letters, and + * normalize any Uris you receive from outside of Android to ensure the scheme + * and host is lower case. + *

    + * + * @param action The Intent action, such as ACTION_VIEW. + * @param uri The Intent data URI. + */ + public Intent(String action, Uri uri) { + } + + /** + * Create an intent for a specific component. All other fields (action, data, + * type, class) are null, though they can be modified later with explicit calls. + * This provides a convenient way to create an intent that is intended to + * execute a hard-coded class name, rather than relying on the system to find an + * appropriate class for you; see {@link #setComponent} for more information on + * the repercussions of this. + * + * @param packageContext A Context of the application package implementing this + * class. + * @param cls The component class that is to be used for the intent. + * + * @see #setClass + * @see #setComponent + * @see #Intent(String, android.net.Uri , Context, Class) + */ + public Intent(Context packageContext, Class cls) { + } + + /** + * Create an intent for a specific component with a specified action and data. + * This is equivalent to using {@link #Intent(String, android.net.Uri)} to + * construct the Intent and then calling {@link #setClass} to set its class. + * + *

    + * Note: scheme and host name matching in the Android framework is + * case-sensitive, unlike the formal RFC. As a result, you should always ensure + * that you write your Uri with these elements using lower case letters, and + * normalize any Uris you receive from outside of Android to ensure the scheme + * and host is lower case. + *

    + * + * @param action The Intent action, such as ACTION_VIEW. + * @param uri The Intent data URI. + * @param packageContext A Context of the application package implementing this + * class. + * @param cls The component class that is to be used for the intent. + * + * @see #Intent(String, android.net.Uri) + * @see #Intent(Context, Class) + * @see #setClass + * @see #setComponent + */ + public Intent(String action, Uri uri, Context packageContext, Class cls) { + } + + /** + * Call {@link #parseUri} with 0 flags. + * + * @deprecated Use {@link #parseUri} instead. + */ + @Deprecated + public static Intent getIntent(String uri) { + } + + /** + * Create an intent from a URI. This URI may encode the action, category, and + * other intent fields, if it was returned by {@link #toUri}. If the Intent was + * not generate by toUri(), its data will be the entire URI and its action will + * be ACTION_VIEW. + * + *

    + * The URI given here must not be relative -- that is, it must include the + * scheme and full path. + * + * @param uri The URI to turn into an Intent. + * @param flags Additional processing flags. + * + * @return Intent The newly created Intent object. + * + * @throws URISyntaxException Throws URISyntaxError if the basic URI syntax it + * bad (as parsed by the Uri class) or the Intent + * data within the URI is invalid. + * + * @see #toUri + */ + public static Intent parseUri(String uri, int flags) { + } + + /** + * Retrieve the general action to be performed, such as {@link #ACTION_VIEW}. + * The action describes the general way the rest of the information in the + * intent should be interpreted -- most importantly, what to do with the data + * returned by {@link #getData}. + * + * @return The action of this intent or null if none is specified. + * + * @see #setAction + */ + public String getAction() { + return null; + } + + /** + * Retrieve data this intent is operating on. This URI specifies the name of the + * data; often it uses the content: scheme, specifying data in a content + * provider. Other schemes may be handled by specific activities, such as http: + * by the web browser. + * + * @return The URI of the data this intent is targeting or null. + * + * @see #getScheme + * @see #setData + */ + public Uri getData() { + return null; + } + + /** + * The same as {@link #getData()}, but returns the URI as an encoded String. + */ + public String getDataString() { + return null; + } + + /** + * Return the scheme portion of the intent's data. If the data is null or does + * not include a scheme, null is returned. Otherwise, the scheme prefix without + * the final ':' is returned, i.e. "http". + * + *

    + * This is the same as calling getData().getScheme() (and checking for null + * data). + * + * @return The scheme of this intent. + * + * @see #getData + */ + public String getScheme() { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if + * none was found. + * + * @deprecated + * @hide + */ + @Deprecated + public Object getExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * @param defaultValue the value to be returned if no value of the desired type + * is stored with the given name. + * + * @return the value of an item previously added with putExtra(), or the default + * value if none was found. + * + * @see #putExtra(String, boolean) + */ + public boolean getBooleanExtra(String name, boolean defaultValue) { + return false; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * @param defaultValue the value to be returned if no value of the desired type + * is stored with the given name. + * + * @return the value of an item previously added with putExtra(), or the default + * value if none was found. + * + * @see #putExtra(String, byte) + */ + public byte getByteExtra(String name, byte defaultValue) { + return -1; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * @param defaultValue the value to be returned if no value of the desired type + * is stored with the given name. + * + * @return the value of an item previously added with putExtra(), or the default + * value if none was found. + * + * @see #putExtra(String, short) + */ + public short getShortExtra(String name, short defaultValue) { + return -1; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * @param defaultValue the value to be returned if no value of the desired type + * is stored with the given name. + * + * @return the value of an item previously added with putExtra(), or the default + * value if none was found. + * + * @see #putExtra(String, char) + */ + public char getCharExtra(String name, char defaultValue) { + return 'a'; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * @param defaultValue the value to be returned if no value of the desired type + * is stored with the given name. + * + * @return the value of an item previously added with putExtra(), or the default + * value if none was found. + * + * @see #putExtra(String, int) + */ + public int getIntExtra(String name, int defaultValue) { + return -1; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * @param defaultValue the value to be returned if no value of the desired type + * is stored with the given name. + * + * @return the value of an item previously added with putExtra(), or the default + * value if none was found. + * + * @see #putExtra(String, long) + */ + public long getLongExtra(String name, long defaultValue) { + return -1; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * @param defaultValue the value to be returned if no value of the desired type + * is stored with the given name. + * + * @return the value of an item previously added with putExtra(), or the default + * value if no such item is present + * + * @see #putExtra(String, float) + */ + public float getFloatExtra(String name, float defaultValue) { + return -1; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * @param defaultValue the value to be returned if no value of the desired type + * is stored with the given name. + * + * @return the value of an item previously added with putExtra(), or the default + * value if none was found. + * + * @see #putExtra(String, double) + */ + public double getDoubleExtra(String name, double defaultValue) { + return -1; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * String value was found. + * + * @see #putExtra(String, String) + */ + public String getStringExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * CharSequence value was found. + * + * @see #putExtra(String, CharSequence) + */ + public CharSequence getCharSequenceExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * Parcelable value was found. + * + * @see #putExtra(String, Parcelable) + */ + public T getParcelableExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * Parcelable[] value was found. + * + * @see #putExtra(String, Parcelable[]) + */ + public Parcelable[] getParcelableArrayExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with + * putParcelableArrayListExtra(), or null if no ArrayList + * value was found. + * + * @see #putParcelableArrayListExtra(String, ArrayList) + */ + public ArrayList getParcelableArrayListExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * Serializable value was found. + * + * @see #putExtra(String, Serializable) + */ + public Serializable getSerializableExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with + * putIntegerArrayListExtra(), or null if no ArrayList value + * was found. + * + * @see #putIntegerArrayListExtra(String, ArrayList) + */ + public ArrayList getIntegerArrayListExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putStringArrayListExtra(), + * or null if no ArrayList value was found. + * + * @see #putStringArrayListExtra(String, ArrayList) + */ + public ArrayList getStringArrayListExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with + * putCharSequenceArrayListExtra, or null if no ArrayList + * value was found. + * + * @see #putCharSequenceArrayListExtra(String, ArrayList) + */ + public ArrayList getCharSequenceArrayListExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * boolean array value was found. + * + * @see #putExtra(String, boolean[]) + */ + public boolean[] getBooleanArrayExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * byte array value was found. + * + * @see #putExtra(String, byte[]) + */ + public byte[] getByteArrayExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * short array value was found. + * + * @see #putExtra(String, short[]) + */ + public short[] getShortArrayExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * char array value was found. + * + * @see #putExtra(String, char[]) + */ + public char[] getCharArrayExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * int array value was found. + * + * @see #putExtra(String, int[]) + */ + public int[] getIntArrayExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * long array value was found. + * + * @see #putExtra(String, long[]) + */ + public long[] getLongArrayExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * float array value was found. + * + * @see #putExtra(String, float[]) + */ + public float[] getFloatArrayExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * double array value was found. + * + * @see #putExtra(String, double[]) + */ + public double[] getDoubleArrayExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * String array value was found. + * + * @see #putExtra(String, String[]) + */ + public String[] getStringArrayExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * CharSequence array value was found. + * + * @see #putExtra(String, CharSequence[]) + */ + public CharSequence[] getCharSequenceArrayExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * + * @return the value of an item previously added with putExtra(), or null if no + * Bundle value was found. + * + * @see #putExtra(String, Bundle) + */ + public Bundle getBundleExtra(String name) { + return null; + } + + /** + * Retrieve extended data from the intent. + * + * @param name The name of the desired item. + * @param defaultValue The default value to return in case no item is associated + * with the key 'name' + * + * @return the value of an item previously added with putExtra(), or + * defaultValue if none was found. + * + * @see #putExtra + * + * @deprecated + * @hide + */ + @Deprecated + public Object getExtra(String name, Object defaultValue) { + return null; + } + + /** + * Retrieves a map of extended data from the intent. + * + * @return the map of all extras previously added with putExtra(), or null if + * none have been added. + */ + public Bundle getExtras() { + return null; + } + + /** + * Filter extras to only basic types. + * + * @hide + */ + public void removeUnsafeExtras() { + } + + /** + * Set the general action to be performed. + * + * @param action An action name, such as ACTION_VIEW. Application-specific + * actions should be prefixed with the vendor's package name. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #getAction + */ + public Intent setAction(String action) { + return null; + } + + /** + * Set the data this intent is operating on. This method automatically clears + * any type that was previously set by {@link #setType} or + * {@link #setTypeAndNormalize}. + * + *

    + * Note: scheme matching in the Android framework is case-sensitive, unlike + * the formal RFC. As a result, you should always write your Uri with a lower + * case scheme, or use {@link Uri#normalizeScheme} or + * {@link #setDataAndNormalize} to ensure that the scheme is converted to lower + * case. + * + * @param data The Uri of the data this intent is now targeting. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #getData + * @see #setDataAndNormalize + * @see android.net.Uri#normalizeScheme() + */ + public Intent setData(Uri data) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The boolean data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getBooleanExtra(String, boolean) + */ + public Intent putExtra(String name, boolean value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The byte data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getByteExtra(String, byte) + */ + public Intent putExtra(String name, byte value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The char data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getCharExtra(String, char) + */ + public Intent putExtra(String name, char value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The short data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getShortExtra(String, short) + */ + public Intent putExtra(String name, short value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The integer data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getIntExtra(String, int) + */ + public Intent putExtra(String name, int value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The long data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getLongExtra(String, long) + */ + public Intent putExtra(String name, long value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The float data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getFloatExtra(String, float) + */ + public Intent putExtra(String name, float value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The double data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getDoubleExtra(String, double) + */ + public Intent putExtra(String name, double value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The String data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getStringExtra(String) + */ + public Intent putExtra(String name, String value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The CharSequence data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getCharSequenceExtra(String) + */ + public Intent putExtra(String name, CharSequence value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The Parcelable data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getParcelableExtra(String) + */ + public Intent putExtra(String name, Parcelable value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The Parcelable[] data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getParcelableArrayExtra(String) + */ + public Intent putExtra(String name, Parcelable[] value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The ArrayList data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getParcelableArrayListExtra(String) + */ + public Intent putParcelableArrayListExtra(String name, ArrayList value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The ArrayList data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getIntegerArrayListExtra(String) + */ + public Intent putIntegerArrayListExtra(String name, ArrayList value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The ArrayList data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getStringArrayListExtra(String) + */ + public Intent putStringArrayListExtra(String name, ArrayList value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The ArrayList data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getCharSequenceArrayListExtra(String) + */ + public Intent putCharSequenceArrayListExtra(String name, ArrayList value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The Serializable data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getSerializableExtra(String) + */ + public Intent putExtra(String name, Serializable value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The boolean array data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getBooleanArrayExtra(String) + */ + public Intent putExtra(String name, boolean[] value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The byte array data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getByteArrayExtra(String) + */ + public Intent putExtra(String name, byte[] value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The short array data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getShortArrayExtra(String) + */ + public Intent putExtra(String name, short[] value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The char array data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getCharArrayExtra(String) + */ + public Intent putExtra(String name, char[] value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The int array data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getIntArrayExtra(String) + */ + public Intent putExtra(String name, int[] value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The byte array data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getLongArrayExtra(String) + */ + public Intent putExtra(String name, long[] value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The float array data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getFloatArrayExtra(String) + */ + public Intent putExtra(String name, float[] value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The double array data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getDoubleArrayExtra(String) + */ + public Intent putExtra(String name, double[] value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The String array data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getStringArrayExtra(String) + */ + public Intent putExtra(String name, String[] value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The CharSequence array data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getCharSequenceArrayExtra(String) + */ + public Intent putExtra(String name, CharSequence[] value) { + return null; + } + + /** + * Add extended data to the intent. The name must include a package prefix, for + * example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param name The name of the extra data, with package prefix. + * @param value The Bundle data value. + * + * @return Returns the same Intent object, for chaining multiple calls into a + * single statement. + * + * @see #putExtras + * @see #removeExtra + * @see #getBundleExtra(String) + */ + public Intent putExtra(String name, Bundle value) { + return null; + } + + /** + * Copy all extras in 'src' in to this intent. + * + * @param src Contains the extras to copy. + * + * @see #putExtra + */ + public Intent putExtras(Intent src) { + return null; + } + + /** + * Add a set of extended data to the intent. The keys must include a package + * prefix, for example the app com.android.contacts would use names like + * "com.android.contacts.ShowAll". + * + * @param extras The Bundle of extras to add to this intent. + * + * @see #putExtra + * @see #removeExtra + */ + public Intent putExtras(Bundle extras) { + return null; + } + + /** + * Completely replace the extras in the Intent with the extras in the given + * Intent. + * + * @param src The exact extras contained in this Intent are copied into the + * target intent, replacing any that were previously there. + */ + public Intent replaceExtras(Intent src) { + return null; + } + + /** + * Completely replace the extras in the Intent with the given Bundle of extras. + * + * @param extras The new set of extras in the Intent, or null to erase all + * extras. + */ + public Intent replaceExtras(Bundle extras) { + return null; + } + + /** + * Remove extended data from the intent. + * + * @see #putExtra + */ + public void removeExtra(String name) { + } + + public void writeToParcel(Parcel out, int flags) { + } + + public void readFromParcel(Parcel in) { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/net/Uri.java b/java/ql/test/stubs/google-android-9.0.0/android/net/Uri.java new file mode 100644 index 00000000000..d5352f0f7ea --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/net/Uri.java @@ -0,0 +1,544 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net; + +import android.content.Intent; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.RandomAccess; +import java.util.Set; + +/** + * Immutable URI reference. A URI reference includes a URI and a fragment, the + * component of the URI following a '#'. Builds and parses URI references which + * conform to RFC 2396. + * + *

    + * In the interest of performance, this class performs little to no validation. + * Behavior is undefined for invalid input. This class is very forgiving--in the + * face of invalid input, it will return garbage rather than throw an exception + * unless otherwise specified. + */ +public abstract class Uri implements Parcelable, Comparable { + /* + * This class aims to do as little up front work as possible. To accomplish + * that, we vary the implementation depending on what the user passes in. For + * example, we have one implementation if the user passes in a URI string + * (StringUri) and another if the user passes in the individual components + * (OpaqueUri). Concurrency notes*: Like any truly immutable object, this class + * is safe for concurrent use. This class uses a caching pattern in some places + * where it doesn't use volatile or synchronized. This is safe to do with ints + * because getting or setting an int is atomic. It's safe to do with a String + * because the internal fields are final and the memory model guarantees other + * threads won't see a partially initialized instance. We are not guaranteed + * that some threads will immediately see changes from other threads on certain + * platforms, but we don't mind if those threads reconstruct the cached result. + * As a result, we get thread safe caching with no concurrency overhead, which + * means the most common case, access from a single thread, is as fast as + * possible. From the Java Language spec.: "17.5 Final Field Semantics ... when + * the object is seen by another thread, that thread will always see the + * correctly constructed version of that object's final fields. It will also see + * versions of any object or array referenced by those final fields that are at + * least as up-to-date as the final fields are." In that same vein, all + * non-transient fields within Uri implementations should be final and immutable + * so as to ensure true immutability for clients even when they don't use proper + * concurrency control. For reference, from RFC 2396: "4.3. Parsing a URI + * Reference A URI reference is typically parsed according to the four main + * components and fragment identifier in order to determine what components are + * present and whether the reference is relative or absolute. The individual + * components are then parsed for their subparts and, if not opaque, to verify + * their validity. Although the BNF defines what is allowed in each component, + * it is ambiguous in terms of differentiating between an authority component + * and a path component that begins with two slash characters. The greedy + * algorithm is used for disambiguation: the left-most matching rule soaks up as + * much of the URI reference string as it is capable of matching. In other + * words, the authority component wins." The "four main components" of a + * hierarchical URI consist of ://? + */ + + /** + * NOTE: EMPTY accesses this field during its own initialization, so this field + * *must* be initialized first, or else EMPTY will see a null value! + * + * Placeholder for strings which haven't been cached. This enables us to cache + * null. We intentionally create a new String instance so we can compare its + * identity and there is no chance we will confuse it with user data. + */ + + /** + * Returns true if this URI is hierarchical like "http://google.com". Absolute + * URIs are hierarchical if the scheme-specific part starts with a '/'. Relative + * URIs are always hierarchical. + */ + public abstract boolean isHierarchical(); + + /** + * Returns true if this URI is opaque like "mailto:nobody@google.com". The + * scheme-specific part of an opaque URI cannot start with a '/'. + */ + public boolean isOpaque() { + return false; + } + + /** + * Returns true if this URI is relative, i.e. if it doesn't contain an + * explicit scheme. + * + * @return true if this URI is relative, false if it's absolute + */ + public abstract boolean isRelative(); + + /** + * Returns true if this URI is absolute, i.e. if it contains an explicit + * scheme. + * + * @return true if this URI is absolute, false if it's relative + */ + public boolean isAbsolute() { + return !isRelative(); + } + + /** + * Gets the scheme of this URI. Example: "http" + * + * @return the scheme or null if this is a relative URI + */ + public abstract String getScheme(); + + /** + * Gets the scheme-specific part of this URI, i.e. everything between the + * scheme separator ':' and the fragment separator '#'. If this is a relative + * URI, this method returns the entire URI. Decodes escaped octets. + * + *

    + * Example: "//www.google.com/search?q=android" + * + * @return the decoded scheme-specific-part + */ + public abstract String getSchemeSpecificPart(); + + /** + * Gets the scheme-specific part of this URI, i.e. everything between the + * scheme separator ':' and the fragment separator '#'. If this is a relative + * URI, this method returns the entire URI. Leaves escaped octets intact. + * + *

    + * Example: "//www.google.com/search?q=android" + * + * @return the decoded scheme-specific-part + */ + public abstract String getEncodedSchemeSpecificPart(); + + /** + * Gets the decoded authority part of this URI. For server addresses, the + * authority is structured as follows: + * {@code [ userinfo '@' ] host [ ':' port ]} + * + *

    + * Examples: "google.com", "bob@google.com:80" + * + * @return the authority for this URI or null if not present + */ + public abstract String getAuthority(); + + /** + * Gets the encoded authority part of this URI. For server addresses, the + * authority is structured as follows: + * {@code [ userinfo '@' ] host [ ':' port ]} + * + *

    + * Examples: "google.com", "bob@google.com:80" + * + * @return the authority for this URI or null if not present + */ + public abstract String getEncodedAuthority(); + + /** + * Gets the decoded user information from the authority. For example, if the + * authority is "nobody@google.com", this method will return "nobody". + * + * @return the user info for this URI or null if not present + */ + public abstract String getUserInfo(); + + /** + * Gets the encoded user information from the authority. For example, if the + * authority is "nobody@google.com", this method will return "nobody". + * + * @return the user info for this URI or null if not present + */ + public abstract String getEncodedUserInfo(); + + /** + * Gets the encoded host from the authority for this URI. For example, if the + * authority is "bob@google.com", this method will return "google.com". + * + * @return the host for this URI or null if not present + */ + public abstract String getHost(); + + /** + * Gets the port from the authority for this URI. For example, if the authority + * is "google.com:80", this method will return 80. + * + * @return the port for this URI or -1 if invalid or not present + */ + public abstract int getPort(); + + /** + * Gets the decoded path. + * + * @return the decoded path, or null if this is not a hierarchical URI (like + * "mailto:nobody@google.com") or the URI is invalid + */ + public abstract String getPath(); + + /** + * Gets the encoded path. + * + * @return the encoded path, or null if this is not a hierarchical URI (like + * "mailto:nobody@google.com") or the URI is invalid + */ + public abstract String getEncodedPath(); + + /** + * Gets the decoded query component from this URI. The query comes after the + * query separator ('?') and before the fragment separator ('#'). This method + * would return "q=android" for "http://www.google.com/search?q=android". + * + * @return the decoded query or null if there isn't one + */ + public abstract String getQuery(); + + /** + * Gets the encoded query component from this URI. The query comes after the + * query separator ('?') and before the fragment separator ('#'). This method + * would return "q=android" for "http://www.google.com/search?q=android". + * + * @return the encoded query or null if there isn't one + */ + public abstract String getEncodedQuery(); + + /** + * Gets the decoded fragment part of this URI, everything after the '#'. + * + * @return the decoded fragment or null if there isn't one + */ + public abstract String getFragment(); + + /** + * Gets the encoded fragment part of this URI, everything after the '#'. + * + * @return the encoded fragment or null if there isn't one + */ + public abstract String getEncodedFragment(); + + /** + * Gets the decoded path segments. + * + * @return decoded path segments, each without a leading or trailing '/' + */ + public abstract List getPathSegments(); + + /** + * Gets the decoded last segment in the path. + * + * @return the decoded last segment or null if the path is empty + */ + public abstract String getLastPathSegment(); + + /** + * Compares this Uri to another object for equality. Returns true if the encoded + * string representations of this Uri and the given Uri are equal. Case counts. + * Paths are not normalized. If one Uri specifies a default port explicitly and + * the other leaves it implicit, they will not be considered equal. + */ + public boolean equals(Object o) { + return false; + } + + /** + * Hashes the encoded string represention of this Uri consistently with + * {@link #equals(Object)}. + */ + public int hashCode() { + return -1; + } + + /** + * Compares the string representation of this Uri with that of another. + */ + public int compareTo(Uri other) { + return -1; + } + + /** + * Returns the encoded string representation of this URI. Example: + * "http://google.com/" + */ + public abstract String toString(); + + /** + * Return a string representation of the URI that is safe to print to logs and + * other places where PII should be avoided. + * + * @hide + */ + public String toSafeString() { + return null; + } + + /** + * Creates a Uri which parses the given encoded URI string. + * + * @param uriString an RFC 2396-compliant, encoded URI + * @throws NullPointerException if uriString is null + * @return Uri for this given uri string + */ + public static Uri parse(String uriString) { + return null; + } + + /** + * Creates a Uri from a file. The URI has the form "file://". + * Encodes path characters with the exception of '/'. + * + *

    + * Example: "file:///tmp/android.txt" + * + * @throws NullPointerException if file is null + * @return a Uri for the given file + */ + public static Uri fromFile(File file) { + return null; + } + + /** + * Creates an opaque Uri from the given components. Encodes the ssp which means + * this method cannot be used to create hierarchical URIs. + * + * @param scheme of the URI + * @param ssp scheme-specific-part, everything between the scheme separator + * (':') and the fragment separator ('#'), which will get + * encoded + * @param fragment fragment, everything after the '#', null if undefined, will + * get encoded + * + * @throws NullPointerException if scheme or ssp is null + * @return Uri composed of the given scheme, ssp, and fragment + * + * @see Builder if you don't want the ssp and fragment to be encoded + */ + public static Uri fromParts(String scheme, String ssp, String fragment) { + return null; + } + + /** + * Returns a set of the unique names of all query parameters. Iterating over the + * set will return the names in order of their first occurrence. + * + * @throws UnsupportedOperationException if this isn't a hierarchical URI + * + * @return a set of decoded names + */ + public Set getQueryParameterNames() { + return null; + } + + /** + * Searches the query string for parameter values with the given key. + * + * @param key which will be encoded + * + * @throws UnsupportedOperationException if this isn't a hierarchical URI + * @throws NullPointerException if key is null + * @return a list of decoded values + */ + public List getQueryParameters(String key) { + return null; + } + + /** + * Searches the query string for the first value with the given key. + * + *

    + * Warning: Prior to Jelly Bean, this decoded the '+' character + * as '+' rather than ' '. + * + * @param key which will be encoded + * @throws UnsupportedOperationException if this isn't a hierarchical URI + * @throws NullPointerException if key is null + * @return the decoded value or null if no parameter is found + */ + public String getQueryParameter(String key) { + return null; + } + + /** + * Searches the query string for the first value with the given key and + * interprets it as a boolean value. "false" and "0" are interpreted as + * false, everything else is interpreted as true. + * + * @param key which will be decoded + * @param defaultValue the default value to return if there is no query + * parameter for key + * @return the boolean interpretation of the query parameter key + */ + public boolean getBooleanQueryParameter(String key, boolean defaultValue) { + return false; + } + + /** + * Return an equivalent URI with a lowercase scheme component. This aligns the + * Uri with Android best practices for intent filtering. + * + *

    + * For example, "HTTP://www.android.com" becomes "http://www.android.com" + * + *

    + * All URIs received from outside Android (such as user input, or external + * sources like Bluetooth, NFC, or the Internet) should be normalized before + * they are used to create an Intent. + * + *

    + * This method does not validate bad URI's, or 'fix' poorly formatted + * URI's - so do not use it for input validation. A Uri will always be returned, + * even if the Uri is badly formatted to begin with and a scheme component + * cannot be found. + * + * @return normalized Uri (never null) + * @see android.content.Intent#setData + * @see android.content.Intent#setDataAndNormalize + */ + public Uri normalizeScheme() { + return null; + } + + /** + * Writes a Uri to a Parcel. + * + * @param out parcel to write to + * @param uri to write, can be null + */ + public static void writeToParcel(Parcel out, Uri uri) { + } + + /** + * Encodes characters in the given string as '%'-escaped octets using the UTF-8 + * scheme. Leaves letters ("A-Z", "a-z"), numbers ("0-9"), and unreserved + * characters ("_-!.~'()*") intact. Encodes all other characters. + * + * @param s string to encode + * @return an encoded version of s suitable for use as a URI component, or null + * if s is null + */ + public static String encode(String s) { + return null; + } + + /** + * Encodes characters in the given string as '%'-escaped octets using the UTF-8 + * scheme. Leaves letters ("A-Z", "a-z"), numbers ("0-9"), and unreserved + * characters ("_-!.~'()*") intact. Encodes all other characters with the + * exception of those specified in the allow argument. + * + * @param s string to encode + * @param allow set of additional characters to allow in the encoded form, null + * if no characters should be skipped + * @return an encoded version of s suitable for use as a URI component, or null + * if s is null + */ + public static String encode(String s, String allow) { + return null; + } + + /** + * Decodes '%'-escaped octets in the given string using the UTF-8 scheme. + * Replaces invalid octets with the unicode replacement character ("\\uFFFD"). + * + * @param s encoded string to decode + * @return the given string with escaped octets decoded, or null if s is null + */ + public static String decode(String s) { + return null; + } + + /** + * Creates a new Uri by appending an already-encoded path segment to a base Uri. + * + * @param baseUri Uri to append path segment to + * @param pathSegment encoded path segment to append + * @return a new Uri based on baseUri with the given segment appended to the + * path + * @throws NullPointerException if baseUri is null + */ + public static Uri withAppendedPath(Uri baseUri, String pathSegment) { + return null; + } + + /** + * If this {@link Uri} is {@code file://}, then resolve and return its canonical + * path. Also fixes legacy emulated storage paths so they are usable across user + * boundaries. Should always be called from the app process before sending + * elsewhere. + * + * @hide + */ + public Uri getCanonicalUri() { + return null; + } + + /** + * If this is a {@code file://} Uri, it will be reported to {@link StrictMode}. + * + * @hide + */ + public void checkFileUriExposed(String location) { + } + + /** + * If this is a {@code content://} Uri without access flags, it will be reported + * to {@link StrictMode}. + * + * @hide + */ + public void checkContentUriWithoutPermission(String location, int flags) { + } + + /** + * Test if this is a path prefix match against the given Uri. Verifies that + * scheme, authority, and atomic path segments match. + * + * @hide + */ + public boolean isPathPrefixMatch(Uri prefix) { + return false; + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java b/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java new file mode 100644 index 00000000000..617ec97ae8c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/BaseBundle.java @@ -0,0 +1,677 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.os; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Set; + +/** + * A mapping from String keys to values of various types. In most cases, you + * should work directly with either the {@link Bundle} or + * {@link PersistableBundle} subclass. + */ +public class BaseBundle { + /** + * Constructs a new, empty Bundle that uses a specific ClassLoader for + * instantiating Parcelable and Serializable objects. + * + * @param loader An explicit ClassLoader to use when instantiating objects + * inside of the Bundle. + * @param capacity Initial size of the ArrayMap. + */ + BaseBundle(ClassLoader loader, int capacity) { + } + + /** + * Constructs a new, empty Bundle. + */ + BaseBundle() { + } + + /** + * Constructs a Bundle whose data is stored as a Parcel. The data will be + * unparcelled on first contact, using the assigned ClassLoader. + * + * @param parcelledData a Parcel containing a Bundle + */ + BaseBundle(Parcel parcelledData) { + } + + BaseBundle(Parcel parcelledData, int length) { + } + + /** + * Constructs a new, empty Bundle that uses a specific ClassLoader for + * instantiating Parcelable and Serializable objects. + * + * @param loader An explicit ClassLoader to use when instantiating objects + * inside of the Bundle. + */ + BaseBundle(ClassLoader loader) { + } + + /** + * Constructs a new, empty Bundle sized to hold the given number of elements. + * The Bundle will grow as needed. + * + * @param capacity the initial capacity of the Bundle + */ + BaseBundle(int capacity) { + } + + /** + * Constructs a Bundle containing a copy of the mappings from the given Bundle. + * + * @param b a Bundle to be copied. + */ + BaseBundle(BaseBundle b) { + } + + /** + * Special constructor that does not initialize the bundle. + */ + BaseBundle(boolean doInit) { + } + + /** + * TODO: optimize this later (getting just the value part of a Bundle with a + * single pair) once Bundle.forPair() above is implemented with a special + * single-value Map implementation/serialization. + * + * Note: value in single-pair Bundle may be null. + * + * @hide + */ + public String getPairValue() { + } + + /** + * Changes the ClassLoader this Bundle uses when instantiating objects. + * + * @param loader An explicit ClassLoader to use when instantiating objects + * inside of the Bundle. + */ + void setClassLoader(ClassLoader loader) { + } + + /** + * Return the ClassLoader currently associated with this Bundle. + */ + ClassLoader getClassLoader() { + return null; + } + + /** + * @hide + */ + public boolean isParcelled() { + return false; + } + + /** + * @hide + */ + public boolean isEmptyParcel() { + return false; + } + + /** + * Inserts an ArrayList value into the mapping of this Bundle, + * replacing any existing value for the given key. Either key or value may be + * null. + * + * @param key a String, or null + * @param value an ArrayList object, or null + */ + void putCharSequenceArrayList(String key, ArrayList value) { + } + + /** + * Inserts a Serializable value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a Serializable object, or null + */ + void putSerializable(String key, Serializable value) { + } + + /** + * Inserts a boolean array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a boolean array object, or null + */ + public void putBooleanArray(String key, boolean[] value) { + } + + /** + * Inserts a byte array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a byte array object, or null + */ + void putByteArray(String key, byte[] value) { + } + + /** + * Inserts a short array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a short array object, or null + */ + void putShortArray(String key, short[] value) { + } + + /** + * Inserts a char array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a char array object, or null + */ + void putCharArray(String key, char[] value) { + } + + /** + * Inserts an int array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value an int array object, or null + */ + public void putIntArray(String key, int[] value) { + } + + /** + * Inserts a long array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a long array object, or null + */ + public void putLongArray(String key, long[] value) { + } + + /** + * Inserts a float array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a float array object, or null + */ + void putFloatArray(String key, float[] value) { + } + + /** + * Inserts a double array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a double array object, or null + */ + public void putDoubleArray(String key, double[] value) { + } + + /** + * Inserts a String array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a String array object, or null + */ + public void putStringArray(String key, String[] value) { + } + + /** + * Inserts a CharSequence array value into the mapping of this Bundle, replacing + * any existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a CharSequence array object, or null + */ + void putCharSequenceArray(String key, CharSequence[] value) { + } + + /** + * Returns the value associated with the given key, or false if no mapping of + * the desired type exists for the given key. + * + * @param key a String + * @return a boolean value + */ + public boolean getBoolean(String key) { + return false; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return a boolean value + */ + public boolean getBoolean(String key, boolean defaultValue) { + return false; + } + + /** + * Returns the value associated with the given key, or (byte) 0 if no mapping of + * the desired type exists for the given key. + * + * @param key a String + * @return a byte value + */ + byte getByte(String key) { + return -1; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return a byte value + */ + Byte getByte(String key, byte defaultValue) { + return -1; + } + + /** + * Returns the value associated with the given key, or (char) 0 if no mapping of + * the desired type exists for the given key. + * + * @param key a String + * @return a char value + */ + char getChar(String key) { + return 'a'; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return a char value + */ + char getChar(String key, char defaultValue) { + return 'a'; + } + + /** + * Returns the value associated with the given key, or (short) 0 if no mapping + * of the desired type exists for the given key. + * + * @param key a String + * @return a short value + */ + short getShort(String key) { + return -1; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return a short value + */ + short getShort(String key, short defaultValue) { + return -1; + } + + /** + * Returns the value associated with the given key, or 0 if no mapping of the + * desired type exists for the given key. + * + * @param key a String + * @return an int value + */ + public int getInt(String key) { + return -1; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return an int value + */ + public int getInt(String key, int defaultValue) { + return -1; + } + + /** + * Returns the value associated with the given key, or 0L if no mapping of the + * desired type exists for the given key. + * + * @param key a String + * @return a long value + */ + public long getLong(String key) { + return -1; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return a long value + */ + public long getLong(String key, long defaultValue) { + return -1; + } + + /** + * Returns the value associated with the given key, or 0.0f if no mapping of the + * desired type exists for the given key. + * + * @param key a String + * @return a float value + */ + float getFloat(String key) { + return -1; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return a float value + */ + float getFloat(String key, float defaultValue) { + return -1; + } + + /** + * Returns the value associated with the given key, or 0.0 if no mapping of the + * desired type exists for the given key. + * + * @param key a String + * @return a double value + */ + public double getDouble(String key) { + return -1; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return a double value + */ + public double getDouble(String key, double defaultValue) { + return -1; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a String value, or null + */ + public String getString(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key or if a null value is + * explicitly associated with the given key. + * + * @param key a String, or null + * @param defaultValue Value to return if key does not exist or if a null value + * is associated with the given key. + * @return the String value associated with the given key, or defaultValue if no + * valid String object is currently mapped to that key. + */ + public String getString(String key, String defaultValue) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a CharSequence value, or null + */ + CharSequence getCharSequence(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key or if a null value is + * explicitly associated with the given key. + * + * @param key a String, or null + * @param defaultValue Value to return if key does not exist or if a null value + * is associated with the given key. + * @return the CharSequence value associated with the given key, or defaultValue + * if no valid CharSequence object is currently mapped to that key. + */ + CharSequence getCharSequence(String key, CharSequence defaultValue) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a Serializable value, or null + */ + Serializable getSerializable(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return an ArrayList value, or null + */ + ArrayList getIntegerArrayList(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return an ArrayList value, or null + */ + ArrayList getStringArrayList(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return an ArrayList value, or null + */ + ArrayList getCharSequenceArrayList(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a boolean[] value, or null + */ + public boolean[] getBooleanArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a byte[] value, or null + */ + byte[] getByteArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a short[] value, or null + */ + short[] getShortArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a char[] value, or null + */ + char[] getCharArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return an int[] value, or null + */ + public int[] getIntArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a long[] value, or null + */ + public long[] getLongArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a float[] value, or null + */ + float[] getFloatArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a double[] value, or null + */ + public double[] getDoubleArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a String[] value, or null + */ + public String[] getStringArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a CharSequence[] value, or null + */ + CharSequence[] getCharSequenceArray(String key) { + return null; + } + + /** + * Writes the Bundle contents to a Parcel, typically in order for it to be + * passed through an IBinder connection. + * + * @param parcel The parcel to copy this bundle to. + */ + void writeToParcelInner(Parcel parcel, int flags) { + } + + /** + * Reads the Parcel contents into this Bundle, typically in order for it to be + * passed through an IBinder connection. + * + * @param parcel The parcel to overwrite this bundle from. + */ + void readFromParcelInner(Parcel parcel) { + } + +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java new file mode 100644 index 00000000000..6c074780ee4 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.os; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * A mapping from String keys to various {@link Parcelable} values. + * + * @see PersistableBundle + */ +public final class Bundle extends BaseBundle implements Cloneable, Parcelable { + + /** + * Constructs a new, empty Bundle. + */ + public Bundle() { + super(); + } + + /** + * Removes all elements from the mapping of this Bundle. + */ + public void clear() { + } + + /** + * Removes any entry with the given key from the mapping of this Bundle. + * + * @param key a String key + */ + public void remove(String key) { + } + + /** + * Inserts all mappings from the given Bundle into this Bundle. + * + * @param bundle a Bundle + */ + public void putAll(Bundle bundle) { + } + + /** + * Return the size of {@link #mParcelledData} in bytes if available, otherwise + * {@code 0}. + * + * @hide + */ + public int getSize() { + return -1; + } + + /** + * Inserts a byte value into the mapping of this Bundle, replacing any existing + * value for the given key. + * + * @param key a String, or null + * @param value a byte + */ + public void putByte(String key, byte value) { + } + + /** + * Inserts a char value into the mapping of this Bundle, replacing any existing + * value for the given key. + * + * @param key a String, or null + * @param value a char + */ + public void putChar(String key, char value) { + } + + /** + * Inserts a short value into the mapping of this Bundle, replacing any existing + * value for the given key. + * + * @param key a String, or null + * @param value a short + */ + public void putShort(String key, short value) { + } + + /** + * Inserts a float value into the mapping of this Bundle, replacing any existing + * value for the given key. + * + * @param key a String, or null + * @param value a float + */ + public void putFloat(String key, float value) { + } + + /** + * Inserts a CharSequence value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a CharSequence, or null + */ + public void putCharSequence(String key, CharSequence value) { + } + + /** + * Inserts an ArrayList value into the mapping of this Bundle, + * replacing any existing value for the given key. Either key or value may be + * null. + * + * @param key a String, or null + * @param value an ArrayList object, or null + */ + public void putIntegerArrayList(String key, ArrayList value) { + } + + /** + * Inserts an ArrayList value into the mapping of this Bundle, replacing + * any existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value an ArrayList object, or null + */ + public void putStringArrayList(String key, ArrayList value) { + } + + /** + * Inserts an ArrayList value into the mapping of this Bundle, + * replacing any existing value for the given key. Either key or value may be + * null. + * + * @param key a String, or null + * @param value an ArrayList object, or null + */ + public void putCharSequenceArrayList(String key, ArrayList value) { + } + + /** + * Inserts a Serializable value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a Serializable object, or null + */ + public void putSerializable(String key, Serializable value) { + } + + /** + * Inserts a byte array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a byte array object, or null + */ + public void putByteArray(String key, byte[] value) { + } + + /** + * Inserts a short array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a short array object, or null + */ + public void putShortArray(String key, short[] value) { + } + + /** + * Inserts a char array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a char array object, or null + */ + public void putCharArray(String key, char[] value) { + } + + /** + * Inserts a float array value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a float array object, or null + */ + public void putFloatArray(String key, float[] value) { + } + + /** + * Inserts a CharSequence array value into the mapping of this Bundle, replacing + * any existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a CharSequence array object, or null + */ + public void putCharSequenceArray(String key, CharSequence[] value) { + } + + /** + * Inserts a Bundle value into the mapping of this Bundle, replacing any + * existing value for the given key. Either key or value may be null. + * + * @param key a String, or null + * @param value a Bundle object, or null + */ + public void putBundle(String key, Bundle value) { + } + + /** + * Returns the value associated with the given key, or (byte) 0 if no mapping of + * the desired type exists for the given key. + * + * @param key a String + * @return a byte value + */ + public byte getByte(String key) { + return -1; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return a byte value + */ + public Byte getByte(String key, byte defaultValue) { + return -1; + } + + /** + * Returns the value associated with the given key, or (char) 0 if no mapping of + * the desired type exists for the given key. + * + * @param key a String + * @return a char value + */ + public char getChar(String key) { + return 'a'; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return a char value + */ + public char getChar(String key, char defaultValue) { + return 'a'; + } + + /** + * Returns the value associated with the given key, or (short) 0 if no mapping + * of the desired type exists for the given key. + * + * @param key a String + * @return a short value + */ + public short getShort(String key) { + return -1; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return a short value + */ + public short getShort(String key, short defaultValue) { + return -1; + } + + /** + * Returns the value associated with the given key, or 0.0f if no mapping of the + * desired type exists for the given key. + * + * @param key a String + * @return a float value + */ + public float getFloat(String key) { + return -1; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key. + * + * @param key a String + * @param defaultValue Value to return if key does not exist + * @return a float value + */ + public float getFloat(String key, float defaultValue) { + return -1; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a CharSequence value, or null + */ + public CharSequence getCharSequence(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or defaultValue if no + * mapping of the desired type exists for the given key or if a null value is + * explicitly associatd with the given key. + * + * @param key a String, or null + * @param defaultValue Value to return if key does not exist or if a null value + * is associated with the given key. + * @return the CharSequence value associated with the given key, or defaultValue + * if no valid CharSequence object is currently mapped to that key. + */ + public CharSequence getCharSequence(String key, CharSequence defaultValue) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a Bundle value, or null + */ + public Bundle getBundle(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return an ArrayList value, or null + */ + public ArrayList getParcelableArrayList(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a Serializable value, or null + */ + public Serializable getSerializable(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return an ArrayList value, or null + */ + public ArrayList getIntegerArrayList(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return an ArrayList value, or null + */ + public ArrayList getStringArrayList(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return an ArrayList value, or null + */ + public ArrayList getCharSequenceArrayList(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a byte[] value, or null + */ + public byte[] getByteArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a short[] value, or null + */ + public short[] getShortArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a char[] value, or null + */ + public char[] getCharArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a float[] value, or null + */ + public float[] getFloatArray(String key) { + return null; + } + + /** + * Returns the value associated with the given key, or null if no mapping of the + * desired type exists for the given key or a null value is explicitly + * associated with the key. + * + * @param key a String, or null + * @return a CharSequence[] value, or null + */ + public CharSequence[] getCharSequenceArray(String key) { + return null; + } + + /** + * Writes the Bundle contents to a Parcel, typically in order for it to be + * passed through an IBinder connection. + * + * @param parcel The parcel to copy this bundle to. + */ + public void writeToParcel(Parcel parcel, int flags) { + } + + /** + * Reads the Parcel contents into this Bundle, typically in order for it to be + * passed through an IBinder connection. + * + * @param parcel The parcel to overwrite this bundle from. + */ + public void readFromParcel(Parcel parcel) { + } + + public synchronized String toString() { + return null; + } + + /** + * @hide + */ + public synchronized String toShortString() { + return null; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java new file mode 100644 index 00000000000..2670e258e4f --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.os; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.Serializable; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public final class Parcel { + + /** + * Retrieve a new Parcel object from the pool. + */ + public static Parcel obtain() { + return null; + } + + /** + * Write a byte array into the parcel at the current {@link #dataPosition}, + * growing {@link #dataCapacity} if needed. + * + * @param b Bytes to place into the parcel. + */ + public final void writeByteArray(byte[] b) { + } + + /** + * Write a byte array into the parcel at the current {@link #dataPosition}, + * growing {@link #dataCapacity} if needed. + * + * @param b Bytes to place into the parcel. + * @param offset Index of first byte to be written. + * @param len Number of bytes to write. + */ + public final void writeByteArray(byte[] b, int offset, int len) { + } + + /** + * Write a blob of data into the parcel at the current {@link #dataPosition}, + * growing {@link #dataCapacity} if needed. + * + * @param b Bytes to place into the parcel. {@hide} {@SystemApi} + */ + public final void writeBlob(byte[] b) { + } + + /** + * Write a blob of data into the parcel at the current {@link #dataPosition}, + * growing {@link #dataCapacity} if needed. + * + * @param b Bytes to place into the parcel. + * @param offset Index of first byte to be written. + * @param len Number of bytes to write. {@hide} {@SystemApi} + */ + public final void writeBlob(byte[] b, int offset, int len) { + } + + /** + * Write an integer value into the parcel at the current dataPosition(), growing + * dataCapacity() if needed. + */ + public final void writeInt(int val) { + } + + /** + * Write a long integer value into the parcel at the current dataPosition(), + * growing dataCapacity() if needed. + */ + public final void writeLong(long val) { + } + + /** + * Write a floating point value into the parcel at the current dataPosition(), + * growing dataCapacity() if needed. + */ + public final void writeFloat(float val) { + } + + /** + * Write a double precision floating point value into the parcel at the current + * dataPosition(), growing dataCapacity() if needed. + */ + public final void writeDouble(double val) { + } + + /** + * Write a string value into the parcel at the current dataPosition(), growing + * dataCapacity() if needed. + */ + public final void writeString(String val) { + } + + /** + * Write a string without going though a {@link ReadWriteHelper}. Subclasses of + * {@link ReadWriteHelper} must use this method instead of {@link #writeString} + * to avoid infinity recursive calls. + * + * @hide + */ + public void writeStringNoHelper(String val) { + } + + /** @hide */ + public final void writeBoolean(boolean val) { + } + + /** + * Write a CharSequence value into the parcel at the current dataPosition(), + * growing dataCapacity() if needed. + * + * @hide + */ + public final void writeCharSequence(CharSequence val) { + } + + /** + * Write a byte value into the parcel at the current dataPosition(), growing + * dataCapacity() if needed. + */ + public final void writeByte(byte val) { + } + + /** + * Please use {@link #writeBundle} instead. Flattens a Map into the parcel at + * the current dataPosition(), growing dataCapacity() if needed. The Map keys + * must be String objects. The Map values are written using {@link #writeValue} + * and must follow the specification there. + * + *

    + * It is strongly recommended to use {@link #writeBundle} instead of this + * method, since the Bundle class provides a type-safe API that allows you to + * avoid mysterious type errors at the point of marshalling. + */ + public final void writeMap(Map val) { + } + + /** + * Flatten a Bundle into the parcel at the current dataPosition(), growing + * dataCapacity() if needed. + */ + public final void writeBundle(Bundle val) { + } + + /** + * Flatten a List into the parcel at the current dataPosition(), growing + * dataCapacity() if needed. The List values are written using + * {@link #writeValue} and must follow the specification there. + */ + public final void writeList(List val) { + } + + /** + * Flatten an Object array into the parcel at the current dataPosition(), + * growing dataCapacity() if needed. The array values are written using + * {@link #writeValue} and must follow the specification there. + */ + public final void writeArray(Object[] val) { + } + + public final void writeBooleanArray(boolean[] val) { + } + + public final boolean[] createBooleanArray() { + return null; + } + + public final void readBooleanArray(boolean[] val) { + } + + public final void writeCharArray(char[] val) { + } + + public final char[] createCharArray() { + return null; + } + + public final void readCharArray(char[] val) { + } + + public final void writeIntArray(int[] val) { + } + + public final int[] createIntArray() { + return null; + } + + public final void readIntArray(int[] val) { + } + + public final void writeLongArray(long[] val) { + } + + public final long[] createLongArray() { + return null; + } + + public final void readLongArray(long[] val) { + } + + public final void writeFloatArray(float[] val) { + } + + public final float[] createFloatArray() { + } + + public final void readFloatArray(float[] val) { + } + + public final void writeDoubleArray(double[] val) { + } + + public final double[] createDoubleArray() { + return null; + } + + public final void readDoubleArray(double[] val) { + } + + public final void writeStringArray(String[] val) { + } + + public final String[] createStringArray() { + return null; + } + + public final void readStringArray(String[] val) { + } + + /** + * @hide + */ + public final void writeCharSequenceArray(CharSequence[] val) { + } + + /** + * @hide + */ + public final void writeCharSequenceList(ArrayList val) { + } + + /** + * Flatten a List containing String objects into the parcel, at the current + * dataPosition() and growing dataCapacity() if needed. They can later be + * retrieved with {@link #createStringArrayList} or {@link #readStringList}. + * + * @param val The list of strings to be written. + * + * @see #createStringArrayList + * @see #readStringList + */ + public final void writeStringList(List val) { + } + + /** + * Flatten a generic object in to a parcel. The given Object value may currently + * be one of the following types: + * + *

      + *
    • null + *
    • String + *
    • Byte + *
    • Short + *
    • Integer + *
    • Long + *
    • Float + *
    • Double + *
    • Boolean + *
    • String[] + *
    • boolean[] + *
    • byte[] + *
    • int[] + *
    • long[] + *
    • Object[] (supporting objects of the same type defined here). + *
    • {@link Bundle} + *
    • Map (as supported by {@link #writeMap}). + *
    • Any object that implements the {@link Parcelable} protocol. + *
    • Parcelable[] + *
    • CharSequence (as supported by {@link TextUtils#writeToParcel}). + *
    • List (as supported by {@link #writeList}). + *
    • {@link SparseArray} (as supported by + * {@link #writeSparseArray(SparseArray)}). + *
    • {@link IBinder} + *
    • Any object that implements Serializable (but see + * {@link #writeSerializable} for caveats). Note that all of the previous types + * have relatively efficient implementations for writing to a Parcel; having to + * rely on the generic serialization approach is much less efficient and should + * be avoided whenever possible. + *
    + * + *

    + * {@link Parcelable} objects are written with {@link Parcelable#writeToParcel} + * using contextual flags of 0. When serializing objects containing + * {@link ParcelFileDescriptor}s, this may result in file descriptor leaks when + * they are returned from Binder calls (where + * {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} should be used). + *

    + */ + public final void writeValue(Object v) { + } + + /** + * Flatten the name of the class of the Parcelable and its contents into the + * parcel. + * + * @param p The Parcelable object to be written. + * @param parcelableFlags Contextual flags as per + * {@link Parcelable#writeToParcel(Parcel, int) + * Parcelable.writeToParcel()}. + */ + public final void writeParcelable(Parcelable p, int parcelableFlags) { + } + + /** @hide */ + public final void writeParcelableCreator(Parcelable p) { + } + + /** + * Write a generic serializable object in to a Parcel. It is strongly + * recommended that this method be avoided, since the serialization overhead is + * extremely large, and this approach will be much slower than using the other + * approaches to writing data in to a Parcel. + */ + public final void writeSerializable(Serializable s) { + } + + /** + * Special function for writing an exception result at the header of a parcel, + * to be used when returning an exception from a transaction. Note that this + * currently only supports a few exception types; any other exception will be + * re-thrown by this function as a RuntimeException (to be caught by the + * system's last-resort exception handling when dispatching a transaction). + * + *

    + * The supported exception types are: + *

      + *
    • {@link BadParcelableException} + *
    • {@link IllegalArgumentException} + *
    • {@link IllegalStateException} + *
    • {@link NullPointerException} + *
    • {@link SecurityException} + *
    • {@link UnsupportedOperationException} + *
    • {@link NetworkOnMainThreadException} + *
    + * + * @param e The Exception to be written. + * + * @see #writeNoException + * @see #readException + */ + public final void writeException(Exception e) { + } + + /** + * Special function for writing information at the front of the Parcel + * indicating that no exception occurred. + * + * @see #writeException + * @see #readException + */ + public final void writeNoException() { + } + + /** + * Special function for reading an exception result from the header of a parcel, + * to be used after receiving the result of a transaction. This will throw the + * exception for you if it had been written to the Parcel, otherwise return and + * let you read the normal result data from the Parcel. + * + * @see #writeException + * @see #writeNoException + */ + public final void readException() { + } + + /** + * Parses the header of a Binder call's response Parcel and returns the + * exception code. Deals with lite or fat headers. In the common successful + * case, this header is generally zero. In less common cases, it's a small + * negative number and will be followed by an error string. + * + * This exists purely for android.database.DatabaseUtils and insulating it from + * having to handle fat headers as returned by e.g. StrictMode-induced RPC + * responses. + * + * @hide + */ + public final int readExceptionCode() { + return -1; + } + + /** + * Throw an exception with the given message. Not intended for use outside the + * Parcel class. + * + * @param code Used to determine which exception class to throw. + * @param msg The exception message. + */ + public final void readException(int code, String msg) { + } + + /** + * Read an integer value from the parcel at the current dataPosition(). + */ + public final int readInt() { + return -1; + } + + /** + * Read a long integer value from the parcel at the current dataPosition(). + */ + public final long readLong() { + return -1; + } + + /** + * Read a floating point value from the parcel at the current dataPosition(). + */ + public final float readFloat() { + return -1; + } + + /** + * Read a double precision floating point value from the parcel at the current + * dataPosition(). + */ + public final double readDouble() { + return -1; + } + + /** + * Read a string value from the parcel at the current dataPosition(). + */ + public final String readString() { + return null; + } + + /** @hide */ + public final boolean readBoolean() { + return false; + } + + /** + * Read a CharSequence value from the parcel at the current dataPosition(). + * + * @hide + */ + public final CharSequence readCharSequence() { + return null; + } + + /** + * Read a byte value from the parcel at the current dataPosition(). + */ + public final byte readByte() { + return -1; + } + + /** + * Please use {@link #readBundle(ClassLoader)} instead (whose data must have + * been written with {@link #writeBundle}. Read into an existing Map object from + * the parcel at the current dataPosition(). + */ + public final void readMap(Map outVal, ClassLoader loader) { + } + + /** + * Read into an existing List object from the parcel at the current + * dataPosition(), using the given class loader to load any enclosed + * Parcelables. If it is null, the default class loader is used. + */ + public final void readList(List outVal, ClassLoader loader) { + } + + /** + * Please use {@link #readBundle(ClassLoader)} instead (whose data must have + * been written with {@link #writeBundle}. Read and return a new HashMap object + * from the parcel at the current dataPosition(), using the given class loader + * to load any enclosed Parcelables. Returns null if the previously written map + * object was null. + */ + public final HashMap readHashMap(ClassLoader loader) { + return null; + } + + /** + * Read and return a new Bundle object from the parcel at the current + * dataPosition(). Returns null if the previously written Bundle object was + * null. + */ + public final Bundle readBundle() { + return null; + } + + /** + * Read and return a new Bundle object from the parcel at the current + * dataPosition(), using the given class loader to initialize the class loader + * of the Bundle for later retrieval of Parcelable objects. Returns null if the + * previously written Bundle object was null. + */ + public final Bundle readBundle(ClassLoader loader) { + return null; + } + + /** + * Read and return a byte[] object from the parcel. + */ + public final byte[] createByteArray() { + return null; + } + + /** + * Read a byte[] object from the parcel and copy it into the given byte array. + */ + public final void readByteArray(byte[] val) { + } + + /** + * Read a blob of data from the parcel and return it as a byte array. + * {@hide} {@SystemApi} + */ + public final byte[] readBlob() { + return null; + } + + /** + * Read and return a String[] object from the parcel. {@hide} + */ + public final String[] readStringArray() { + return null; + } + + /** + * Read and return a CharSequence[] object from the parcel. {@hide} + */ + public final CharSequence[] readCharSequenceArray() { + return null; + } + + /** + * Read and return an ArrayList<CharSequence> object from the parcel. + * {@hide} + */ + public final ArrayList readCharSequenceList() { + return null; + } + + /** + * Read and return a new ArrayList object from the parcel at the current + * dataPosition(). Returns null if the previously written list object was null. + * The given class loader will be used to load any enclosed Parcelables. + */ + public final ArrayList readArrayList(ClassLoader loader) { + return null; + } + + /** + * Read and return a new Object array from the parcel at the current + * dataPosition(). Returns null if the previously written array was null. The + * given class loader will be used to load any enclosed Parcelables. + */ + public final Object[] readArray(ClassLoader loader) { + return null; + } + + /** + * Read and return a new ArrayList containing String objects from the parcel + * that was written with {@link #writeStringList} at the current dataPosition(). + * Returns null if the previously written list object was null. + * + * @return A newly created ArrayList containing strings with the same data as + * those that were previously written. + * + * @see #writeStringList + */ + public final ArrayList createStringArrayList() { + return null; + } + + /** + * Read into the given List items String objects that were written with + * {@link #writeStringList} at the current dataPosition(). + * + * @see #writeStringList + */ + public final void readStringList(List list) { + } + + /** + * Read a typed object from a parcel. The given class loader will be used to + * load any enclosed Parcelables. If it is null, the default class loader will + * be used. + */ + public final Object readValue(ClassLoader loader) { + return null; + } + + /** + * Read and return a new Parcelable from the parcel. The given class loader will + * be used to load any enclosed Parcelables. If it is null, the default class + * loader will be used. + * + * @param loader A ClassLoader from which to instantiate the Parcelable object, + * or null for the default class loader. + * @return Returns the newly created Parcelable, or null if a null object has + * been written. + * @throws BadParcelableException Throws BadParcelableException if there was an + * error trying to instantiate the Parcelable. + */ + public final T readParcelable(ClassLoader loader) { + return null; + } + + /** + * Read and return a new Parcelable array from the parcel. The given class + * loader will be used to load any enclosed Parcelables. + * + * @return the Parcelable array, or null if the array is null + */ + public final Parcelable[] readParcelableArray(ClassLoader loader) { + return null; + } + + /** @hide */ + public final T[] readParcelableArray(ClassLoader loader, Class clazz) { + return null; + } + + /** + * Read and return a new Serializable object from the parcel. + * + * @return the Serializable object, or null if the Serializable name wasn't + * found in the parcel. + */ + public final Serializable readSerializable() { + return null; + } + + private final Serializable readSerializable(final ClassLoader loader) { + return null; + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java new file mode 100644 index 00000000000..ba9942dbb79 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.os; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Interface for classes whose instances can be written to and restored from a + * {@link Parcel}. Classes implementing the Parcelable interface must also have + * a non-null static field called CREATOR of a type that implements + * the {@link Parcelable.Creator} interface. + * + *

    + * A typical implementation of Parcelable is: + *

    + * + *
    + * public class MyParcelable implements Parcelable {
    + *     private int mData;
    + *
    + *     public int describeContents() {
    + *         return 0;
    + *     }
    + *
    + *     public void writeToParcel(Parcel out, int flags) {
    + *         out.writeInt(mData);
    + *     }
    + *
    + *     public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {
    + *         public MyParcelable createFromParcel(Parcel in) {
    + *             return new MyParcelable(in);
    + *         }
    + *
    + *         public MyParcelable[] newArray(int size) {
    + *             return new MyParcelable[size];
    + *         }
    + *     };
    + * 
    + *     private MyParcelable(Parcel in) {
    + *         mData = in.readInt();
    + *     }
    + * }
    + * 
    + */ +public interface Parcelable { + /** + * Flatten this object in to a Parcel. + * + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. May be + * 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. + */ + public void writeToParcel(Parcel dest, int flags); + + /** + * Specialization of {@link Creator} that allows you to receive the ClassLoader + * the object is being created in. + */ + public interface ClassLoaderCreator { + /** + * Create a new instance of the Parcelable class, instantiating it from the + * given Parcel whose data had previously been written by + * {@link Parcelable#writeToParcel Parcelable.writeToParcel()} and using the + * given ClassLoader. + * + * @param source The Parcel to read the object's data from. + * @param loader The ClassLoader that this object is being created in. + * @return Returns a new instance of the Parcelable class. + */ + public T createFromParcel(Parcel source, ClassLoader loader); + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/View.java b/java/ql/test/stubs/google-android-9.0.0/android/view/View.java new file mode 100644 index 00000000000..6940772eb6e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/View.java @@ -0,0 +1,682 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.view; + +import android.content.Context; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +/** + *

    + * This class represents the basic building block for user interface components. A View + * occupies a rectangular area on the screen and is responsible for drawing and + * event handling. View is the base class for widgets, which are + * used to create interactive UI components (buttons, text fields, etc.). The + * {@link android.view.ViewGroup} subclass is the base class for layouts, which + * are invisible containers that hold other Views (or other ViewGroups) and define + * their layout properties. + *

    + * + *
    + *

    Developer Guides

    + *

    For information about using this class to develop your application's user interface, + * read the User Interface developer guide. + *

    + * + * + *

    Using Views

    + *

    + * All of the views in a window are arranged in a single tree. You can add views + * either from code or by specifying a tree of views in one or more XML layout + * files. There are many specialized subclasses of views that act as controls or + * are capable of displaying text, images, or other content. + *

    + *

    + * Once you have created a tree of views, there are typically a few types of + * common operations you may wish to perform: + *

      + *
    • Set properties: for example setting the text of a + * {@link android.widget.TextView}. The available properties and the methods + * that set them will vary among the different subclasses of views. Note that + * properties that are known at build time can be set in the XML layout + * files.
    • + *
    • Set focus: The framework will handle moving focus in + * response to user input. To force focus to a specific view, call + * {@link #requestFocus}.
    • + *
    • Set up listeners: Views allow clients to set listeners + * that will be notified when something interesting happens to the view. For + * example, all views will let you set a listener to be notified when the view + * gains or loses focus. You can register such a listener using + * {@link #setOnFocusChangeListener(android.view.View.OnFocusChangeListener)}. + * Other view subclasses offer more specialized listeners. For example, a Button + * exposes a listener to notify clients when the button is clicked.
    • + *
    • Set visibility: You can hide or show views using + * {@link #setVisibility(int)}.
    • + *
    + *

    + *

    + * Note: The Android framework is responsible for measuring, laying out and + * drawing views. You should not call methods that perform these actions on + * views yourself unless you are actually implementing a + * {@link android.view.ViewGroup}. + *

    + * + * + *

    Implementing a Custom View

    + * + *

    + * To implement a custom view, you will usually begin by providing overrides for + * some of the standard methods that the framework calls on all views. You do + * not need to override all of these methods. In fact, you can start by just + * overriding {@link #onDraw(android.graphics.Canvas)}. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Category Methods Description
    CreationConstructorsThere is a form of the constructor that are called when the view + * is created from code and a form that is called when the view is + * inflated from a layout file. The second form should parse and apply + * any attributes defined in the layout file. + *
    {@link #onFinishInflate()}Called after a view and all of its children has been inflated + * from XML.
    Layout{@link #onMeasure(int, int)}Called to determine the size requirements for this view and all + * of its children. + *
    {@link #onLayout(boolean, int, int, int, int)}Called when this view should assign a size and position to all + * of its children. + *
    {@link #onSizeChanged(int, int, int, int)}Called when the size of this view has changed. + *
    Drawing{@link #onDraw(android.graphics.Canvas)}Called when the view should render its content. + *
    Event processing{@link #onKeyDown(int, KeyEvent)}Called when a new hardware key event occurs. + *
    {@link #onKeyUp(int, KeyEvent)}Called when a hardware key up event occurs. + *
    {@link #onTrackballEvent(MotionEvent)}Called when a trackball motion event occurs. + *
    {@link #onTouchEvent(MotionEvent)}Called when a touch screen motion event occurs. + *
    Focus{@link #onFocusChanged(boolean, int, android.graphics.Rect)}Called when the view gains or loses focus. + *
    {@link #onWindowFocusChanged(boolean)}Called when the window containing the view gains or loses focus. + *
    Attaching{@link #onAttachedToWindow()}Called when the view is attached to a window. + *
    {@link #onDetachedFromWindow}Called when the view is detached from its window. + *
    {@link #onWindowVisibilityChanged(int)}Called when the visibility of the window containing the view + * has changed. + *
    + *

    + * + * + *

    IDs

    + * Views may have an integer id associated with them. These ids are typically + * assigned in the layout XML files, and are used to find specific views within + * the view tree. A common pattern is to: + *
      + *
    • Define a Button in the layout file and assign it a unique ID. + *
      + * <Button
      + *     android:id="@+id/my_button"
      + *     android:layout_width="wrap_content"
      + *     android:layout_height="wrap_content"
      + *     android:text="@string/my_button_text"/>
      + * 
    • + *
    • From the onCreate method of an Activity, find the Button + *
      + *      Button myButton = findViewById(R.id.my_button);
      + * 
    • + *
    + *

    + * View IDs need not be unique throughout the tree, but it is good practice to + * ensure that they are at least unique within the part of the tree you are + * searching. + *

    + * + * + *

    Position

    + *

    + * The geometry of a view is that of a rectangle. A view has a location, + * expressed as a pair of left and top coordinates, and + * two dimensions, expressed as a width and a height. The unit for location + * and dimensions is the pixel. + *

    + * + *

    + * It is possible to retrieve the location of a view by invoking the methods + * {@link #getLeft()} and {@link #getTop()}. The former returns the left, or X, + * coordinate of the rectangle representing the view. The latter returns the + * top, or Y, coordinate of the rectangle representing the view. These methods + * both return the location of the view relative to its parent. For instance, + * when getLeft() returns 20, that means the view is located 20 pixels to the + * right of the left edge of its direct parent. + *

    + * + *

    + * In addition, several convenience methods are offered to avoid unnecessary + * computations, namely {@link #getRight()} and {@link #getBottom()}. + * These methods return the coordinates of the right and bottom edges of the + * rectangle representing the view. For instance, calling {@link #getRight()} + * is similar to the following computation: getLeft() + getWidth() + * (see Size for more information about the width.) + *

    + * + * + *

    Size, padding and margins

    + *

    + * The size of a view is expressed with a width and a height. A view actually + * possess two pairs of width and height values. + *

    + * + *

    + * The first pair is known as measured width and + * measured height. These dimensions define how big a view wants to be + * within its parent (see Layout for more details.) The + * measured dimensions can be obtained by calling {@link #getMeasuredWidth()} + * and {@link #getMeasuredHeight()}. + *

    + * + *

    + * The second pair is simply known as width and height, or + * sometimes drawing width and drawing height. These + * dimensions define the actual size of the view on screen, at drawing time and + * after layout. These values may, but do not have to, be different from the + * measured width and height. The width and height can be obtained by calling + * {@link #getWidth()} and {@link #getHeight()}. + *

    + * + *

    + * To measure its dimensions, a view takes into account its padding. The padding + * is expressed in pixels for the left, top, right and bottom parts of the view. + * Padding can be used to offset the content of the view by a specific amount of + * pixels. For instance, a left padding of 2 will push the view's content by + * 2 pixels to the right of the left edge. Padding can be set using the + * {@link #setPadding(int, int, int, int)} or {@link #setPaddingRelative(int, int, int, int)} + * method and queried by calling {@link #getPaddingLeft()}, {@link #getPaddingTop()}, + * {@link #getPaddingRight()}, {@link #getPaddingBottom()}, {@link #getPaddingStart()}, + * {@link #getPaddingEnd()}. + *

    + * + *

    + * Even though a view can define a padding, it does not provide any support for + * margins. However, view groups provide such a support. Refer to + * {@link android.view.ViewGroup} and + * {@link android.view.ViewGroup.MarginLayoutParams} for further information. + *

    + * + * + *

    Layout

    + *

    + * Layout is a two pass process: a measure pass and a layout pass. The measuring + * pass is implemented in {@link #measure(int, int)} and is a top-down traversal + * of the view tree. Each view pushes dimension specifications down the tree + * during the recursion. At the end of the measure pass, every view has stored + * its measurements. The second pass happens in + * {@link #layout(int,int,int,int)} and is also top-down. During + * this pass each parent is responsible for positioning all of its children + * using the sizes computed in the measure pass. + *

    + * + *

    + * When a view's measure() method returns, its {@link #getMeasuredWidth()} and + * {@link #getMeasuredHeight()} values must be set, along with those for all of + * that view's descendants. A view's measured width and measured height values + * must respect the constraints imposed by the view's parents. This guarantees + * that at the end of the measure pass, all parents accept all of their + * children's measurements. A parent view may call measure() more than once on + * its children. For example, the parent may measure each child once with + * unspecified dimensions to find out how big they want to be, then call + * measure() on them again with actual numbers if the sum of all the children's + * unconstrained sizes is too big or too small. + *

    + * + *

    + * The measure pass uses two classes to communicate dimensions. The + * {@link MeasureSpec} class is used by views to tell their parents how they + * want to be measured and positioned. The base LayoutParams class just + * describes how big the view wants to be for both width and height. For each + * dimension, it can specify one of: + *

      + *
    • an exact number + *
    • MATCH_PARENT, which means the view wants to be as big as its parent + * (minus padding) + *
    • WRAP_CONTENT, which means that the view wants to be just big enough to + * enclose its content (plus padding). + *
    + * There are subclasses of LayoutParams for different subclasses of ViewGroup. + * For example, AbsoluteLayout has its own subclass of LayoutParams which adds + * an X and Y value. + *

    + * + *

    + * MeasureSpecs are used to push requirements down the tree from parent to + * child. A MeasureSpec can be in one of three modes: + *

      + *
    • UNSPECIFIED: This is used by a parent to determine the desired dimension + * of a child view. For example, a LinearLayout may call measure() on its child + * with the height set to UNSPECIFIED and a width of EXACTLY 240 to find out how + * tall the child view wants to be given a width of 240 pixels. + *
    • EXACTLY: This is used by the parent to impose an exact size on the + * child. The child must use this size, and guarantee that all of its + * descendants will fit within this size. + *
    • AT_MOST: This is used by the parent to impose a maximum size on the + * child. The child must guarantee that it and all of its descendants will fit + * within this size. + *
    + *

    + * + *

    + * To initiate a layout, call {@link #requestLayout}. This method is typically + * called by a view on itself when it believes that is can no longer fit within + * its current bounds. + *

    + * + * + *

    Drawing

    + *

    + * Drawing is handled by walking the tree and recording the drawing commands of + * any View that needs to update. After this, the drawing commands of the + * entire tree are issued to screen, clipped to the newly damaged area. + *

    + * + *

    + * The tree is largely recorded and drawn in order, with parents drawn before + * (i.e., behind) their children, with siblings drawn in the order they appear + * in the tree. If you set a background drawable for a View, then the View will + * draw it before calling back to its onDraw() method. The child + * drawing order can be overridden with + * {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean) custom child drawing order} + * in a ViewGroup, and with {@link #setZ(float)} custom Z values} set on Views. + *

    + * + *

    + * To force a view to draw, call {@link #invalidate()}. + *

    + * + * + *

    Event Handling and Threading

    + *

    + * The basic cycle of a view is as follows: + *

      + *
    1. An event comes in and is dispatched to the appropriate view. The view + * handles the event and notifies any listeners.
    2. + *
    3. If in the course of processing the event, the view's bounds may need + * to be changed, the view will call {@link #requestLayout()}.
    4. + *
    5. Similarly, if in the course of processing the event the view's appearance + * may need to be changed, the view will call {@link #invalidate()}.
    6. + *
    7. If either {@link #requestLayout()} or {@link #invalidate()} were called, + * the framework will take care of measuring, laying out, and drawing the tree + * as appropriate.
    8. + *
    + *

    + * + *

    Note: The entire view tree is single threaded. You must always be on + * the UI thread when calling any method on any view. + * If you are doing work on other threads and want to update the state of a view + * from that thread, you should use a {@link Handler}. + *

    + * + * + *

    Focus Handling

    + *

    + * The framework will handle routine focus movement in response to user input. + * This includes changing the focus as views are removed or hidden, or as new + * views become available. Views indicate their willingness to take focus + * through the {@link #isFocusable} method. To change whether a view can take + * focus, call {@link #setFocusable(boolean)}. When in touch mode (see notes below) + * views indicate whether they still would like focus via {@link #isFocusableInTouchMode} + * and can change this via {@link #setFocusableInTouchMode(boolean)}. + *

    + *

    + * Focus movement is based on an algorithm which finds the nearest neighbor in a + * given direction. In rare cases, the default algorithm may not match the + * intended behavior of the developer. In these situations, you can provide + * explicit overrides by using these XML attributes in the layout file: + *

    + * nextFocusDown
    + * nextFocusLeft
    + * nextFocusRight
    + * nextFocusUp
    + * 
    + *

    + * + * + *

    + * To get a particular view to take focus, call {@link #requestFocus()}. + *

    + * + * + *

    Touch Mode

    + *

    + * When a user is navigating a user interface via directional keys such as a D-pad, it is + * necessary to give focus to actionable items such as buttons so the user can see + * what will take input. If the device has touch capabilities, however, and the user + * begins interacting with the interface by touching it, it is no longer necessary to + * always highlight, or give focus to, a particular view. This motivates a mode + * for interaction named 'touch mode'. + *

    + *

    + * For a touch capable device, once the user touches the screen, the device + * will enter touch mode. From this point onward, only views for which + * {@link #isFocusableInTouchMode} is true will be focusable, such as text editing widgets. + * Other views that are touchable, like buttons, will not take focus when touched; they will + * only fire the on click listeners. + *

    + *

    + * Any time a user hits a directional key, such as a D-pad direction, the view device will + * exit touch mode, and find a view to take focus, so that the user may resume interacting + * with the user interface without touching the screen again. + *

    + *

    + * The touch mode state is maintained across {@link android.app.Activity}s. Call + * {@link #isInTouchMode} to see whether the device is currently in touch mode. + *

    + * + * + *

    Scrolling

    + *

    + * The framework provides basic support for views that wish to internally + * scroll their content. This includes keeping track of the X and Y scroll + * offset as well as mechanisms for drawing scrollbars. See + * {@link #scrollBy(int, int)}, {@link #scrollTo(int, int)}, and + * {@link #awakenScrollBars()} for more details. + *

    + * + * + *

    Tags

    + *

    + * Unlike IDs, tags are not used to identify views. Tags are essentially an + * extra piece of information that can be associated with a view. They are most + * often used as a convenience to store data related to views in the views + * themselves rather than by putting them in a separate structure. + *

    + *

    + * Tags may be specified with character sequence values in layout XML as either + * a single tag using the {@link android.R.styleable#View_tag android:tag} + * attribute or multiple tags using the {@code } child element: + *

    + *     <View ...
    + *           android:tag="@string/mytag_value" />
    + *     <View ...>
    + *         <tag android:id="@+id/mytag"
    + *              android:value="@string/mytag_value" />
    + *     </View>
    + * 
    + *

    + *

    + * Tags may also be specified with arbitrary objects from code using + * {@link #setTag(Object)} or {@link #setTag(int, Object)}. + *

    + * + * + *

    Themes

    + *

    + * By default, Views are created using the theme of the Context object supplied + * to their constructor; however, a different theme may be specified by using + * the {@link android.R.styleable#View_theme android:theme} attribute in layout + * XML or by passing a {@link ContextThemeWrapper} to the constructor from + * code. + *

    + *

    + * When the {@link android.R.styleable#View_theme android:theme} attribute is + * used in XML, the specified theme is applied on top of the inflation + * context's theme (see {@link LayoutInflater}) and used for the view itself as + * well as any child elements. + *

    + *

    + * In the following example, both views will be created using the Material dark + * color scheme; however, because an overlay theme is used which only defines a + * subset of attributes, the value of + * {@link android.R.styleable#Theme_colorAccent android:colorAccent} defined on + * the inflation context's theme (e.g. the Activity theme) will be preserved. + *

    + *     <LinearLayout
    + *             ...
    + *             android:theme="@android:theme/ThemeOverlay.Material.Dark">
    + *         <View ...>
    + *     </LinearLayout>
    + * 
    + *

    + * + * + *

    Properties

    + *

    + * The View class exposes an {@link #ALPHA} property, as well as several transform-related + * properties, such as {@link #TRANSLATION_X} and {@link #TRANSLATION_Y}. These properties are + * available both in the {@link Property} form as well as in similarly-named setter/getter + * methods (such as {@link #setAlpha(float)} for {@link #ALPHA}). These properties can + * be used to set persistent state associated with these rendering-related properties on the view. + * The properties and methods can also be used in conjunction with + * {@link android.animation.Animator Animator}-based animations, described more in the + * Animation section. + *

    + * + * + *

    Animation

    + *

    + * Starting with Android 3.0, the preferred way of animating views is to use the + * {@link android.animation} package APIs. These {@link android.animation.Animator Animator}-based + * classes change actual properties of the View object, such as {@link #setAlpha(float) alpha} and + * {@link #setTranslationX(float) translationX}. This behavior is contrasted to that of the pre-3.0 + * {@link android.view.animation.Animation Animation}-based classes, which instead animate only + * how the view is drawn on the display. In particular, the {@link ViewPropertyAnimator} class + * makes animating these View properties particularly easy and efficient. + *

    + *

    + * Alternatively, you can use the pre-3.0 animation classes to animate how Views are rendered. + * You can attach an {@link Animation} object to a view using + * {@link #setAnimation(Animation)} or + * {@link #startAnimation(Animation)}. The animation can alter the scale, + * rotation, translation and alpha of a view over time. If the animation is + * attached to a view that has children, the animation will affect the entire + * subtree rooted by that node. When an animation is started, the framework will + * take care of redrawing the appropriate views until the animation completes. + *

    + * + * + *

    Security

    + *

    + * Sometimes it is essential that an application be able to verify that an action + * is being performed with the full knowledge and consent of the user, such as + * granting a permission request, making a purchase or clicking on an advertisement. + * Unfortunately, a malicious application could try to spoof the user into + * performing these actions, unaware, by concealing the intended purpose of the view. + * As a remedy, the framework offers a touch filtering mechanism that can be used to + * improve the security of views that provide access to sensitive functionality. + *

    + * To enable touch filtering, call {@link #setFilterTouchesWhenObscured(boolean)} or set the + * android:filterTouchesWhenObscured layout attribute to true. When enabled, the framework + * will discard touches that are received whenever the view's window is obscured by + * another visible window. As a result, the view will not receive touches whenever a + * toast, dialog or other window appears above the view's window. + *

    + * For more fine-grained control over security, consider overriding the + * {@link #onFilterTouchEventForSecurity(MotionEvent)} method to implement your own + * security policy. See also {@link MotionEvent#FLAG_WINDOW_IS_OBSCURED}. + *

    + * + * @attr ref android.R.styleable#View_accessibilityHeading + * @attr ref android.R.styleable#View_alpha + * @attr ref android.R.styleable#View_background + * @attr ref android.R.styleable#View_clickable + * @attr ref android.R.styleable#View_contentDescription + * @attr ref android.R.styleable#View_drawingCacheQuality + * @attr ref android.R.styleable#View_duplicateParentState + * @attr ref android.R.styleable#View_id + * @attr ref android.R.styleable#View_requiresFadingEdge + * @attr ref android.R.styleable#View_fadeScrollbars + * @attr ref android.R.styleable#View_fadingEdgeLength + * @attr ref android.R.styleable#View_filterTouchesWhenObscured + * @attr ref android.R.styleable#View_fitsSystemWindows + * @attr ref android.R.styleable#View_isScrollContainer + * @attr ref android.R.styleable#View_focusable + * @attr ref android.R.styleable#View_focusableInTouchMode + * @attr ref android.R.styleable#View_focusedByDefault + * @attr ref android.R.styleable#View_hapticFeedbackEnabled + * @attr ref android.R.styleable#View_keepScreenOn + * @attr ref android.R.styleable#View_keyboardNavigationCluster + * @attr ref android.R.styleable#View_layerType + * @attr ref android.R.styleable#View_layoutDirection + * @attr ref android.R.styleable#View_longClickable + * @attr ref android.R.styleable#View_minHeight + * @attr ref android.R.styleable#View_minWidth + * @attr ref android.R.styleable#View_nextClusterForward + * @attr ref android.R.styleable#View_nextFocusDown + * @attr ref android.R.styleable#View_nextFocusLeft + * @attr ref android.R.styleable#View_nextFocusRight + * @attr ref android.R.styleable#View_nextFocusUp + * @attr ref android.R.styleable#View_onClick + * @attr ref android.R.styleable#View_outlineSpotShadowColor + * @attr ref android.R.styleable#View_outlineAmbientShadowColor + * @attr ref android.R.styleable#View_padding + * @attr ref android.R.styleable#View_paddingHorizontal + * @attr ref android.R.styleable#View_paddingVertical + * @attr ref android.R.styleable#View_paddingBottom + * @attr ref android.R.styleable#View_paddingLeft + * @attr ref android.R.styleable#View_paddingRight + * @attr ref android.R.styleable#View_paddingTop + * @attr ref android.R.styleable#View_paddingStart + * @attr ref android.R.styleable#View_paddingEnd + * @attr ref android.R.styleable#View_saveEnabled + * @attr ref android.R.styleable#View_rotation + * @attr ref android.R.styleable#View_rotationX + * @attr ref android.R.styleable#View_rotationY + * @attr ref android.R.styleable#View_scaleX + * @attr ref android.R.styleable#View_scaleY + * @attr ref android.R.styleable#View_scrollX + * @attr ref android.R.styleable#View_scrollY + * @attr ref android.R.styleable#View_scrollbarSize + * @attr ref android.R.styleable#View_scrollbarStyle + * @attr ref android.R.styleable#View_scrollbars + * @attr ref android.R.styleable#View_scrollbarDefaultDelayBeforeFade + * @attr ref android.R.styleable#View_scrollbarFadeDuration + * @attr ref android.R.styleable#View_scrollbarTrackHorizontal + * @attr ref android.R.styleable#View_scrollbarThumbHorizontal + * @attr ref android.R.styleable#View_scrollbarThumbVertical + * @attr ref android.R.styleable#View_scrollbarTrackVertical + * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack + * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack + * @attr ref android.R.styleable#View_stateListAnimator + * @attr ref android.R.styleable#View_transitionName + * @attr ref android.R.styleable#View_soundEffectsEnabled + * @attr ref android.R.styleable#View_tag + * @attr ref android.R.styleable#View_textAlignment + * @attr ref android.R.styleable#View_textDirection + * @attr ref android.R.styleable#View_transformPivotX + * @attr ref android.R.styleable#View_transformPivotY + * @attr ref android.R.styleable#View_translationX + * @attr ref android.R.styleable#View_translationY + * @attr ref android.R.styleable#View_translationZ + * @attr ref android.R.styleable#View_visibility + * @attr ref android.R.styleable#View_theme + * + * @see android.view.ViewGroup + */ +public class View { + + /** + * Simple constructor to use when creating a view from code. + * + * @param context The Context the view is running in, through which it can + * access the current theme, resources, etc. + */ + public View(Context context) { + } + + + + + + + + + +} \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java new file mode 100644 index 00000000000..9732ca67ae0 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.webkit; + +import android.net.Uri; +import java.util.Map; + +/** + * Encompasses parameters to the {@link WebViewClient#shouldInterceptRequest} + * method. + */ +public interface WebResourceRequest { + /** + * Gets the URL for which the resource request was made. + * + * @return the URL for which the resource request was made. + */ + Uri getUrl(); + + /** + * Gets whether the request was made for the main frame. + * + * @return whether the request was made for the main frame. Will be + * {@code false} for iframes, for example. + */ + boolean isForMainFrame(); + + /** + * Gets whether the request was a result of a server-side redirect. + * + * @return whether the request was a result of a server-side redirect. + */ + boolean isRedirect(); + + /** + * Gets whether a gesture (such as a click) was associated with the request. For + * security reasons in certain situations this method may return {@code false} + * even though the sequence of events which caused the request to be created was + * initiated by a user gesture. + * + * @return whether a gesture was associated with the request. + */ + boolean hasGesture(); + + /** + * Gets the method associated with the request, for example "GET". + * + * @return the method associated with the request. + */ + String getMethod(); + + /** + * Gets the headers associated with the request. These are represented as a + * mapping of header name to header value. + * + * @return the headers associated with the request. + */ + Map getRequestHeaders(); +} \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java new file mode 100644 index 00000000000..0df04265046 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.webkit; + +import java.io.InputStream; +import java.io.StringBufferInputStream; +import java.util.Map; + +/** + * Encapsulates a resource response. Applications can return an instance of this + * class from {@link WebViewClient#shouldInterceptRequest} to provide a custom + * response when the WebView requests a particular resource. + */ +public class WebResourceResponse { + /** + * Constructs a resource response with the given MIME type, encoding, and input + * stream. Callers must implement {@link InputStream#read(byte[]) + * InputStream.read(byte[])} for the input stream. + * + * @param mimeType the resource response's MIME type, for example text/html + * @param encoding the resource response's encoding + * @param data the input stream that provides the resource response's data. + * Must not be a StringBufferInputStream. + */ + public WebResourceResponse(String mimeType, String encoding, InputStream data) { + } + + /** + * Constructs a resource response with the given parameters. Callers must + * implement {@link InputStream#read(byte[]) InputStream.read(byte[])} for the + * input stream. + * + * @param mimeType the resource response's MIME type, for example + * text/html + * @param encoding the resource response's encoding + * @param statusCode the status code needs to be in the ranges [100, 299], + * [400, 599]. Causing a redirect by specifying a 3xx + * code is not supported. + * @param reasonPhrase the phrase describing the status code, for example + * "OK". Must be non-empty. + * @param responseHeaders the resource response's headers represented as a + * mapping of header name -> header value. + * @param data the input stream that provides the resource response's + * data. Must not be a StringBufferInputStream. + */ + public WebResourceResponse(String mimeType, String encoding, int statusCode, String reasonPhrase, + Map responseHeaders, InputStream data) { + } + + /** + * Sets the resource response's MIME type, for example "text/html". + * + * @param mimeType The resource response's MIME type + */ + public void setMimeType(String mimeType) { + } + + /** + * Gets the resource response's MIME type. + * + * @return The resource response's MIME type + */ + public String getMimeType() { + return null; + } + + /** + * Sets the resource response's encoding, for example "UTF-8". This is + * used to decode the data from the input stream. + * + * @param encoding The resource response's encoding + */ + public void setEncoding(String encoding) { + } + + /** + * Gets the resource response's encoding. + * + * @return The resource response's encoding + */ + public String getEncoding() { + return null; + } + + /** + * Sets the resource response's status code and reason phrase. + * + * @param statusCode the status code needs to be in the ranges [100, 299], + * [400, 599]. Causing a redirect by specifying a 3xx code + * is not supported. + * @param reasonPhrase the phrase describing the status code, for example "OK". + * Must be non-empty. + */ + public void setStatusCodeAndReasonPhrase(int statusCode, String reasonPhrase) { + } + + /** + * Gets the resource response's status code. + * + * @return The resource response's status code. + */ + public int getStatusCode() { + return -1; + } + + /** + * Gets the description of the resource response's status code. + * + * @return The description of the resource response's status code. + */ + public String getReasonPhrase() { + return null; + } + + /** + * Sets the headers for the resource response. + * + * @param headers Mapping of header name -> header value. + */ + public void setResponseHeaders(Map headers) { + } + + /** + * Gets the headers for the resource response. + * + * @return The headers for the resource response. + */ + public Map getResponseHeaders() { + return null; + } + + /** + * Sets the input stream that provides the resource response's data. Callers + * must implement {@link InputStream#read(byte[]) InputStream.read(byte[])}. + * + * @param data the input stream that provides the resource response's data. Must + * not be a StringBufferInputStream. + */ + public void setData(InputStream data) { + } + + /** + * Gets the input stream that provides the resource response's data. + * + * @return The input stream that provides the resource response's data + */ + public InputStream getData() { + return null; + } + + /** + * The internal version of the constructor that doesn't perform arguments + * checks. + * + * @hide + */ + public WebResourceResponse(boolean immutable, String mimeType, String encoding, int statusCode, String reasonPhrase, + Map responseHeaders, InputStream data) { + } + +} \ No newline at end of file diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java new file mode 100644 index 00000000000..e6a7d5d7050 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java @@ -0,0 +1,1382 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.webkit; + +import android.content.Context; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Manages settings state for a WebView. When a WebView is first created, it + * obtains a set of default settings. These default settings will be returned + * from any getter call. A {@code WebSettings} object obtained from + * {@link WebView#getSettings()} is tied to the life of the WebView. If a + * WebView has been destroyed, any method call on {@code WebSettings} will throw + * an {@link IllegalStateException}. + */ +// This is an abstract base class: concrete WebViewProviders must +// create a class derived from this, and return an instance of it in the +// WebViewProvider.getWebSettingsProvider() method implementation. +public abstract class WebSettings { + /** + * Enum for controlling the layout of html. + *
      + *
    • {@code NORMAL} means no rendering changes. This is the recommended choice + * for maximum compatibility across different platforms and Android + * versions.
    • + *
    • {@code SINGLE_COLUMN} moves all content into one column that is the width + * of the view.
    • + *
    • {@code NARROW_COLUMNS} makes all columns no wider than the screen if + * possible. Only use this for API levels prior to + * {@link android.os.Build.VERSION_CODES#KITKAT}.
    • + *
    • {@code TEXT_AUTOSIZING} boosts font size of paragraphs based on + * heuristics to make the text readable when viewing a wide-viewport layout in + * the overview mode. It is recommended to enable zoom support + * {@link #setSupportZoom} when using this mode. Supported from API level + * {@link android.os.Build.VERSION_CODES#KITKAT}
    • + *
    + */ + // XXX: These must match LayoutAlgorithm in Settings.h in WebCore. + public enum LayoutAlgorithm { + NORMAL, + /** + * @deprecated This algorithm is now obsolete. + */ + @Deprecated + SINGLE_COLUMN, + /** + * @deprecated This algorithm is now obsolete. + */ + @Deprecated + NARROW_COLUMNS, TEXT_AUTOSIZING + } + + /** + * Enum for specifying the text size. + *
      + *
    • SMALLEST is 50%
    • + *
    • SMALLER is 75%
    • + *
    • NORMAL is 100%
    • + *
    • LARGER is 150%
    • + *
    • LARGEST is 200%
    • + *
    + * + * @deprecated Use {@link WebSettings#setTextZoom(int)} and + * {@link WebSettings#getTextZoom()} instead. + */ + @Deprecated + public enum TextSize { + SMALLEST(50), SMALLER(75), NORMAL(100), LARGER(150), LARGEST(200); + + TextSize(int size) { + value = size; + } + + int value; + } + + /** + * Enum for specifying the WebView's desired density. + *
      + *
    • {@code FAR} makes 100% looking like in 240dpi
    • + *
    • {@code MEDIUM} makes 100% looking like in 160dpi
    • + *
    • {@code CLOSE} makes 100% looking like in 120dpi
    • + *
    + */ + public enum ZoomDensity { + FAR(150), // 240dpi + MEDIUM(100), // 160dpi + CLOSE(75); // 120dpi + + ZoomDensity(int size) { + value = size; + } + + /** + * @hide Only for use by WebViewProvider implementations + */ + public int getValue() { + return value; + } + + int value; + } + + public @interface CacheMode { + } + + /** + * Default cache usage mode. If the navigation type doesn't impose any specific + * behavior, use cached resources when they are available and not expired, + * otherwise load resources from the network. Use with {@link #setCacheMode}. + */ + public static final int LOAD_DEFAULT = -1; + /** + * Normal cache usage mode. Use with {@link #setCacheMode}. + * + * @deprecated This value is obsolete, as from API level + * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and onwards it + * has the same effect as {@link #LOAD_DEFAULT}. + */ + @Deprecated + public static final int LOAD_NORMAL = 0; + /** + * Use cached resources when they are available, even if they have expired. + * Otherwise load resources from the network. Use with {@link #setCacheMode}. + */ + public static final int LOAD_CACHE_ELSE_NETWORK = 1; + /** + * Don't use the cache, load from the network. Use with {@link #setCacheMode}. + */ + public static final int LOAD_NO_CACHE = 2; + /** + * Don't use the network, load from the cache. Use with {@link #setCacheMode}. + */ + public static final int LOAD_CACHE_ONLY = 3; + + public enum RenderPriority { + NORMAL, HIGH, LOW + } + + /** + * The plugin state effects how plugins are treated on a page. ON means that any + * object will be loaded even if a plugin does not exist to handle the content. + * ON_DEMAND means that if there is a plugin installed that can handle the + * content, a placeholder is shown until the user clicks on the placeholder. + * Once clicked, the plugin will be enabled on the page. OFF means that all + * plugins will be turned off and any fallback content will be used. + */ + public enum PluginState { + ON, ON_DEMAND, OFF + } + + /** + * Used with {@link #setMixedContentMode} + * + * In this mode, the WebView will allow a secure origin to load content from any + * other origin, even if that origin is insecure. This is the least secure mode + * of operation for the WebView, and where possible apps should not set this + * mode. + */ + public static final int MIXED_CONTENT_ALWAYS_ALLOW = 0; + /** + * Used with {@link #setMixedContentMode} + * + * In this mode, the WebView will not allow a secure origin to load content from + * an insecure origin. This is the preferred and most secure mode of operation + * for the WebView and apps are strongly advised to use this mode. + */ + public static final int MIXED_CONTENT_NEVER_ALLOW = 1; + /** + * Used with {@link #setMixedContentMode} + * + * In this mode, the WebView will attempt to be compatible with the approach of + * a modern web browser with regard to mixed content. Some insecure content may + * be allowed to be loaded by a secure origin and other types of content will be + * blocked. The types of content are allowed or blocked may change release to + * release and are not explicitly defined. + * + * This mode is intended to be used by apps that are not in control of the + * content that they render but desire to operate in a reasonably secure + * environment. For highest security, apps are recommended to use + * {@link #MIXED_CONTENT_NEVER_ALLOW}. + */ + public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2; + + /** + * Enables dumping the pages navigation cache to a text file. The default is + * {@code false}. + * + * @deprecated This method is now obsolete. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @Deprecated + public abstract void setNavDump(boolean enabled); + + /** + * Gets whether dumping the navigation cache is enabled. + * + * @return whether dumping the navigation cache is enabled + * @see #setNavDump + * @deprecated This method is now obsolete. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @Deprecated + public abstract boolean getNavDump(); + + /** + * Sets whether the WebView should support zooming using its on-screen zoom + * controls and gestures. The particular zoom mechanisms that should be used can + * be set with {@link #setBuiltInZoomControls}. This setting does not affect + * zooming performed using the {@link WebView#zoomIn()} and + * {@link WebView#zoomOut()} methods. The default is {@code true}. + * + * @param support whether the WebView should support zoom + */ + public abstract void setSupportZoom(boolean support); + + /** + * Gets whether the WebView supports zoom. + * + * @return {@code true} if the WebView supports zoom + * @see #setSupportZoom + */ + public abstract boolean supportZoom(); + + /** + * Sets whether the WebView requires a user gesture to play media. The default + * is {@code true}. + * + * @param require whether the WebView requires a user gesture to play media + */ + public abstract void setMediaPlaybackRequiresUserGesture(boolean require); + + /** + * Gets whether the WebView requires a user gesture to play media. + * + * @return {@code true} if the WebView requires a user gesture to play media + * @see #setMediaPlaybackRequiresUserGesture + */ + public abstract boolean getMediaPlaybackRequiresUserGesture(); + + /** + * Sets whether the WebView should use its built-in zoom mechanisms. The + * built-in zoom mechanisms comprise on-screen zoom controls, which are + * displayed over the WebView's content, and the use of a pinch gesture to + * control zooming. Whether or not these on-screen controls are displayed can be + * set with {@link #setDisplayZoomControls}. The default is {@code false}. + *

    + * The built-in mechanisms are the only currently supported zoom mechanisms, so + * it is recommended that this setting is always enabled. + * + * @param enabled whether the WebView should use its built-in zoom mechanisms + */ + // This method was intended to select between the built-in zoom mechanisms + // and the separate zoom controls. The latter were obtained using + // {@link WebView#getZoomControls}, which is now hidden. + public abstract void setBuiltInZoomControls(boolean enabled); + + /** + * Gets whether the zoom mechanisms built into WebView are being used. + * + * @return {@code true} if the zoom mechanisms built into WebView are being used + * @see #setBuiltInZoomControls + */ + public abstract boolean getBuiltInZoomControls(); + + /** + * Sets whether the WebView should display on-screen zoom controls when using + * the built-in zoom mechanisms. See {@link #setBuiltInZoomControls}. The + * default is {@code true}. + * + * @param enabled whether the WebView should display on-screen zoom controls + */ + public abstract void setDisplayZoomControls(boolean enabled); + + /** + * Gets whether the WebView displays on-screen zoom controls when using the + * built-in zoom mechanisms. + * + * @return {@code true} if the WebView displays on-screen zoom controls when + * using the built-in zoom mechanisms + * @see #setDisplayZoomControls + */ + public abstract boolean getDisplayZoomControls(); + + /** + * Enables or disables file access within WebView. File access is enabled by + * default. Note that this enables or disables file system access only. Assets + * and resources are still accessible using file:///android_asset and + * file:///android_res. + */ + public abstract void setAllowFileAccess(boolean allow); + + /** + * Gets whether this WebView supports file access. + * + * @see #setAllowFileAccess + */ + public abstract boolean getAllowFileAccess(); + + /** + * Enables or disables content URL access within WebView. Content URL access + * allows WebView to load content from a content provider installed in the + * system. The default is enabled. + */ + public abstract void setAllowContentAccess(boolean allow); + + /** + * Gets whether this WebView supports content URL access. + * + * @see #setAllowContentAccess + */ + public abstract boolean getAllowContentAccess(); + + /** + * Sets whether the WebView loads pages in overview mode, that is, zooms out the + * content to fit on screen by width. This setting is taken into account when + * the content width is greater than the width of the WebView control, for + * example, when {@link #getUseWideViewPort} is enabled. The default is + * {@code false}. + */ + public abstract void setLoadWithOverviewMode(boolean overview); + + /** + * Gets whether this WebView loads pages in overview mode. + * + * @return whether this WebView loads pages in overview mode + * @see #setLoadWithOverviewMode + */ + public abstract boolean getLoadWithOverviewMode(); + + /** + * Sets whether the WebView will enable smooth transition while panning or + * zooming or while the window hosting the WebView does not have focus. If it is + * {@code true}, WebView will choose a solution to maximize the performance. + * e.g. the WebView's content may not be updated during the transition. If it is + * false, WebView will keep its fidelity. The default value is {@code false}. + * + * @deprecated This method is now obsolete, and will become a no-op in future. + */ + @Deprecated + public abstract void setEnableSmoothTransition(boolean enable); + + /** + * Gets whether the WebView enables smooth transition while panning or zooming. + * + * @see #setEnableSmoothTransition + * + * @deprecated This method is now obsolete, and will become a no-op in future. + */ + @Deprecated + public abstract boolean enableSmoothTransition(); + + /** + * Sets whether the WebView uses its background for over scroll background. If + * {@code true}, it will use the WebView's background. If {@code false}, it will + * use an internal pattern. Default is {@code true}. + * + * @deprecated This method is now obsolete. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @Deprecated + public abstract void setUseWebViewBackgroundForOverscrollBackground(boolean view); + + /** + * Gets whether this WebView uses WebView's background instead of internal + * pattern for over scroll background. + * + * @see #setUseWebViewBackgroundForOverscrollBackground + * @deprecated This method is now obsolete. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @Deprecated + public abstract boolean getUseWebViewBackgroundForOverscrollBackground(); + + /** + * Sets whether the WebView should save form data. In Android O, the platform + * has implemented a fully functional Autofill feature to store form data. + * Therefore, the Webview form data save feature is disabled. + * + * Note that the feature will continue to be supported on older versions of + * Android as before. + * + * This function does not have any effect. + */ + @Deprecated + public abstract void setSaveFormData(boolean save); + + /** + * Gets whether the WebView saves form data. + * + * @return whether the WebView saves form data + * @see #setSaveFormData + */ + @Deprecated + public abstract boolean getSaveFormData(); + + /** + * Sets whether the WebView should save passwords. The default is {@code true}. + * + * @deprecated Saving passwords in WebView will not be supported in future + * versions. + */ + @Deprecated + public abstract void setSavePassword(boolean save); + + /** + * Gets whether the WebView saves passwords. + * + * @return whether the WebView saves passwords + * @see #setSavePassword + * @deprecated Saving passwords in WebView will not be supported in future + * versions. + */ + @Deprecated + public abstract boolean getSavePassword(); + + /** + * Sets the text zoom of the page in percent. The default is 100. + * + * @param textZoom the text zoom in percent + */ + public abstract void setTextZoom(int textZoom); + + /** + * Gets the text zoom of the page in percent. + * + * @return the text zoom of the page in percent + * @see #setTextZoom + */ + public abstract int getTextZoom(); + + /** + * Sets policy for third party cookies. Developers should access this via + * {@link CookieManager#setShouldAcceptThirdPartyCookies}. + * + * @hide Internal API. + */ + public abstract void setAcceptThirdPartyCookies(boolean accept); + + /** + * Gets policy for third party cookies. Developers should access this via + * {@link CookieManager#getShouldAcceptThirdPartyCookies}. + * + * @hide Internal API + */ + public abstract boolean getAcceptThirdPartyCookies(); + + /** + * Sets the text size of the page. The default is {@link TextSize#NORMAL}. + * + * @param t the text size as a {@link TextSize} value + * @deprecated Use {@link #setTextZoom} instead. + */ + @Deprecated + public synchronized void setTextSize(TextSize t) { + setTextZoom(t.value); + } + + /** + * Gets the text size of the page. If the text size was previously specified in + * percent using {@link #setTextZoom}, this will return the closest matching + * {@link TextSize}. + * + * @return the text size as a {@link TextSize} value + * @see #setTextSize + * @deprecated Use {@link #getTextZoom} instead. + */ + @Deprecated + public synchronized TextSize getTextSize() { + return null; + } + + /** + * Sets the default zoom density of the page. This must be called from the UI + * thread. The default is {@link ZoomDensity#MEDIUM}. + * + * This setting is not recommended for use in new applications. If the WebView + * is utilized to display mobile-oriented pages, the desired effect can be + * achieved by adjusting 'width' and 'initial-scale' attributes of page's 'meta + * viewport' tag. For pages lacking the tag, + * {@link android.webkit.WebView#setInitialScale} and + * {@link #setUseWideViewPort} can be used. + * + * @param zoom the zoom density + * @deprecated This method is no longer supported, see the function + * documentation for recommended alternatives. + */ + @Deprecated + public abstract void setDefaultZoom(ZoomDensity zoom); + + /** + * Gets the default zoom density of the page. This should be called from the UI + * thread. + * + * This setting is not recommended for use in new applications. + * + * @return the zoom density + * @see #setDefaultZoom + * @deprecated Will only return the default value. + */ + @Deprecated + public abstract ZoomDensity getDefaultZoom(); + + /** + * Enables using light touches to make a selection and activate mouseovers. + * + * @deprecated From {@link android.os.Build.VERSION_CODES#JELLY_BEAN} this + * setting is obsolete and has no effect. + */ + @Deprecated + public abstract void setLightTouchEnabled(boolean enabled); + + /** + * Gets whether light touches are enabled. + * + * @see #setLightTouchEnabled + * @deprecated This setting is obsolete. + */ + @Deprecated + public abstract boolean getLightTouchEnabled(); + + /** + * Controlled a rendering optimization that is no longer present. Setting it now + * has no effect. + * + * @deprecated This setting now has no effect. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @Deprecated + public void setUseDoubleTree(boolean use) { + // Specified to do nothing, so no need for derived classes to override. + } + + /** + * Controlled a rendering optimization that is no longer present. Setting it now + * has no effect. + * + * @deprecated This setting now has no effect. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @Deprecated + public boolean getUseDoubleTree() { + // Returns false unconditionally, so no need for derived classes to override. + return false; + } + + /** + * Sets the user-agent string using an integer code. + *

      + *
    • 0 means the WebView should use an Android user-agent string
    • + *
    • 1 means the WebView should use a desktop user-agent string
    • + *
    + * Other values are ignored. The default is an Android user-agent string, i.e. + * code value 0. + * + * @param ua the integer code for the user-agent string + * @deprecated Please use {@link #setUserAgentString} instead. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @Deprecated + public abstract void setUserAgent(int ua); + + /** + * Gets the user-agent as an integer code. + *
      + *
    • -1 means the WebView is using a custom user-agent string set with + * {@link #setUserAgentString}
    • + *
    • 0 means the WebView should use an Android user-agent string
    • + *
    • 1 means the WebView should use a desktop user-agent string
    • + *
    + * + * @return the integer code for the user-agent string + * @see #setUserAgent + * @deprecated Please use {@link #getUserAgentString} instead. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + */ + @Deprecated + public abstract int getUserAgent(); + + /** + * Sets whether the WebView should enable support for the "viewport" + * HTML meta tag or should use a wide viewport. When the value of the setting is + * {@code false}, the layout width is always set to the width of the WebView + * control in device-independent (CSS) pixels. When the value is {@code true} + * and the page contains the viewport meta tag, the value of the width specified + * in the tag is used. If the page does not contain the tag or does not provide + * a width, then a wide viewport will be used. + * + * @param use whether to enable support for the viewport meta tag + */ + public abstract void setUseWideViewPort(boolean use); + + /** + * Gets whether the WebView supports the "viewport" HTML meta tag or + * will use a wide viewport. + * + * @return {@code true} if the WebView supports the viewport meta tag + * @see #setUseWideViewPort + */ + public abstract boolean getUseWideViewPort(); + + /** + * Sets whether the WebView whether supports multiple windows. If set to true, + * {@link WebChromeClient#onCreateWindow} must be implemented by the host + * application. The default is {@code false}. + * + * @param support whether to support multiple windows + */ + public abstract void setSupportMultipleWindows(boolean support); + + /** + * Gets whether the WebView supports multiple windows. + * + * @return {@code true} if the WebView supports multiple windows + * @see #setSupportMultipleWindows + */ + public abstract boolean supportMultipleWindows(); + + /** + * Sets the underlying layout algorithm. This will cause a re-layout of the + * WebView. The default is {@link LayoutAlgorithm#NARROW_COLUMNS}. + * + * @param l the layout algorithm to use, as a {@link LayoutAlgorithm} value + */ + public abstract void setLayoutAlgorithm(LayoutAlgorithm l); + + /** + * Gets the current layout algorithm. + * + * @return the layout algorithm in use, as a {@link LayoutAlgorithm} value + * @see #setLayoutAlgorithm + */ + public abstract LayoutAlgorithm getLayoutAlgorithm(); + + /** + * Sets the standard font family name. The default is "sans-serif". + * + * @param font a font family name + */ + public abstract void setStandardFontFamily(String font); + + /** + * Gets the standard font family name. + * + * @return the standard font family name as a string + * @see #setStandardFontFamily + */ + public abstract String getStandardFontFamily(); + + /** + * Sets the fixed font family name. The default is "monospace". + * + * @param font a font family name + */ + public abstract void setFixedFontFamily(String font); + + /** + * Gets the fixed font family name. + * + * @return the fixed font family name as a string + * @see #setFixedFontFamily + */ + public abstract String getFixedFontFamily(); + + /** + * Sets the sans-serif font family name. The default is "sans-serif". + * + * @param font a font family name + */ + public abstract void setSansSerifFontFamily(String font); + + /** + * Gets the sans-serif font family name. + * + * @return the sans-serif font family name as a string + * @see #setSansSerifFontFamily + */ + public abstract String getSansSerifFontFamily(); + + /** + * Sets the serif font family name. The default is "sans-serif". + * + * @param font a font family name + */ + public abstract void setSerifFontFamily(String font); + + /** + * Gets the serif font family name. The default is "serif". + * + * @return the serif font family name as a string + * @see #setSerifFontFamily + */ + public abstract String getSerifFontFamily(); + + /** + * Sets the cursive font family name. The default is "cursive". + * + * @param font a font family name + */ + public abstract void setCursiveFontFamily(String font); + + /** + * Gets the cursive font family name. + * + * @return the cursive font family name as a string + * @see #setCursiveFontFamily + */ + public abstract String getCursiveFontFamily(); + + /** + * Sets the fantasy font family name. The default is "fantasy". + * + * @param font a font family name + */ + public abstract void setFantasyFontFamily(String font); + + /** + * Gets the fantasy font family name. + * + * @return the fantasy font family name as a string + * @see #setFantasyFontFamily + */ + public abstract String getFantasyFontFamily(); + + /** + * Sets the minimum font size. The default is 8. + * + * @param size a non-negative integer between 1 and 72. Any number outside the + * specified range will be pinned. + */ + public abstract void setMinimumFontSize(int size); + + /** + * Gets the minimum font size. + * + * @return a non-negative integer between 1 and 72 + * @see #setMinimumFontSize + */ + public abstract int getMinimumFontSize(); + + /** + * Sets the minimum logical font size. The default is 8. + * + * @param size a non-negative integer between 1 and 72. Any number outside the + * specified range will be pinned. + */ + public abstract void setMinimumLogicalFontSize(int size); + + /** + * Gets the minimum logical font size. + * + * @return a non-negative integer between 1 and 72 + * @see #setMinimumLogicalFontSize + */ + public abstract int getMinimumLogicalFontSize(); + + /** + * Sets the default font size. The default is 16. + * + * @param size a non-negative integer between 1 and 72. Any number outside the + * specified range will be pinned. + */ + public abstract void setDefaultFontSize(int size); + + /** + * Gets the default font size. + * + * @return a non-negative integer between 1 and 72 + * @see #setDefaultFontSize + */ + public abstract int getDefaultFontSize(); + + /** + * Sets the default fixed font size. The default is 16. + * + * @param size a non-negative integer between 1 and 72. Any number outside the + * specified range will be pinned. + */ + public abstract void setDefaultFixedFontSize(int size); + + /** + * Gets the default fixed font size. + * + * @return a non-negative integer between 1 and 72 + * @see #setDefaultFixedFontSize + */ + public abstract int getDefaultFixedFontSize(); + + /** + * Sets whether the WebView should load image resources. Note that this method + * controls loading of all images, including those embedded using the data URI + * scheme. Use {@link #setBlockNetworkImage} to control loading only of images + * specified using network URI schemes. Note that if the value of this setting + * is changed from {@code false} to {@code true}, all images resources + * referenced by content currently displayed by the WebView are loaded + * automatically. The default is {@code true}. + * + * @param flag whether the WebView should load image resources + */ + public abstract void setLoadsImagesAutomatically(boolean flag); + + /** + * Gets whether the WebView loads image resources. This includes images embedded + * using the data URI scheme. + * + * @return {@code true} if the WebView loads image resources + * @see #setLoadsImagesAutomatically + */ + public abstract boolean getLoadsImagesAutomatically(); + + /** + * Sets whether the WebView should not load image resources from the network + * (resources accessed via http and https URI schemes). Note that this method + * has no effect unless {@link #getLoadsImagesAutomatically} returns + * {@code true}. Also note that disabling all network loads using + * {@link #setBlockNetworkLoads} will also prevent network images from loading, + * even if this flag is set to false. When the value of this setting is changed + * from {@code true} to {@code false}, network images resources referenced by + * content currently displayed by the WebView are fetched automatically. The + * default is {@code false}. + * + * @param flag whether the WebView should not load image resources from the + * network + * @see #setBlockNetworkLoads + */ + public abstract void setBlockNetworkImage(boolean flag); + + /** + * Gets whether the WebView does not load image resources from the network. + * + * @return {@code true} if the WebView does not load image resources from the + * network + * @see #setBlockNetworkImage + */ + public abstract boolean getBlockNetworkImage(); + + /** + * Sets whether the WebView should not load resources from the network. Use + * {@link #setBlockNetworkImage} to only avoid loading image resources. Note + * that if the value of this setting is changed from {@code true} to + * {@code false}, network resources referenced by content currently displayed by + * the WebView are not fetched until {@link android.webkit.WebView#reload} is + * called. If the application does not have the + * {@link android.Manifest.permission#INTERNET} permission, attempts to set a + * value of {@code false} will cause a {@link java.lang.SecurityException} to be + * thrown. The default value is {@code false} if the application has the + * {@link android.Manifest.permission#INTERNET} permission, otherwise it is + * {@code true}. + * + * @param flag {@code true} means block network loads by the WebView + * @see android.webkit.WebView#reload + */ + public abstract void setBlockNetworkLoads(boolean flag); + + /** + * Gets whether the WebView does not load any resources from the network. + * + * @return {@code true} if the WebView does not load any resources from the + * network + * @see #setBlockNetworkLoads + */ + public abstract boolean getBlockNetworkLoads(); + + /** + * Tells the WebView to enable JavaScript execution. The default is + * {@code false}. + * + * @param flag {@code true} if the WebView should execute JavaScript + */ + public abstract void setJavaScriptEnabled(boolean flag); + + /** + * Sets whether JavaScript running in the context of a file scheme URL should be + * allowed to access content from any origin. This includes access to content + * from other file scheme URLs. See {@link #setAllowFileAccessFromFileURLs}. To + * enable the most restrictive, and therefore secure policy, this setting should + * be disabled. Note that this setting affects only JavaScript access to file + * scheme resources. Other access to such resources, for example, from image + * HTML elements, is unaffected. To prevent possible violation of same domain + * policy when targeting + * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier, + * you should explicitly set this value to {@code false}. + *

    + * The default value is {@code true} for apps targeting + * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, and + * {@code false} when targeting + * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} and above. + * + * @param flag whether JavaScript running in the context of a file scheme URL + * should be allowed to access content from any origin + */ + public abstract void setAllowUniversalAccessFromFileURLs(boolean flag); + + /** + * Sets whether JavaScript running in the context of a file scheme URL should be + * allowed to access content from other file scheme URLs. To enable the most + * restrictive, and therefore secure, policy this setting should be disabled. + * Note that the value of this setting is ignored if the value of + * {@link #getAllowUniversalAccessFromFileURLs} is {@code true}. Note too, that + * this setting affects only JavaScript access to file scheme resources. Other + * access to such resources, for example, from image HTML elements, is + * unaffected. To prevent possible violation of same domain policy when + * targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and + * earlier, you should explicitly set this value to {@code false}. + *

    + * The default value is {@code true} for apps targeting + * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, and + * {@code false} when targeting + * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} and above. + * + * @param flag whether JavaScript running in the context of a file scheme URL + * should be allowed to access content from other file scheme URLs + */ + public abstract void setAllowFileAccessFromFileURLs(boolean flag); + + /** + * Sets whether the WebView should enable plugins. The default is {@code false}. + * + * @param flag {@code true} if plugins should be enabled + * @deprecated This method has been deprecated in favor of + * {@link #setPluginState} + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} + */ + @Deprecated + public abstract void setPluginsEnabled(boolean flag); + + /** + * Tells the WebView to enable, disable, or have plugins on demand. On demand + * mode means that if a plugin exists that can handle the embedded content, a + * placeholder icon will be shown instead of the plugin. When the placeholder is + * clicked, the plugin will be enabled. The default is {@link PluginState#OFF}. + * + * @param state a PluginState value + * @deprecated Plugins will not be supported in future, and should not be used. + */ + @Deprecated + public abstract void setPluginState(PluginState state); + + /** + * Sets a custom path to plugins used by the WebView. This method is obsolete + * since each plugin is now loaded from its own package. + * + * @param pluginsPath a String path to the directory containing plugins + * @deprecated This method is no longer used as plugins are loaded from their + * own APK via the system's package manager. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} + */ + @Deprecated + public void setPluginsPath(String pluginsPath) { + // Specified to do nothing, so no need for derived classes to override. + } + + /** + * Sets the path to where database storage API databases should be saved. In + * order for the database storage API to function correctly, this method must be + * called with a path to which the application can write. This method should + * only be called once: repeated calls are ignored. + * + * @param databasePath a path to the directory where databases should be saved. + * @deprecated Database paths are managed by the implementation and calling this + * method will have no effect. + */ + @Deprecated + public abstract void setDatabasePath(String databasePath); + + /** + * Sets the path where the Geolocation databases should be saved. In order for + * Geolocation permissions and cached positions to be persisted, this method + * must be called with a path to which the application can write. + * + * @param databasePath a path to the directory where databases should be saved. + * @deprecated Geolocation database are managed by the implementation and + * calling this method will have no effect. + */ + @Deprecated + public abstract void setGeolocationDatabasePath(String databasePath); + + /** + * Sets whether the Application Caches API should be enabled. The default is + * {@code false}. Note that in order for the Application Caches API to be + * enabled, a valid database path must also be supplied to + * {@link #setAppCachePath}. + * + * @param flag {@code true} if the WebView should enable Application Caches + */ + public abstract void setAppCacheEnabled(boolean flag); + + /** + * Sets the path to the Application Caches files. In order for the Application + * Caches API to be enabled, this method must be called with a path to which the + * application can write. This method should only be called once: repeated calls + * are ignored. + * + * @param appCachePath a String path to the directory containing Application + * Caches files. + * @see #setAppCacheEnabled + */ + public abstract void setAppCachePath(String appCachePath); + + /** + * Sets the maximum size for the Application Cache content. The passed size will + * be rounded to the nearest value that the database can support, so this should + * be viewed as a guide, not a hard limit. Setting the size to a value less than + * current database size does not cause the database to be trimmed. The default + * size is {@link Long#MAX_VALUE}. It is recommended to leave the maximum size + * set to the default value. + * + * @param appCacheMaxSize the maximum size in bytes + * @deprecated In future quota will be managed automatically. + */ + @Deprecated + public abstract void setAppCacheMaxSize(long appCacheMaxSize); + + /** + * Sets whether the database storage API is enabled. The default value is false. + * See also {@link #setDatabasePath} for how to correctly set up the database + * storage API. + * + * This setting is global in effect, across all WebView instances in a process. + * Note you should only modify this setting prior to making any WebView + * page load within a given process, as the WebView implementation may ignore + * changes to this setting after that point. + * + * @param flag {@code true} if the WebView should use the database storage API + */ + public abstract void setDatabaseEnabled(boolean flag); + + /** + * Sets whether the DOM storage API is enabled. The default value is + * {@code false}. + * + * @param flag {@code true} if the WebView should use the DOM storage API + */ + public abstract void setDomStorageEnabled(boolean flag); + + /** + * Gets whether the DOM Storage APIs are enabled. + * + * @return {@code true} if the DOM Storage APIs are enabled + * @see #setDomStorageEnabled + */ + public abstract boolean getDomStorageEnabled(); + + /** + * Gets the path to where database storage API databases are saved. + * + * @return the String path to the database storage API databases + * @see #setDatabasePath + * @deprecated Database paths are managed by the implementation this method is + * obsolete. + */ + @Deprecated + public abstract String getDatabasePath(); + + /** + * Gets whether the database storage API is enabled. + * + * @return {@code true} if the database storage API is enabled + * @see #setDatabaseEnabled + */ + public abstract boolean getDatabaseEnabled(); + + /** + * Sets whether Geolocation is enabled. The default is {@code true}. + *

    + * Please note that in order for the Geolocation API to be usable by a page in + * the WebView, the following requirements must be met: + *

      + *
    • an application must have permission to access the device location, see + * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}; + *
    • an application must provide an implementation of the + * {@link WebChromeClient#onGeolocationPermissionsShowPrompt} callback to + * receive notifications that a page is requesting access to location via the + * JavaScript Geolocation API. + *
    + *

    + * + * @param flag whether Geolocation should be enabled + */ + public abstract void setGeolocationEnabled(boolean flag); + + /** + * Gets whether JavaScript is enabled. + * + * @return {@code true} if JavaScript is enabled + * @see #setJavaScriptEnabled + */ + public abstract boolean getJavaScriptEnabled(); + + /** + * Gets whether JavaScript running in the context of a file scheme URL can + * access content from any origin. This includes access to content from other + * file scheme URLs. + * + * @return whether JavaScript running in the context of a file scheme URL can + * access content from any origin + * @see #setAllowUniversalAccessFromFileURLs + */ + public abstract boolean getAllowUniversalAccessFromFileURLs(); + + /** + * Gets whether JavaScript running in the context of a file scheme URL can + * access content from other file scheme URLs. + * + * @return whether JavaScript running in the context of a file scheme URL can + * access content from other file scheme URLs + * @see #setAllowFileAccessFromFileURLs + */ + public abstract boolean getAllowFileAccessFromFileURLs(); + + /** + * Gets whether plugins are enabled. + * + * @return {@code true} if plugins are enabled + * @see #setPluginsEnabled + * @deprecated This method has been replaced by {@link #getPluginState} + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} + */ + @Deprecated + public abstract boolean getPluginsEnabled(); + + /** + * Gets the current state regarding whether plugins are enabled. + * + * @return the plugin state as a {@link PluginState} value + * @see #setPluginState + * @deprecated Plugins will not be supported in future, and should not be used. + */ + @Deprecated + public abstract PluginState getPluginState(); + + /** + * Gets the directory that contains the plugin libraries. This method is + * obsolete since each plugin is now loaded from its own package. + * + * @return an empty string + * @deprecated This method is no longer used as plugins are loaded from their + * own APK via the system's package manager. + * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} + */ + @Deprecated + public String getPluginsPath() { + // Unconditionally returns empty string, so no need for derived classes to + // override. + return ""; + } + + /** + * Tells JavaScript to open windows automatically. This applies to the + * JavaScript function {@code window.open()}. The default is {@code false}. + * + * @param flag {@code true} if JavaScript can open windows automatically + */ + public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean flag); + + /** + * Gets whether JavaScript can open windows automatically. + * + * @return {@code true} if JavaScript can open windows automatically during + * {@code window.open()} + * @see #setJavaScriptCanOpenWindowsAutomatically + */ + public abstract boolean getJavaScriptCanOpenWindowsAutomatically(); + + /** + * Sets the default text encoding name to use when decoding html pages. The + * default is "UTF-8". + * + * @param encoding the text encoding name + */ + public abstract void setDefaultTextEncodingName(String encoding); + + /** + * Gets the default text encoding name. + * + * @return the default text encoding name as a string + * @see #setDefaultTextEncodingName + */ + public abstract String getDefaultTextEncodingName(); + + /** + * Sets the WebView's user-agent string. If the string is {@code null} or empty, + * the system default value will be used. + * + * Note that starting from {@link android.os.Build.VERSION_CODES#KITKAT} Android + * version, changing the user-agent while loading a web page causes WebView to + * initiate loading once again. + * + * @param ua new user-agent string + */ + public abstract void setUserAgentString(String ua); + + /** + * Gets the WebView's user-agent string. + * + * @return the WebView's user-agent string + * @see #setUserAgentString + */ + public abstract String getUserAgentString(); + + /** + * Returns the default User-Agent used by a WebView. An instance of WebView + * could use a different User-Agent if a call is made to + * {@link WebSettings#setUserAgentString(String)}. + * + * @param context a Context object used to access application assets + */ + public static String getDefaultUserAgent(Context context) { + return null; + } + + /** + * Tells the WebView whether it needs to set a node to have focus when + * {@link WebView#requestFocus(int, android.graphics.Rect)} is called. The + * default value is {@code true}. + * + * @param flag whether the WebView needs to set a node + */ + public abstract void setNeedInitialFocus(boolean flag); + + /** + * Sets the priority of the Render thread. Unlike the other settings, this one + * only needs to be called once per process. The default value is + * {@link RenderPriority#NORMAL}. + * + * @param priority the priority + * @deprecated It is not recommended to adjust thread priorities, and this will + * not be supported in future versions. + */ + @Deprecated + public abstract void setRenderPriority(RenderPriority priority); + + /** + * Overrides the way the cache is used. The way the cache is used is based on + * the navigation type. For a normal page load, the cache is checked and content + * is re-validated as needed. When navigating back, content is not revalidated, + * instead the content is just retrieved from the cache. This method allows the + * client to override this behavior by specifying one of {@link #LOAD_DEFAULT}, + * {@link #LOAD_CACHE_ELSE_NETWORK}, {@link #LOAD_NO_CACHE} or + * {@link #LOAD_CACHE_ONLY}. The default value is {@link #LOAD_DEFAULT}. + * + * @param mode the mode to use + */ + public abstract void setCacheMode(@CacheMode int mode); + + /** + * Gets the current setting for overriding the cache mode. + * + * @return the current setting for overriding the cache mode + * @see #setCacheMode + */ + public abstract int getCacheMode(); + + /** + * Configures the WebView's behavior when a secure origin attempts to load a + * resource from an insecure origin. + * + * By default, apps that target {@link android.os.Build.VERSION_CODES#KITKAT} or + * below default to {@link #MIXED_CONTENT_ALWAYS_ALLOW}. Apps targeting + * {@link android.os.Build.VERSION_CODES#LOLLIPOP} default to + * {@link #MIXED_CONTENT_NEVER_ALLOW}. + * + * The preferred and most secure mode of operation for the WebView is + * {@link #MIXED_CONTENT_NEVER_ALLOW} and use of + * {@link #MIXED_CONTENT_ALWAYS_ALLOW} is strongly discouraged. + * + * @param mode The mixed content mode to use. One of + * {@link #MIXED_CONTENT_NEVER_ALLOW}, + * {@link #MIXED_CONTENT_ALWAYS_ALLOW} or + * {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. + */ + public abstract void setMixedContentMode(int mode); + + /** + * Gets the current behavior of the WebView with regard to loading insecure + * content from a secure origin. + * + * @return The current setting, one of {@link #MIXED_CONTENT_NEVER_ALLOW}, + * {@link #MIXED_CONTENT_ALWAYS_ALLOW} or + * {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. + */ + public abstract int getMixedContentMode(); + + /** + * Sets whether to use a video overlay for embedded encrypted video. In API + * levels prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, encrypted + * video can only be rendered directly on a secure video surface, so it had been + * a hard problem to play encrypted video in HTML. When this flag is on, WebView + * can play encrypted video (MSE/EME) by using a video overlay (aka + * hole-punching) for videos embedded using HTML <video> tag.
    + * Caution: This setting is intended for use only in a narrow set of + * circumstances and apps should only enable it if they require playback of + * encrypted video content. It will impose the following limitations on the + * WebView: + *

      + *
    • Only one video overlay can be played at a time. + *
    • Changes made to position or dimensions of a video element may be + * propagated to the corresponding video overlay with a noticeable delay. + *
    • The video overlay is not visible to web APIs and as such may not interact + * with script or styling. For example, CSS styles applied to the <video> + * tag may be ignored. + *
    + * This is not an exhaustive set of constraints and it may vary with new + * versions of the WebView. + * + * @hide + */ + public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean flag); + + /** + * Gets whether a video overlay will be used for embedded encrypted video. + * + * @return {@code true} if WebView uses a video overlay for embedded encrypted + * video. + * @see #setVideoOverlayForEmbeddedEncryptedVideoEnabled + * @hide + */ + public abstract boolean getVideoOverlayForEmbeddedEncryptedVideoEnabled(); + + /** + * Sets whether this WebView should raster tiles when it is offscreen but + * attached to a window. Turning this on can avoid rendering artifacts when + * animating an offscreen WebView on-screen. Offscreen WebViews in this mode use + * more memory. The default value is false.
    + * Please follow these guidelines to limit memory usage: + *
      + *
    • WebView size should be not be larger than the device screen size. + *
    • Limit use of this mode to a small number of WebViews. Use it for visible + * WebViews and WebViews about to be animated to visible. + *
    + */ + public abstract void setOffscreenPreRaster(boolean enabled); + + /** + * Gets whether this WebView should raster tiles when it is offscreen but + * attached to a window. + * + * @return {@code true} if this WebView will raster tiles when it is offscreen + * but attached to a window. + */ + public abstract boolean getOffscreenPreRaster(); + + /** + * Sets whether Safe Browsing is enabled. Safe Browsing allows WebView to + * protect against malware and phishing attacks by verifying the links. + * + *

    + * Safe Browsing can be disabled for all WebViews using a manifest tag (read + * general Safe + * Browsing info). The manifest tag has a lower precedence than this API. + * + *

    + * Safe Browsing is enabled by default for devices which support it. + * + * @param enabled Whether Safe Browsing is enabled. + */ + public abstract void setSafeBrowsingEnabled(boolean enabled); + + /** + * Gets whether Safe Browsing is enabled. See {@link #setSafeBrowsingEnabled}. + * + * @return {@code true} if Safe Browsing is enabled and {@code false} otherwise. + */ + public abstract boolean getSafeBrowsingEnabled(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java new file mode 100644 index 00000000000..1b377ad3e17 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.webkit; + +import android.content.Context; +import android.content.Intent; + +import java.io.File; +import java.util.List; +import java.util.Map; + +/** + *

    + * A View that displays web pages. This class is the basis upon which you can + * roll your own web browser or simply display some online content within your + * Activity. It uses the WebKit rendering engine to display web pages and + * includes methods to navigate forward and backward through a history, zoom in + * and out, perform text searches and more. + * + *

    + * Note that, in order for your Activity to access the Internet and load web + * pages in a WebView, you must add the {@code INTERNET} permissions to your + * Android Manifest file: + * + *

    + * {@code }
    + * 
    + * + *

    + * This must be a child of the {@code } + * element. + * + *

    + * For more information, read + * Building Web Apps in + * WebView. + * + *

    Basic usage

    + * + *

    + * By default, a WebView provides no browser-like widgets, does not enable + * JavaScript and web page errors are ignored. If your goal is only to display + * some HTML as a part of your UI, this is probably fine; the user won't need to + * interact with the web page beyond reading it, and the web page won't need to + * interact with the user. If you actually want a full-blown web browser, then + * you probably want to invoke the Browser application with a URL Intent rather + * than show it with a WebView. For example: + * + *

    + * Uri uri = Uri.parse("https://www.example.com");
    + * Intent intent = new Intent(Intent.ACTION_VIEW, uri);
    + * startActivity(intent);
    + * 
    + *

    + * See {@link android.content.Intent} for more information. + * + *

    + * To provide a WebView in your own Activity, include a {@code } in + * your layout, or set the entire Activity window as a WebView during + * {@link android.app.Activity#onCreate(Bundle) onCreate()}: + * + *

    + * WebView webview = new WebView(this);
    + * setContentView(webview);
    + * 
    + * + *

    + * Then load the desired web page: + * + *

    + * // Simplest usage: note that an exception will NOT be thrown
    + * // if there is an error loading this page (see below).
    + * webview.loadUrl("https://example.com/");
    + *
    + * // OR, you can also load from an HTML string:
    + * String summary = "<html><body>You scored <b>192</b> points.</body></html>";
    + * webview.loadData(summary, "text/html", null);
    + * // ... although note that there are restrictions on what this HTML can do.
    + * // See {@link #loadData(String,String,String)} and {@link
    + * #loadDataWithBaseURL(String,String,String,String,String)} for more info.
    + * // Also see {@link #loadData(String,String,String)} for information on encoding special
    + * // characters.
    + * 
    + * + *

    + * A WebView has several customization points where you can add your own + * behavior. These are: + * + *

      + *
    • Creating and setting a {@link android.webkit.WebChromeClient} subclass. + * This class is called when something that might impact a browser UI happens, + * for instance, progress updates and JavaScript alerts are sent here (see + * Debugging + * Tasks).
    • + *
    • Creating and setting a {@link android.webkit.WebViewClient} subclass. It + * will be called when things happen that impact the rendering of the content, + * eg, errors or form submissions. You can also intercept URL loading here (via + * {@link android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String) + * shouldOverrideUrlLoading()}).
    • + *
    • Modifying the {@link android.webkit.WebSettings}, such as enabling + * JavaScript with + * {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean) + * setJavaScriptEnabled()}.
    • + *
    • Injecting Java objects into the WebView using the + * {@link android.webkit.WebView#addJavascriptInterface} method. This method + * allows you to inject Java objects into a page's JavaScript context, so that + * they can be accessed by JavaScript in the page.
    • + *
    + * + *

    + * Here's a more complicated example, showing error handling, settings, and + * progress notification: + * + *

    + * // Let's display the progress in the activity title bar, like the
    + * // browser app does.
    + * getWindow().requestFeature(Window.FEATURE_PROGRESS);
    + *
    + * webview.getSettings().setJavaScriptEnabled(true);
    + *
    + * final Activity activity = this;
    + * webview.setWebChromeClient(new WebChromeClient() {
    + *     public void onProgressChanged(WebView view, int progress) {
    + *         // Activities and WebViews measure progress with different scales.
    + *         // The progress meter will automatically disappear when we reach 100%
    + *         activity.setProgress(progress * 1000);
    + *     }
    + * });
    + * webview.setWebViewClient(new WebViewClient() {
    + *     public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
    + *         Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
    + *     }
    + * });
    + *
    + * webview.loadUrl("https://developer.android.com/");
    + * 
    + * + *

    Zoom

    + * + *

    + * To enable the built-in zoom, set {@link #getSettings() + * WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)} (introduced + * in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}). + * + *

    + * Note: Using zoom if either the height or width is set to + * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} may lead to + * undefined behavior and should be avoided. + * + *

    Cookie and window management

    + * + *

    + * For obvious security reasons, your application has its own cache, cookie + * store etc.—it does not share the Browser application's data. + * + *

    + * By default, requests by the HTML to open new windows are ignored. This is + * {@code true} whether they be opened by JavaScript or by the target attribute + * on a link. You can customize your {@link WebChromeClient} to provide your own + * behavior for opening multiple windows, and render them in whatever manner you + * want. + * + *

    + * The standard behavior for an Activity is to be destroyed and recreated when + * the device orientation or any other configuration changes. This will cause + * the WebView to reload the current page. If you don't want that, you can set + * your Activity to handle the {@code orientation} and {@code keyboardHidden} + * changes, and then just leave the WebView alone. It'll automatically re-orient + * itself as appropriate. Read + * Handling + * Runtime Changes for more information about how to handle configuration + * changes during runtime. + * + * + *

    Building web pages to support different screen densities

    + * + *

    + * The screen density of a device is based on the screen resolution. A screen + * with low density has fewer available pixels per inch, where a screen with + * high density has more — sometimes significantly more — pixels per + * inch. The density of a screen is important because, other things being equal, + * a UI element (such as a button) whose height and width are defined in terms + * of screen pixels will appear larger on the lower density screen and smaller + * on the higher density screen. For simplicity, Android collapses all actual + * screen densities into three generalized densities: high, medium, and low. + *

    + * By default, WebView scales a web page so that it is drawn at a size that + * matches the default appearance on a medium density screen. So, it applies + * 1.5x scaling on a high density screen (because its pixels are smaller) and + * 0.75x scaling on a low density screen (because its pixels are bigger). + * Starting with API level {@link android.os.Build.VERSION_CODES#ECLAIR}, + * WebView supports DOM, CSS, and meta tag features to help you (as a web + * developer) target screens with different screen densities. + *

    + * Here's a summary of the features you can use to handle different screen + * densities: + *

      + *
    • The {@code window.devicePixelRatio} DOM property. The value of this + * property specifies the default scaling factor used for the current device. + * For example, if the value of {@code + * window.devicePixelRatio} is "1.0", then the device is considered a medium + * density (mdpi) device and default scaling is not applied to the web page; if + * the value is "1.5", then the device is considered a high density device + * (hdpi) and the page content is scaled 1.5x; if the value is "0.75", then the + * device is considered a low density device (ldpi) and the content is scaled + * 0.75x.
    • + *
    • The {@code -webkit-device-pixel-ratio} CSS media query. Use this to + * specify the screen densities for which this style sheet is to be used. The + * corresponding value should be either "0.75", "1", or "1.5", to indicate that + * the styles are for devices with low density, medium density, or high density + * screens, respectively. For example: + * + *
      + * <link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" />
      + * 
      + *

      + * The {@code hdpi.css} stylesheet is only used for devices with a screen pixel + * ratio of 1.5, which is the high density pixel ratio.

    • + *
    + * + *

    HTML5 Video support

    + * + *

    + * In order to support inline HTML5 video in your application you need to have + * hardware acceleration turned on. + * + *

    Full screen support

    + * + *

    + * In order to support full screen — for video or other HTML content + * — you need to set a {@link android.webkit.WebChromeClient} and + * implement both + * {@link WebChromeClient#onShowCustomView(View, WebChromeClient.CustomViewCallback)} + * and {@link WebChromeClient#onHideCustomView()}. If the implementation of + * either of these two methods is missing then the web contents will not be + * allowed to enter full screen. Optionally you can implement + * {@link WebChromeClient#getVideoLoadingProgressView()} to customize the View + * displayed whilst a video is loading. + * + *

    HTML5 Geolocation API support

    + * + *

    + * For applications targeting Android N and later releases (API level > + * {@link android.os.Build.VERSION_CODES#M}) the geolocation api is only + * supported on secure origins such as https. For such applications requests to + * geolocation api on non-secure origins are automatically denied without + * invoking the corresponding + * {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)} + * method. + * + *

    Layout size

    + *

    + * It is recommended to set the WebView layout height to a fixed value or to + * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} instead of using + * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}. When using + * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} for the height none + * of the WebView's parents should use a + * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} layout height since + * that could result in incorrect sizing of the views. + * + *

    + * Setting the WebView's height to + * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} enables the + * following behaviors: + *

      + *
    • The HTML body layout height is set to a fixed value. This means that + * elements with a height relative to the HTML body may not be sized correctly. + *
    • + *
    • For applications targeting {@link android.os.Build.VERSION_CODES#KITKAT} + * and earlier SDKs the HTML viewport meta tag will be ignored in order to + * preserve backwards compatibility.
    • + *
    + * + *

    + * Using a layout width of + * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} is not supported. If + * such a width is used the WebView will attempt to use the width of the parent + * instead. + * + *

    Metrics

    + * + *

    + * WebView may upload anonymous diagnostic data to Google when the user has + * consented. This data helps Google improve WebView. Data is collected on a + * per-app basis for each app which has instantiated a WebView. An individual + * app can opt out of this feature by putting the following tag in its + * manifest's {@code } element: + * + *

    + * <manifest>
    + *     <application>
    + *         ...
    + *         <meta-data android:name="android.webkit.WebView.MetricsOptOut"
    + *             android:value="true" />
    + *     </application>
    + * </manifest>
    + * 
    + *

    + * Data will only be uploaded for a given app if the user has consented AND the + * app has not opted out. + * + *

    Safe Browsing

    + * + *

    + * With Safe Browsing, WebView will block malicious URLs and present a warning + * UI to the user to allow them to navigate back safely or proceed to the + * malicious page. + *

    + * Safe Browsing is enabled by default on devices which support it. If your app + * needs to disable Safe Browsing for all WebViews, it can do so in the + * manifest's {@code } element: + *

    + * + *

    + * <manifest>
    + *     <application>
    + *         ...
    + *         <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
    + *             android:value="false" />
    + *     </application>
    + * </manifest>
    + * 
    + * + *

    + * Otherwise, see {@link WebSettings#setSafeBrowsingEnabled}. + * + */ +// Implementation notes. +// The WebView is a thin API class that delegates its public API to a backend +// WebViewProvider +// class instance. WebView extends {@link AbsoluteLayout} for backward +// compatibility reasons. +// Methods are delegated to the provider implementation: all public API methods +// introduced in this +// file are fully delegated, whereas public and protected methods from the View +// base classes are +// only delegated where a specific need exists for them to do so. +public class WebView { + + /** + * Constructs a new WebView with an Activity Context object. + * + *

    + * Note: WebView should always be instantiated with an Activity Context. + * If instantiated with an Application Context, WebView will be unable to + * provide several features, such as JavaScript dialogs and autofill. + * + * @param context an Activity Context to access application assets + */ + public WebView(Context context) { + } + + /** + * Loads the given URL with the specified additional HTTP headers. + *

    + * Also see compatibility note on {@link #evaluateJavascript}. + * + * @param url the URL of the resource to load + * @param additionalHttpHeaders the additional headers to be used in the HTTP + * request for this URL, specified as a map from + * name to value. Note that if this map contains + * any of the headers that are set by default by + * this WebView, such as those controlling caching, + * accept types or the User-Agent, their values may + * be overridden by this WebView's defaults. + */ + public void loadUrl(String url, Map additionalHttpHeaders) { + } + + /** + * Loads the given URL. + *

    + * Also see compatibility note on {@link #evaluateJavascript}. + * + * @param url the URL of the resource to load + */ + public void loadUrl(String url) { + } + + /** + * Loads the URL with postData using "POST" method into this WebView. If url is + * not a network URL, it will be loaded with {@link #loadUrl(String)} instead, + * ignoring the postData param. + * + * @param url the URL of the resource to load + * @param postData the data will be passed to "POST" request, which must be be + * "application/x-www-form-urlencoded" encoded. + */ + public void postUrl(String url, byte[] postData) { + } + + /** + * Loads the given data into this WebView using a 'data' scheme URL. + *

    + * Note that JavaScript's same origin policy means that script running in a page + * loaded using this method will be unable to access content loaded using any + * scheme other than 'data', including 'http(s)'. To avoid this restriction, use + * {@link #loadDataWithBaseURL(String,String,String,String,String) + * loadDataWithBaseURL()} with an appropriate base URL. + *

    + * The {@code encoding} parameter specifies whether the data is base64 or URL + * encoded. If the data is base64 encoded, the value of the encoding parameter + * must be 'base64'. HTML can be encoded with + * {@link android.util.Base64#encodeToString(byte[],int)} like so: + * + *

    +     * String unencodedHtml = "<html><body>'%28' is the code for '('</body></html>";
    +     * String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING);
    +     * webView.loadData(encodedHtml, "text/html", "base64");
    +     * 
    + *

    + * For all other values of {@code encoding} (including {@code null}) it is + * assumed that the data uses ASCII encoding for octets inside the range of safe + * URL characters and use the standard %xx hex encoding of URLs for octets + * outside that range. See + * RFC 3986 for + * more information. + *

    + * The {@code mimeType} parameter specifies the format of the data. If WebView + * can't handle the specified MIME type, it will download the data. If + * {@code null}, defaults to 'text/html'. + *

    + * The 'data' scheme URL formed by this method uses the default US-ASCII + * charset. If you need to set a different charset, you should form a 'data' + * scheme URL which explicitly specifies a charset parameter in the mediatype + * portion of the URL and call {@link #loadUrl(String)} instead. Note that the + * charset obtained from the mediatype portion of a data URL always overrides + * that specified in the HTML or XML document itself. + *

    + * Content loaded using this method will have a {@code window.origin} value of + * {@code "null"}. This must not be considered to be a trusted origin by the + * application or by any JavaScript code running inside the WebView (for + * example, event sources in DOM event handlers or web messages), because + * malicious content can also create frames with a null origin. If you need to + * identify the main frame's origin in a trustworthy way, you should use + * {@link #loadDataWithBaseURL(String,String,String,String,String) + * loadDataWithBaseURL()} with a valid HTTP or HTTPS base URL to set the origin. + * + * @param data a String of data in the given encoding + * @param mimeType the MIME type of the data, e.g. 'text/html'. + * @param encoding the encoding of the data + */ + public void loadData(String data, String mimeType, String encoding) { + } + + /** + * Loads the given data into this WebView, using baseUrl as the base URL for the + * content. The base URL is used both to resolve relative URLs and when applying + * JavaScript's same origin policy. The historyUrl is used for the history + * entry. + *

    + * The {@code mimeType} parameter specifies the format of the data. If WebView + * can't handle the specified MIME type, it will download the data. If + * {@code null}, defaults to 'text/html'. + *

    + * Note that content specified in this way can access local device files (via + * 'file' scheme URLs) only if baseUrl specifies a scheme other than 'http', + * 'https', 'ftp', 'ftps', 'about' or 'javascript'. + *

    + * If the base URL uses the data scheme, this method is equivalent to calling + * {@link #loadData(String,String,String) loadData()} and the historyUrl is + * ignored, and the data will be treated as part of a data: URL. If the base URL + * uses any other scheme, then the data will be loaded into the WebView as a + * plain string (i.e. not part of a data URL) and any URL-encoded entities in + * the string will not be decoded. + *

    + * Note that the baseUrl is sent in the 'Referer' HTTP header when requesting + * subresources (images, etc.) of the page loaded using this method. + *

    + * If a valid HTTP or HTTPS base URL is not specified in {@code baseUrl}, then + * content loaded using this method will have a {@code window.origin} value of + * {@code "null"}. This must not be considered to be a trusted origin by the + * application or by any JavaScript code running inside the WebView (for + * example, event sources in DOM event handlers or web messages), because + * malicious content can also create frames with a null origin. If you need to + * identify the main frame's origin in a trustworthy way, you should use a valid + * HTTP or HTTPS base URL to set the origin. + * + * @param baseUrl the URL to use as the page's base URL. If {@code null} + * defaults to 'about:blank'. + * @param data a String of data in the given encoding + * @param mimeType the MIME type of the data, e.g. 'text/html'. + * @param encoding the encoding of the data + * @param historyUrl the URL to use as the history entry. If {@code null} + * defaults to 'about:blank'. If non-null, this must be a + * valid URL. + */ + public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) { + } + + /** + * Sets the WebViewClient that will receive various notifications and + * requests. This will replace the current handler. + * + * @param client an implementation of WebViewClient + * @see #getWebViewClient + */ + public void setWebViewClient(WebViewClient client) { + } + /** + * Gets the WebViewClient. + * + * @return the WebViewClient, or a default client if not yet set + * @see #setWebViewClient + */ + public WebViewClient getWebViewClient() { + return null; + } + + /** + * Injects the supplied Java object into this WebView. The object is + * injected into the JavaScript context of the main frame, using the + * supplied name. This allows the Java object's methods to be + * accessed from JavaScript. For applications targeted to API + * level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + * and above, only public methods that are annotated with + * {@link android.webkit.JavascriptInterface} can be accessed from JavaScript. + * For applications targeted to API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or below, + * all public methods (including the inherited ones) can be accessed, see the + * important security note below for implications. + *

    Note that injected objects will not appear in JavaScript until the page is next + * (re)loaded. JavaScript should be enabled before injecting the object. For example: + *

    +     * class JsObject {
    +     *    {@literal @}JavascriptInterface
    +     *    public String toString() { return "injectedObject"; }
    +     * }
    +     * webview.getSettings().setJavaScriptEnabled(true);
    +     * webView.addJavascriptInterface(new JsObject(), "injectedObject");
    +     * webView.loadData("", "text/html", null);
    +     * webView.loadUrl("javascript:alert(injectedObject.toString())");
    + *

    + * IMPORTANT: + *

      + *
    • This method can be used to allow JavaScript to control the host + * application. This is a powerful feature, but also presents a security + * risk for apps targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or earlier. + * Apps that target a version later than {@link android.os.Build.VERSION_CODES#JELLY_BEAN} + * are still vulnerable if the app runs on a device running Android earlier than 4.2. + * The most secure way to use this method is to target {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} + * and to ensure the method is called only when running on Android 4.2 or later. + * With these older versions, JavaScript could use reflection to access an + * injected object's public fields. Use of this method in a WebView + * containing untrusted content could allow an attacker to manipulate the + * host application in unintended ways, executing Java code with the + * permissions of the host application. Use extreme care when using this + * method in a WebView which could contain untrusted content.
    • + *
    • JavaScript interacts with Java object on a private, background + * thread of this WebView. Care is therefore required to maintain thread + * safety. + *
    • + *
    • The Java object's fields are not accessible.
    • + *
    • For applications targeted to API level {@link android.os.Build.VERSION_CODES#LOLLIPOP} + * and above, methods of injected Java objects are enumerable from + * JavaScript.
    • + *
    + * + * @param object the Java object to inject into this WebView's JavaScript + * context. {@code null} values are ignored. + * @param name the name used to expose the object in JavaScript + */ + public void addJavascriptInterface(Object object, String name) { + } + /** + * Removes a previously injected Java object from this WebView. Note that + * the removal will not be reflected in JavaScript until the page is next + * (re)loaded. See {@link #addJavascriptInterface}. + * + * @param name the name used to expose the object in JavaScript + */ + public void removeJavascriptInterface(String name) { + } + + /** + * Gets the WebSettings object used to control the settings for this + * WebView. + * + * @return a WebSettings object that can be used to control this WebView's + * settings + */ + public WebSettings getSettings() { + return null; + } + + + +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java new file mode 100644 index 00000000000..4b1bd58498b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.webkit; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +public class WebViewClient { + /** + * Give the host application a chance to take over the control when a new url is + * about to be loaded in the current WebView. If WebViewClient is not provided, + * by default WebView will ask Activity Manager to choose the proper handler for + * the url. If WebViewClient is provided, return {@code true} means the host + * application handles the url, while return {@code false} means the current + * WebView handles the url. This method is not called for requests using the + * POST "method". + * + * @param view The WebView that is initiating the callback. + * @param url The url to be loaded. + * @return {@code true} if the host application wants to leave the current + * WebView and handle the url itself, otherwise return {@code false}. + * @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest) + * shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead. + */ + @Deprecated + public boolean shouldOverrideUrlLoading(WebView view, String url) { + return false; + } + + /** + * Give the host application a chance to take over the control when a new url is + * about to be loaded in the current WebView. If WebViewClient is not provided, + * by default WebView will ask Activity Manager to choose the proper handler for + * the url. If WebViewClient is provided, return {@code true} means the host + * application handles the url, while return {@code false} means the current + * WebView handles the url. + * + *

    + * Notes: + *

      + *
    • This method is not called for requests using the POST + * "method".
    • + *
    • This method is also called for subframes with non-http schemes, thus it + * is strongly disadvised to unconditionally call + * {@link WebView#loadUrl(String)} with the request's url from inside the method + * and then return {@code true}, as this will make WebView to attempt loading a + * non-http url, and thus fail.
    • + *
    + * + * @param view The WebView that is initiating the callback. + * @param request Object containing the details of the request. + * @return {@code true} if the host application wants to leave the current + * WebView and handle the url itself, otherwise return {@code false}. + */ + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + return false; + } + + /** + * Notify the host application that a page has finished loading. This method is + * called only for main frame. When onPageFinished() is called, the rendering + * picture may not be updated yet. To get the notification for the new Picture, + * use {@link WebView.PictureListener#onNewPicture}. + * + * @param view The WebView that is initiating the callback. + * @param url The url of the page. + */ + public void onPageFinished(WebView view, String url) { + } + + /** + * Notify the host application that the WebView will load the resource specified + * by the given url. + * + * @param view The WebView that is initiating the callback. + * @param url The url of the resource the WebView will load. + */ + public void onLoadResource(WebView view, String url) { + } + + /** + * Notify the host application that {@link android.webkit.WebView} content left + * over from previous page navigations will no longer be drawn. + * + *

    + * This callback can be used to determine the point at which it is safe to make + * a recycled {@link android.webkit.WebView} visible, ensuring that no stale + * content is shown. It is called at the earliest point at which it can be + * guaranteed that {@link WebView#onDraw} will no longer draw any content from + * previous navigations. The next draw will display either the + * {@link WebView#setBackgroundColor background color} of the {@link WebView}, + * or some of the contents of the newly loaded page. + * + *

    + * This method is called when the body of the HTTP response has started loading, + * is reflected in the DOM, and will be visible in subsequent draws. This + * callback occurs early in the document loading process, and as such you should + * expect that linked resources (for example, CSS and images) may not be + * available. + * + *

    + * For more fine-grained notification of visual state updates, see + * {@link WebView#postVisualStateCallback}. + * + *

    + * Please note that all the conditions and recommendations applicable to + * {@link WebView#postVisualStateCallback} also apply to this API. + * + *

    + * This callback is only called for main frame navigations. + * + * @param view The {@link android.webkit.WebView} for which the navigation + * occurred. + * @param url The URL corresponding to the page navigation that triggered this + * callback. + */ + public void onPageCommitVisible(WebView view, String url) { + } + + /** + * Notify the host application of a resource request and allow the application + * to return the data. If the return value is {@code null}, the WebView will + * continue to load the resource as usual. Otherwise, the return response and + * data will be used. + * + *

    + * This callback is invoked for a variety of URL schemes (e.g., + * {@code http(s):}, {@code + * data:}, {@code file:}, etc.), not only those schemes which send requests over + * the network. This is not called for {@code javascript:} URLs, {@code blob:} + * URLs, or for assets accessed via {@code file:///android_asset/} or + * {@code file:///android_res/} URLs. + * + *

    + * In the case of redirects, this is only called for the initial resource URL, + * not any subsequent redirect URLs. + * + *

    + * Note: This method is called on a thread other than the UI thread so + * clients should exercise caution when accessing private data or the view + * system. + * + *

    + * Note: When Safe Browsing is enabled, these URLs still undergo Safe + * Browsing checks. If this is undesired, whitelist the URL with + * {@link WebView#setSafeBrowsingWhitelist} or ignore the warning with + * {@link #onSafeBrowsingHit}. + * + * @param view The {@link android.webkit.WebView} that is requesting the + * resource. + * @param url The raw url of the resource. + * @return A {@link android.webkit.WebResourceResponse} containing the response + * information or {@code null} if the WebView should load the resource + * itself. + * @deprecated Use {@link #shouldInterceptRequest(WebView, WebResourceRequest) + * shouldInterceptRequest(WebView, WebResourceRequest)} instead. + */ + @Deprecated + public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + return null; + } + + /** + * Notify the host application of a resource request and allow the application + * to return the data. If the return value is {@code null}, the WebView will + * continue to load the resource as usual. Otherwise, the return response and + * data will be used. + * + *

    + * This callback is invoked for a variety of URL schemes (e.g., + * {@code http(s):}, {@code + * data:}, {@code file:}, etc.), not only those schemes which send requests over + * the network. This is not called for {@code javascript:} URLs, {@code blob:} + * URLs, or for assets accessed via {@code file:///android_asset/} or + * {@code file:///android_res/} URLs. + * + *

    + * In the case of redirects, this is only called for the initial resource URL, + * not any subsequent redirect URLs. + * + *

    + * Note: This method is called on a thread other than the UI thread so + * clients should exercise caution when accessing private data or the view + * system. + * + *

    + * Note: When Safe Browsing is enabled, these URLs still undergo Safe + * Browsing checks. If this is undesired, whitelist the URL with + * {@link WebView#setSafeBrowsingWhitelist} or ignore the warning with + * {@link #onSafeBrowsingHit}. + * + * @param view The {@link android.webkit.WebView} that is requesting the + * resource. + * @param request Object containing the details of the request. + * @return A {@link android.webkit.WebResourceResponse} containing the response + * information or {@code null} if the WebView should load the resource + * itself. + */ + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { + return null; + } + + /** + * Report an error to the host application. These errors are unrecoverable (i.e. + * the main resource is unavailable). The {@code errorCode} parameter + * corresponds to one of the {@code ERROR_*} constants. + * + * @param view The WebView that is initiating the callback. + * @param errorCode The error code corresponding to an ERROR_* value. + * @param description A String describing the error. + * @param failingUrl The url that failed to load. + * @deprecated Use + * {@link #onReceivedError(WebView, WebResourceRequest, WebResourceError) + * onReceivedError(WebView, WebResourceRequest, WebResourceError)} + * instead. + */ + @Deprecated + public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + } + +} From ebc2bd9a58033cfbf630ea0b433b393a54c4e89a Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 15 Jun 2020 21:26:39 +0000 Subject: [PATCH 120/166] Text changes to the help file --- .../Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp index fcae00945a2..653b1a6a2a5 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp @@ -13,11 +13,11 @@ -

    Only allow trusted web contents to be displayed in WebViews when JavaScript is enabled. And disallow universal resource access in WebSetting to reduce the attack surface .

    +

    Only allow trusted web contents to be displayed in WebViews when JavaScript is enabled. And disallow universal resource access in WebSettings to reduce the attack surface .

    -

    The following example shows both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, setting is enabled and JavaScript is enabled while urls are loaded from externally controlled inputs. In the 'GOOD' configuration, JavaScript is disabled or only trusted web contents are allowed to be loaded.

    +

    The following example shows both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, universal resource access is enabled and JavaScript is enabled while urls are loaded from externally controlled inputs. In the 'GOOD' configuration, JavaScript is disabled or only trusted web contents are allowed to be loaded.

    @@ -28,4 +28,4 @@ OWASP - Testing WebView Protocol Handlers (MSTG-PLATFORM-5 and MSTG-PLATFORM-6)
    -
    \ No newline at end of file + From 55af37312b451a2f6cbc13214834b5aad064e6c4 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Tue, 16 Jun 2020 01:52:03 +0000 Subject: [PATCH 121/166] Text changes to the help file --- .../experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp index 653b1a6a2a5..19810b6b904 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp @@ -13,7 +13,7 @@ -

    Only allow trusted web contents to be displayed in WebViews when JavaScript is enabled. And disallow universal resource access in WebSettings to reduce the attack surface .

    +

    Only allow trusted web contents to be displayed in WebViews when JavaScript is enabled. And disallow universal resource access in WebSettings to reduce the attack surface.

    From 53383326481b7db89364f6501bc8faf307ab2682 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 14 Oct 2020 03:45:47 +0000 Subject: [PATCH 122/166] Enhance the query and add more test cases --- .../CWE/CWE-749/UnsafeAndroidAccess.java | 29 ++++++-- .../CWE/CWE-749/UnsafeAndroidAccess.qhelp | 10 +-- .../CWE/CWE-749/UnsafeAndroidAccess.ql | 66 +++++++++--------- .../CWE-749/UnsafeAndroidAccess.expected | 3 +- .../security/CWE-749/UnsafeAndroidAccess.java | 68 ++++++++++++++++++- .../CWE-749/UnsafeAndroidAccess.qlref | 2 +- 6 files changed, 129 insertions(+), 49 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java index 1ba5e94590a..e52dbb3b026 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java @@ -20,19 +20,36 @@ public class UnsafeAndroidAccess extends Activity { } }); - String thisUrl = getIntent().getExtras().getString("url"); // dangerous remote input - //String thisUrl = getIntent().getStringExtra("url"); //An alternative way to load dangerous remote input + String thisUrl = getIntent().getExtras().getString("url"); // dangerous remote input from the intent's Bundle of extras wv.loadUrl(thisUrl); } - // GOOD: Have JavaScript and universal resource access disabled while taking - // remote user inputs + // BAD: Have both JavaScript and universal resource access enabled in webview while + // taking remote user inputs + { + WebView wv = (WebView) findViewById(R.id.my_webview); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowUniversalAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getStringExtra("url"); //dangerous remote input from intent extra + wv.loadUrl(thisUrl); + } + + // GOOD: Have JavaScript and universal resource access disabled by default on modern Android (Jellybean+) while taking remote user inputs { WebView wv = (WebView) findViewById(-1); WebSettings webSettings = wv.getSettings(); - webSettings.setJavaScriptEnabled(false); - wv.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp index 19810b6b904..0deffecfc7f 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp @@ -2,9 +2,9 @@ -

    Android WebViews that allow loading urls controlled by external inputs and whose JavaScript interface is enabled are potentially vulnerable to cross-site scripting and sensitive resource disclosure attacks.

    -

    WebSettings of WebViews with setAllowFileAccessFromFileURLs or setAllowUniversalAccessFromFileURLs enabled must not load any untrusted web content.

    -

    Enabling this setting allows malicious scripts loaded in a file:// context to launch cross-site scripting attacks, either accessing arbitrary local files including WebView cookies, session tokens, private app data or even credentials used on arbitrary web sites.

    +

    Android WebViews that allow loading URLs controlled by external inputs and whose JavaScript interface is enabled are potentially vulnerable to cross-site scripting and sensitive resource disclosure attacks.

    +

    A WebView whose WebSettings object has setAllowFileAccessFromFileURLs(true) or setAllowUniversalAccessFromFileURLs(true) called must not load any untrusted web content.

    +

    Enabling these settings allows malicious scripts loaded in a file:// context to launch cross-site scripting attacks, either accessing arbitrary local files including WebView cookies, session tokens, private app data or even credentials used on arbitrary web sites.

    This query detects the following two scenarios:

    1. Vulnerability introduced by WebViews with JavaScript enabled and remote inputs allowed.
    2. @@ -13,11 +13,11 @@ -

      Only allow trusted web contents to be displayed in WebViews when JavaScript is enabled. And disallow universal resource access in WebSettings to reduce the attack surface.

      +

      Only allow trusted web content to be displayed in WebViews when JavaScript is enabled. Disallow universal resource access in WebSetting to reduce the attack surface .

      -

      The following example shows both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, universal resource access is enabled and JavaScript is enabled while urls are loaded from externally controlled inputs. In the 'GOOD' configuration, JavaScript is disabled or only trusted web contents are allowed to be loaded.

      +

      The following example shows both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration, setting is enabled and JavaScript is enabled while URLs are loaded from externally controlled inputs. In the 'GOOD' configuration, JavaScript is disabled or only trusted web content is allowed to be loaded.

      diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql index 1f40975d9af..c1f3387caa3 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql @@ -1,5 +1,5 @@ /** - * @name Unsafe resource loading in Android webview + * @name Unsafe resource fetching in Android webview * @description JavaScript rendered inside WebViews can access any protected application file and web resource from any origin * @kind path-problem * @tags security @@ -13,10 +13,10 @@ import semmle.code.java.frameworks.android.WebView import semmle.code.java.dataflow.FlowSources /** - * Allow universal access methods in the WebSettings class + * Methods allowing any-local-file and universal access in the WebSettings class */ -class AllowUniversalAccessMethod extends Method { - AllowUniversalAccessMethod() { +class CrossOriginAccessMethod extends Method { + CrossOriginAccessMethod() { this.getDeclaringType() instanceof TypeWebSettings and ( this.hasName("setAllowUniversalAccessFromFileURLs") or @@ -36,22 +36,22 @@ class AllowJavaScriptMethod extends Method { } /** - * Check whether JavaScript is enabled in the webview with universal resource access + * Holds if `ma` is a method invocation against `va` and `va.setJavaScriptEnabled(true)` occurs elsewhere in the program */ -predicate isJSEnabled(MethodAccess ma) { +predicate isJSEnabled(Variable v) { exists(VarAccess va, MethodAccess jsa | - ma.getQualifier() = va and - jsa.getQualifier() = va.getVariable().getAnAccess() and + v.getAnAccess() = va and + jsa.getQualifier() = v.getAnAccess() and jsa.getMethod() instanceof AllowJavaScriptMethod and jsa.getArgument(0).(BooleanLiteral).getBooleanValue() = true ) } /** - * Load URL method call on the `android.webkit.WebView` object + * Fetch URL method call on the `android.webkit.WebView` object */ -class LoadResourceMethodAccess extends MethodAccess { - LoadResourceMethodAccess() { +class FetchResourceMethodAccess extends MethodAccess { + FetchResourceMethodAccess() { this.getMethod().getDeclaringType() instanceof TypeWebView and ( this.getMethod().hasName("loadUrl") or @@ -75,12 +75,11 @@ class IntentGetExtraMethodAccess extends MethodAccess { } /** - * Source of loading urls + * Source of fetching urls */ class UntrustedResourceSource extends RemoteFlowSource { UntrustedResourceSource() { - exists(MethodAccess ma | - ma instanceof IntentGetExtraMethodAccess and + exists(IntentGetExtraMethodAccess ma | this.asExpr().(VarAccess).getVariable().getAnAssignedValue() = ma ) } @@ -88,52 +87,49 @@ class UntrustedResourceSource extends RemoteFlowSource { override string getSourceType() { result = "user input vulnerable to XSS and sensitive resource disclosure attacks" and exists(MethodAccess ma | - ma.getMethod() instanceof AllowUniversalAccessMethod and //High precision match of unsafe resource loading + ma.getMethod() instanceof CrossOriginAccessMethod and //High precision match of unsafe resource fetching ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true ) or result = "user input potentially vulnerable to XSS and sensitive resource disclosure attacks" and not exists(MethodAccess ma | - ma.getMethod() instanceof AllowUniversalAccessMethod and //High precision match of unsafe resource loading + ma.getMethod() instanceof CrossOriginAccessMethod and //High precision match of unsafe resource fetching ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true ) } } /** - * load externally controlled data from loadUntrustedResource + * Holds if `ma` loads url `sink` */ -predicate loadUntrustedResource(MethodAccess ma, Expr sink) { - ma instanceof LoadResourceMethodAccess and - sink = ma.getArgument(0) -} +predicate fetchResource(FetchResourceMethodAccess ma, Expr sink) { sink = ma.getArgument(0) } /** - * Sink of loading urls + * Sink of fetching urls */ -class UntrustedResourceSink extends DataFlow::ExprNode { - UntrustedResourceSink() { loadUntrustedResource(_, this.getExpr()) } +class UrlResourceSink extends DataFlow::ExprNode { + UrlResourceSink() { fetchResource(_, this.getExpr()) } - MethodAccess getMethodAccess() { loadUntrustedResource(result, this.getExpr()) } + FetchResourceMethodAccess getMethodAccess() { fetchResource(result, this.getExpr()) } } -class LoadUntrustedResourceConfiguration extends TaintTracking::Configuration { - LoadUntrustedResourceConfiguration() { this = "LoadUntrustedResourceConfiguration" } +class FetchUntrustedResourceConfiguration extends TaintTracking::Configuration { + FetchUntrustedResourceConfiguration() { this = "FetchUntrustedResourceConfiguration" } override predicate isSource(DataFlow::Node source) { source instanceof UntrustedResourceSource } - override predicate isSink(DataFlow::Node sink) { sink instanceof UntrustedResourceSink } + override predicate isSink(DataFlow::Node sink) { sink instanceof UrlResourceSink } } -from DataFlow::PathNode source, DataFlow::PathNode sink, LoadUntrustedResourceConfiguration conf +from DataFlow::PathNode source, DataFlow::PathNode sink, FetchUntrustedResourceConfiguration conf where - exists(VarAccess webviewVa, MethodAccess getSettingsMa, MethodAccess ma | + exists(VarAccess webviewVa, MethodAccess getSettingsMa, Variable v | conf.hasFlowPath(source, sink) and - sink.getNode().(UntrustedResourceSink).getMethodAccess().getQualifier() = webviewVa and + sink.getNode().(UrlResourceSink).getMethodAccess().getQualifier() = webviewVa and webviewVa.getVariable().getAnAccess() = getSettingsMa.getQualifier() and - ma.getQualifier().(VarAccess).getVariable().getAnAssignedValue() = getSettingsMa and - isJSEnabled(ma) + v.getAnAssignedValue() = getSettingsMa and + isJSEnabled(v) ) -select sink.getNode().(UntrustedResourceSink).getMethodAccess(), source, sink, - "Unsafe resource loading in Android webview due to $@.", source.getNode(), +select sink.getNode().(UrlResourceSink).getMethodAccess(), source, sink, + "Unsafe resource fetching in Android webview due to $@.", source.getNode(), source.getNode().(UntrustedResourceSource).getSourceType() diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected index f1795e649cd..b8766854b4d 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected @@ -1 +1,2 @@ -| UnsafeAndroidAccess.java:29:3:29:21 | loadUrl(...) | UnsafeAndroidAccess.java:29:14:29:20 | thisUrl | UnsafeAndroidAccess.java:29:14:29:20 | thisUrl | Unsafe resource loading in Android webview due to $@. | UnsafeAndroidAccess.java:29:14:29:20 | thisUrl | user input vulnerable to XSS and sensitive resource disclosure attacks | +| UnsafeAndroidAccess.java:30:3:30:21 | loadUrl(...) | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | user input vulnerable to XSS and sensitive resource disclosure attacks | +| UnsafeAndroidAccess.java:53:3:53:21 | loadUrl(...) | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | user input vulnerable to XSS and sensitive resource disclosure attacks | diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java index 01c28912729..6811dfdd212 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java @@ -7,7 +7,8 @@ import android.webkit.WebView; import android.webkit.WebViewClient; public class UnsafeAndroidAccess extends Activity { - public void onCreate(Bundle savedInstanceState) { + //Test onCreate with both JavaScript and universal resource access enabled while taking remote user inputs from bundle extras + public void testOnCreate1(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(-1); @@ -28,4 +29,69 @@ public class UnsafeAndroidAccess extends Activity { String thisUrl = getIntent().getExtras().getString("url"); wv.loadUrl(thisUrl); } + + //Test onCreate with both JavaScript and universal resource access enabled while taking remote user inputs from string extra + public void testOnCreate2(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowFileAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getStringExtra("url"); + wv.loadUrl(thisUrl); + } + + //Test onCreate with both JavaScript and universal resource access disabled by default while taking remote user inputs + public void testOnCreate3(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getStringExtra("url"); + wv.loadUrl(thisUrl); + } + + //Test onCreate with both JavaScript and universal resource access enabled while not taking remote user inputs + public void testOnCreate4(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + + webSettings.setJavaScriptEnabled(true); + webSettings.setAllowFileAccessFromFileURLs(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + wv.loadUrl("https://www.mycorp.com"); + } } \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.qlref b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.qlref index 3f41abfe566..abaed120e99 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.qlref +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.qlref @@ -1 +1 @@ -experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql +experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql \ No newline at end of file From c7750fd8c2a4b54ae2eeb3d2b94704480d71a1e9 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 14 Oct 2020 16:16:15 +0000 Subject: [PATCH 123/166] Fine tune the query --- .../CWE/CWE-749/UnsafeAndroidAccess.java | 6 +- .../CWE/CWE-749/UnsafeAndroidAccess.qhelp | 6 +- .../CWE/CWE-749/UnsafeAndroidAccess.ql | 67 ++++++++++--------- .../CWE-749/UnsafeAndroidAccess.expected | 5 +- .../security/CWE-749/UnsafeAndroidAccess.java | 30 +++++++-- 5 files changed, 70 insertions(+), 44 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java index e52dbb3b026..d05700dd2a4 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.java @@ -3,7 +3,7 @@ public class UnsafeAndroidAccess extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.webview); - // BAD: Have both JavaScript and universal resource access enabled in webview while + // BAD: Have both JavaScript and cross-origin resource access enabled in webview while // taking remote user inputs { WebView wv = (WebView) findViewById(R.id.my_webview); @@ -24,7 +24,7 @@ public class UnsafeAndroidAccess extends Activity { wv.loadUrl(thisUrl); } - // BAD: Have both JavaScript and universal resource access enabled in webview while + // BAD: Have both JavaScript and cross-origin resource access enabled in webview while // taking remote user inputs { WebView wv = (WebView) findViewById(R.id.my_webview); @@ -45,7 +45,7 @@ public class UnsafeAndroidAccess extends Activity { wv.loadUrl(thisUrl); } - // GOOD: Have JavaScript and universal resource access disabled by default on modern Android (Jellybean+) while taking remote user inputs + // GOOD: Have JavaScript and cross-origin resource access disabled by default on modern Android (Jellybean+) while taking remote user inputs { WebView wv = (WebView) findViewById(-1); WebSettings webSettings = wv.getSettings(); diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp index 0deffecfc7f..1ef21e0c3e1 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp @@ -8,12 +8,12 @@

      This query detects the following two scenarios:

      1. Vulnerability introduced by WebViews with JavaScript enabled and remote inputs allowed.
      2. -
      3. High precision vulnerability when allowing universal resource access is also enabled. The setting was just deprecated in API level 30 (Android 11) thus most devices are still affected given that Android phones don't get timely version updates like iPhones.
      4. +
      5. A more severe vulnerability when allowing cross-origin resource access is also enabled. The setting was deprecated in API level 30 (Android 11), but most devices are still affected, especially given that some Android phones are updated slowly or no longer updated at all.
      -

      Only allow trusted web content to be displayed in WebViews when JavaScript is enabled. Disallow universal resource access in WebSetting to reduce the attack surface .

      +

      Only allow trusted web content to be displayed in WebViews when JavaScript is enabled. Disallow cross-origin resource access in WebSetting to reduce the attack surface .

      @@ -28,4 +28,4 @@ OWASP - Testing WebView Protocol Handlers (MSTG-PLATFORM-5 and MSTG-PLATFORM-6) - + \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql index c1f3387caa3..bbf788bc10d 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql @@ -13,7 +13,7 @@ import semmle.code.java.frameworks.android.WebView import semmle.code.java.dataflow.FlowSources /** - * Methods allowing any-local-file and universal access in the WebSettings class + * Methods allowing any-local-file and cross-origin access in the WebSettings class */ class CrossOriginAccessMethod extends Method { CrossOriginAccessMethod() { @@ -36,12 +36,11 @@ class AllowJavaScriptMethod extends Method { } /** - * Holds if `ma` is a method invocation against `va` and `va.setJavaScriptEnabled(true)` occurs elsewhere in the program + * Holds if a call to `v.setJavaScriptEnabled(true)` exists */ predicate isJSEnabled(Variable v) { - exists(VarAccess va, MethodAccess jsa | - v.getAnAccess() = va and - jsa.getQualifier() = v.getAnAccess() and + exists(MethodAccess jsa | + v.getAnAccess() = jsa.getQualifier() and jsa.getMethod() instanceof AllowJavaScriptMethod and jsa.getArgument(0).(BooleanLiteral).getBooleanValue() = true ) @@ -53,10 +52,7 @@ predicate isJSEnabled(Variable v) { class FetchResourceMethodAccess extends MethodAccess { FetchResourceMethodAccess() { this.getMethod().getDeclaringType() instanceof TypeWebView and - ( - this.getMethod().hasName("loadUrl") or - this.getMethod().hasName("postUrl") - ) + this.getMethod().hasName(["loadUrl", "postUrl"]) } } @@ -84,19 +80,7 @@ class UntrustedResourceSource extends RemoteFlowSource { ) } - override string getSourceType() { - result = "user input vulnerable to XSS and sensitive resource disclosure attacks" and - exists(MethodAccess ma | - ma.getMethod() instanceof CrossOriginAccessMethod and //High precision match of unsafe resource fetching - ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true - ) - or - result = "user input potentially vulnerable to XSS and sensitive resource disclosure attacks" and - not exists(MethodAccess ma | - ma.getMethod() instanceof CrossOriginAccessMethod and //High precision match of unsafe resource fetching - ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true - ) - } + override string getSourceType() { result = "UntrustedIntentExtraSource" } } /** @@ -111,6 +95,23 @@ class UrlResourceSink extends DataFlow::ExprNode { UrlResourceSink() { fetchResource(_, this.getExpr()) } FetchResourceMethodAccess getMethodAccess() { fetchResource(result, this.getExpr()) } + + predicate crossOriginAccessEnabled() { + exists(MethodAccess ma, MethodAccess getSettingsMa | + ma.getMethod() instanceof CrossOriginAccessMethod and // Unsafe resource fetching of more severe vulnerabilities + ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true and + ma.getQualifier().(VarAccess).getVariable().getAnAssignedValue() = getSettingsMa and + getSettingsMa.getMethod() instanceof WebViewGetSettingsMethod and + getSettingsMa.getQualifier().(VarAccess).getVariable().getAnAccess() = + getMethodAccess().getQualifier() + ) + } + + string getSinkType() { + if crossOriginAccessEnabled() + then result = "user input vulnerable to cross-origin and sensitive resource disclosure attacks" + else result = "user input vulnerable to XSS attacks" + } } class FetchUntrustedResourceConfiguration extends TaintTracking::Configuration { @@ -118,18 +119,20 @@ class FetchUntrustedResourceConfiguration extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof UntrustedResourceSource } - override predicate isSink(DataFlow::Node sink) { sink instanceof UrlResourceSink } + override predicate isSink(DataFlow::Node sink) { + sink instanceof UrlResourceSink and + exists(VarAccess webviewVa, MethodAccess getSettingsMa, Variable v | + sink.(UrlResourceSink).getMethodAccess().getQualifier() = webviewVa and + getSettingsMa.getMethod() instanceof WebViewGetSettingsMethod and + webviewVa.getVariable().getAnAccess() = getSettingsMa.getQualifier() and + v.getAnAssignedValue() = getSettingsMa and + isJSEnabled(v) + ) + } } from DataFlow::PathNode source, DataFlow::PathNode sink, FetchUntrustedResourceConfiguration conf -where - exists(VarAccess webviewVa, MethodAccess getSettingsMa, Variable v | - conf.hasFlowPath(source, sink) and - sink.getNode().(UrlResourceSink).getMethodAccess().getQualifier() = webviewVa and - webviewVa.getVariable().getAnAccess() = getSettingsMa.getQualifier() and - v.getAnAssignedValue() = getSettingsMa and - isJSEnabled(v) - ) +where conf.hasFlowPath(source, sink) select sink.getNode().(UrlResourceSink).getMethodAccess(), source, sink, "Unsafe resource fetching in Android webview due to $@.", source.getNode(), - source.getNode().(UntrustedResourceSource).getSourceType() + sink.getNode().(UrlResourceSink).getSinkType() diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected index b8766854b4d..596a02ffcff 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected @@ -1,2 +1,3 @@ -| UnsafeAndroidAccess.java:30:3:30:21 | loadUrl(...) | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | user input vulnerable to XSS and sensitive resource disclosure attacks | -| UnsafeAndroidAccess.java:53:3:53:21 | loadUrl(...) | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | user input vulnerable to XSS and sensitive resource disclosure attacks | +| UnsafeAndroidAccess.java:30:3:30:21 | loadUrl(...) | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | user input vulnerable to cross-origin and sensitive resource disclosure attacks | +| UnsafeAndroidAccess.java:53:3:53:21 | loadUrl(...) | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | user input vulnerable to cross-origin and sensitive resource disclosure attacks | +| UnsafeAndroidAccess.java:95:3:95:21 | loadUrl(...) | UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | user input vulnerable to XSS attacks | diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java index 6811dfdd212..8a929120bf8 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.java @@ -7,7 +7,7 @@ import android.webkit.WebView; import android.webkit.WebViewClient; public class UnsafeAndroidAccess extends Activity { - //Test onCreate with both JavaScript and universal resource access enabled while taking remote user inputs from bundle extras + //Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from bundle extras public void testOnCreate1(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(-1); @@ -30,7 +30,7 @@ public class UnsafeAndroidAccess extends Activity { wv.loadUrl(thisUrl); } - //Test onCreate with both JavaScript and universal resource access enabled while taking remote user inputs from string extra + //Test onCreate with both JavaScript and cross-origin resource access enabled while taking remote user inputs from string extra public void testOnCreate2(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(-1); @@ -53,7 +53,7 @@ public class UnsafeAndroidAccess extends Activity { wv.loadUrl(thisUrl); } - //Test onCreate with both JavaScript and universal resource access disabled by default while taking remote user inputs + //Test onCreate with both JavaScript and cross-origin resource access disabled by default while taking remote user inputs public void testOnCreate3(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(-1); @@ -73,7 +73,7 @@ public class UnsafeAndroidAccess extends Activity { wv.loadUrl(thisUrl); } - //Test onCreate with both JavaScript and universal resource access enabled while not taking remote user inputs + //Test onCreate with JavaScript enabled but cross-origin resource access disabled while taking remote user inputs public void testOnCreate4(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(-1); @@ -81,6 +81,28 @@ public class UnsafeAndroidAccess extends Activity { WebView wv = (WebView) findViewById(-1); WebSettings webSettings = wv.getSettings(); + webSettings.setJavaScriptEnabled(true); + + wv.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + view.loadUrl(url); + return true; + } + }); + + String thisUrl = getIntent().getStringExtra("url"); + wv.loadUrl(thisUrl); + } + + //Test onCreate with both JavaScript and cross-origin resource access enabled while not taking remote user inputs + public void testOnCreate5(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(-1); + + WebView wv = (WebView) findViewById(-1); + WebSettings webSettings = wv.getSettings(); + webSettings.setJavaScriptEnabled(true); webSettings.setAllowFileAccessFromFileURLs(true); From f5e969059466edcd5c1bf44b86344cd03ad84db2 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Thu, 15 Oct 2020 10:52:30 +0000 Subject: [PATCH 124/166] Update the doc comments --- .../CWE/CWE-749/UnsafeAndroidAccess.qhelp | 2 +- .../CWE/CWE-749/UnsafeAndroidAccess.ql | 22 +++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp index 1ef21e0c3e1..3ac8ec67e59 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.qhelp @@ -13,7 +13,7 @@ -

      Only allow trusted web content to be displayed in WebViews when JavaScript is enabled. Disallow cross-origin resource access in WebSetting to reduce the attack surface .

      +

      Only allow trusted web content to be displayed in WebViews when JavaScript is enabled. Disallow cross-origin resource access in WebSetting to reduce the attack surface.

      diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql index bbf788bc10d..204b030a17c 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql @@ -71,7 +71,7 @@ class IntentGetExtraMethodAccess extends MethodAccess { } /** - * Source of fetching urls + * Source of fetching URLs */ class UntrustedResourceSource extends RemoteFlowSource { UntrustedResourceSource() { @@ -84,21 +84,28 @@ class UntrustedResourceSource extends RemoteFlowSource { } /** - * Holds if `ma` loads url `sink` + * Holds if `ma` loads URL `sink` */ predicate fetchResource(FetchResourceMethodAccess ma, Expr sink) { sink = ma.getArgument(0) } /** - * Sink of fetching urls + * A URL argument to a `loadUrl` or `postUrl` call, considered as a sink. */ class UrlResourceSink extends DataFlow::ExprNode { UrlResourceSink() { fetchResource(_, this.getExpr()) } + /** Gets the fetch method that fetches this sink URL. */ FetchResourceMethodAccess getMethodAccess() { fetchResource(result, this.getExpr()) } + /** + * Holds if cross-origin access is enabled for this resource fetch. + * + * Specifically this looks for code like + * `webView.getSettings().setAllow[File|Universal]AccessFromFileURLs(true);` + */ predicate crossOriginAccessEnabled() { exists(MethodAccess ma, MethodAccess getSettingsMa | - ma.getMethod() instanceof CrossOriginAccessMethod and // Unsafe resource fetching of more severe vulnerabilities + ma.getMethod() instanceof CrossOriginAccessMethod and ma.getArgument(0).(BooleanLiteral).getBooleanValue() = true and ma.getQualifier().(VarAccess).getVariable().getAnAssignedValue() = getSettingsMa and getSettingsMa.getMethod() instanceof WebViewGetSettingsMethod and @@ -107,6 +114,10 @@ class UrlResourceSink extends DataFlow::ExprNode { ) } + /** + * Returns a description of this vulnerability, assuming Javascript is enabled and + * the fetched URL is attacker-controlled. + */ string getSinkType() { if crossOriginAccessEnabled() then result = "user input vulnerable to cross-origin and sensitive resource disclosure attacks" @@ -114,6 +125,9 @@ class UrlResourceSink extends DataFlow::ExprNode { } } +/** + * Taint configuration tracking flow from untrusted inputs to `loadUrl` or `postUrl` calls. + */ class FetchUntrustedResourceConfiguration extends TaintTracking::Configuration { FetchUntrustedResourceConfiguration() { this = "FetchUntrustedResourceConfiguration" } From 6f6ec9d51a4432c7bdbebc4b874085fbd2270247 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Thu, 15 Oct 2020 11:17:10 +0000 Subject: [PATCH 125/166] Change the source class type and simplify the data-flow step --- .../Security/CWE/CWE-749/UnsafeAndroidAccess.ql | 12 +++--------- .../security/CWE-749/UnsafeAndroidAccess.expected | 6 +++--- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql index 204b030a17c..74e6c060c98 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql @@ -71,16 +71,10 @@ class IntentGetExtraMethodAccess extends MethodAccess { } /** - * Source of fetching URLs + * Source of fetching URLs from intent extras */ -class UntrustedResourceSource extends RemoteFlowSource { - UntrustedResourceSource() { - exists(IntentGetExtraMethodAccess ma | - this.asExpr().(VarAccess).getVariable().getAnAssignedValue() = ma - ) - } - - override string getSourceType() { result = "UntrustedIntentExtraSource" } +class UntrustedResourceSource extends DataFlow::ExprNode { + UntrustedResourceSource() { this.asExpr() instanceof IntentGetExtraMethodAccess } } /** diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected index 596a02ffcff..800af691785 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected @@ -1,3 +1,3 @@ -| UnsafeAndroidAccess.java:30:3:30:21 | loadUrl(...) | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | user input vulnerable to cross-origin and sensitive resource disclosure attacks | -| UnsafeAndroidAccess.java:53:3:53:21 | loadUrl(...) | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | user input vulnerable to cross-origin and sensitive resource disclosure attacks | -| UnsafeAndroidAccess.java:95:3:95:21 | loadUrl(...) | UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | user input vulnerable to XSS attacks | +| UnsafeAndroidAccess.java:30:3:30:21 | loadUrl(...) | UnsafeAndroidAccess.java:29:20:29:59 | getString(...) : String | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:29:20:29:59 | getString(...) | user input vulnerable to cross-origin and sensitive resource disclosure attacks | +| UnsafeAndroidAccess.java:53:3:53:21 | loadUrl(...) | UnsafeAndroidAccess.java:52:20:52:52 | getStringExtra(...) : String | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:52:20:52:52 | getStringExtra(...) | user input vulnerable to cross-origin and sensitive resource disclosure attacks | +| UnsafeAndroidAccess.java:95:3:95:21 | loadUrl(...) | UnsafeAndroidAccess.java:94:20:94:52 | getStringExtra(...) : String | UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:94:20:94:52 | getStringExtra(...) | user input vulnerable to XSS attacks | From b359802dd43893f04db45cbe4f557d96e449ec7d Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Thu, 15 Oct 2020 12:50:39 +0000 Subject: [PATCH 126/166] Replace non-ASCII apostrophe in Java stub classes --- .../stubs/google-android-9.0.0/android/content/Context.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java index 63709ae41b6..8c528a58e41 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Context.java @@ -324,7 +324,7 @@ public abstract class Context { *

      * Starting from {@link android.os.Build.VERSION_CODES#N}, * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission is not - * required, so don’t ask for this permission at runtime. To handle both cases, + * required, so don't ask for this permission at runtime. To handle both cases, * your app must first try to read the OBB file, and if it fails, you must * request {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission * at runtime. From f32a7be8747c5466b7ff945687f1e4a7ac99ea79 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 15 Oct 2020 10:16:13 -0400 Subject: [PATCH 127/166] Fix formatting --- .../cpp/ir/implementation/aliased_ssa/Operand.qll | 4 +--- .../cpp/ir/implementation/aliased_ssa/PrintIR.qll | 11 +++++++++-- .../cpp/ir/implementation/internal/OperandTag.qll | 4 +--- .../semmle/code/cpp/ir/implementation/raw/Operand.qll | 4 +--- .../semmle/code/cpp/ir/implementation/raw/PrintIR.qll | 11 +++++++++-- .../cpp/ir/implementation/unaliased_ssa/Operand.qll | 4 +--- .../cpp/ir/implementation/unaliased_ssa/PrintIR.qll | 11 +++++++++-- .../ir/implementation/internal/OperandTag.qll | 4 +--- .../experimental/ir/implementation/raw/Operand.qll | 4 +--- .../experimental/ir/implementation/raw/PrintIR.qll | 11 +++++++++-- .../ir/implementation/unaliased_ssa/Operand.qll | 4 +--- .../ir/implementation/unaliased_ssa/PrintIR.qll | 11 +++++++++-- 12 files changed, 52 insertions(+), 31 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index d8ae610b2f0..a12e35d471b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -484,9 +484,7 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } - final override string getDumpId() { - result = getPredecessorBlock().getDisplayIndex().toString() - } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } /** * Gets the predecessor block from which this value comes. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll index b26cf972ce4..59dadee7154 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll @@ -62,7 +62,12 @@ private string getAdditionalOperandProperty(Operand operand, string key) { * operand has no properties, this predicate has no result. */ private string getOperandPropertyListString(Operand operand) { - result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ") + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) } /** @@ -247,7 +252,9 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio concat(Operand operand | operand = instr.getAnOperand() | - operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder() + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() ) } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll index 274b6510bd6..21dfedd95cd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll @@ -40,9 +40,7 @@ abstract class OperandTag extends TOperandTag { /** * Gets a label that will appear before the operand when the IR is printed. */ - final string getLabel() { - if alwaysPrintLabel() then result = getId() + ":" else result = "" - } + final string getLabel() { if alwaysPrintLabel() then result = getId() + ":" else result = "" } /** * Gets an identifier that uniquely identifies this operand within its instruction. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index d8ae610b2f0..a12e35d471b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -484,9 +484,7 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } - final override string getDumpId() { - result = getPredecessorBlock().getDisplayIndex().toString() - } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } /** * Gets the predecessor block from which this value comes. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll index b26cf972ce4..59dadee7154 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll @@ -62,7 +62,12 @@ private string getAdditionalOperandProperty(Operand operand, string key) { * operand has no properties, this predicate has no result. */ private string getOperandPropertyListString(Operand operand) { - result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ") + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) } /** @@ -247,7 +252,9 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio concat(Operand operand | operand = instr.getAnOperand() | - operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder() + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() ) } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index d8ae610b2f0..a12e35d471b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -484,9 +484,7 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } - final override string getDumpId() { - result = getPredecessorBlock().getDisplayIndex().toString() - } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } /** * Gets the predecessor block from which this value comes. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll index b26cf972ce4..59dadee7154 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll @@ -62,7 +62,12 @@ private string getAdditionalOperandProperty(Operand operand, string key) { * operand has no properties, this predicate has no result. */ private string getOperandPropertyListString(Operand operand) { - result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ") + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) } /** @@ -247,7 +252,9 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio concat(Operand operand | operand = instr.getAnOperand() | - operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder() + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() ) } } diff --git a/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll b/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll index 274b6510bd6..21dfedd95cd 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll @@ -40,9 +40,7 @@ abstract class OperandTag extends TOperandTag { /** * Gets a label that will appear before the operand when the IR is printed. */ - final string getLabel() { - if alwaysPrintLabel() then result = getId() + ":" else result = "" - } + final string getLabel() { if alwaysPrintLabel() then result = getId() + ":" else result = "" } /** * Gets an identifier that uniquely identifies this operand within its instruction. diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll index d8ae610b2f0..a12e35d471b 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Operand.qll @@ -484,9 +484,7 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } - final override string getDumpId() { - result = getPredecessorBlock().getDisplayIndex().toString() - } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } /** * Gets the predecessor block from which this value comes. diff --git a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll index b26cf972ce4..59dadee7154 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll @@ -62,7 +62,12 @@ private string getAdditionalOperandProperty(Operand operand, string key) { * operand has no properties, this predicate has no result. */ private string getOperandPropertyListString(Operand operand) { - result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ") + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) } /** @@ -247,7 +252,9 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio concat(Operand operand | operand = instr.getAnOperand() | - operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder() + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() ) } } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll index d8ae610b2f0..a12e35d471b 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll @@ -484,9 +484,7 @@ class PhiInputOperand extends MemoryOperand, PhiOperandBase { result = "from " + getPredecessorBlock().getDisplayIndex().toString() + ":" } - final override string getDumpId() { - result = getPredecessorBlock().getDisplayIndex().toString() - } + final override string getDumpId() { result = getPredecessorBlock().getDisplayIndex().toString() } /** * Gets the predecessor block from which this value comes. diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll index b26cf972ce4..59dadee7154 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll @@ -62,7 +62,12 @@ private string getAdditionalOperandProperty(Operand operand, string key) { * operand has no properties, this predicate has no result. */ private string getOperandPropertyListString(Operand operand) { - result = strictconcat(string key, string value | value = getAdditionalOperandProperty(operand, key) | key + ":" + value, ", ") + result = + strictconcat(string key, string value | + value = getAdditionalOperandProperty(operand, key) + | + key + ":" + value, ", " + ) } /** @@ -247,7 +252,9 @@ private class PrintableInstruction extends PrintableIRNode, TPrintableInstructio concat(Operand operand | operand = instr.getAnOperand() | - operand.getDumpString() + getOperandPropertyString(operand), ", " order by operand.getDumpSortOrder() + operand.getDumpString() + getOperandPropertyString(operand), ", " + order by + operand.getDumpSortOrder() ) } } From 89f5352324ceb2e4adfdd00e8e1f305d5c9041ac Mon Sep 17 00:00:00 2001 From: Rasmus Lerchedahl Petersen Date: Thu, 15 Oct 2020 16:41:41 +0200 Subject: [PATCH 128/166] Python: fix QL format --- python/ql/src/experimental/semmle/python/frameworks/Dill.qll | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/python/ql/src/experimental/semmle/python/frameworks/Dill.qll b/python/ql/src/experimental/semmle/python/frameworks/Dill.qll index 579717aa73c..15057bf8083 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Dill.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Dill.qll @@ -45,13 +45,12 @@ private module Dill { */ private class DillLoadsCall extends Decoding::Range, DataFlow::CfgNode { override CallNode node; + DillLoadsCall() { node.getFunction() = Dill::dill::loads().asCfgNode() } override predicate mayExecuteInput() { any() } - override DataFlow::Node getAnInput() { - result.asCfgNode() = node.getArg(0) - } + override DataFlow::Node getAnInput() { result.asCfgNode() = node.getArg(0) } override DataFlow::Node getOutput() { result = this } From afd82e202de98bb82e6c69bff5089aa30299e12d Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Tue, 29 Sep 2020 12:01:47 +0100 Subject: [PATCH 129/166] JS: Add Angular2 model --- javascript/ql/src/javascript.qll | 1 + .../semmle/javascript/frameworks/Angular2.qll | 214 ++++++++++++++++++ .../query-tests/Security/CWE-079/Xss.expected | 85 +++++++ .../Security/CWE-079/angular2-client.ts | 43 ++++ 4 files changed, 343 insertions(+) create mode 100644 javascript/ql/src/semmle/javascript/frameworks/Angular2.qll create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/angular2-client.ts diff --git a/javascript/ql/src/javascript.qll b/javascript/ql/src/javascript.qll index ac1fe785029..a647a5b261e 100644 --- a/javascript/ql/src/javascript.qll +++ b/javascript/ql/src/javascript.qll @@ -65,6 +65,7 @@ import semmle.javascript.YAML import semmle.javascript.dataflow.DataFlow import semmle.javascript.dataflow.TaintTracking import semmle.javascript.dataflow.TypeInference +import semmle.javascript.frameworks.Angular2 import semmle.javascript.frameworks.AngularJS import semmle.javascript.frameworks.AsyncPackage import semmle.javascript.frameworks.AWS diff --git a/javascript/ql/src/semmle/javascript/frameworks/Angular2.qll b/javascript/ql/src/semmle/javascript/frameworks/Angular2.qll new file mode 100644 index 00000000000..89e884d0bb1 --- /dev/null +++ b/javascript/ql/src/semmle/javascript/frameworks/Angular2.qll @@ -0,0 +1,214 @@ +/** + * Provides classes for working with Angular (also known as Angular 2.x) applications. + */ + +private import javascript +private import semmle.javascript.security.dataflow.Xss +private import semmle.javascript.security.dataflow.CodeInjectionCustomizations +private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomizations +private import semmle.javascript.DynamicPropertyAccess + +/** + * Provides classes for working with Angular (also known as Angular 2.x) applications. + */ +module Angular2 { + /** Gets a reference to a `Router` object. */ + DataFlow::SourceNode router() { + result.hasUnderlyingType("@angular/router", "Router") + } + + /** Gets a reference to a `RouterState` object. */ + DataFlow::SourceNode routerState() { + result.hasUnderlyingType("@angular/router", "RouterState") + or + result = router().getAPropertyRead("routerState") + } + + /** Gets a reference to a `RouterStateSnapshot` object. */ + DataFlow::SourceNode routerStateSnapshot() { + result.hasUnderlyingType("@angular/router", "RouterStateSnapshot") + or + result = routerState().getAPropertyRead("snapshot") + } + + /** Gets a reference to an `ActivatedRoute` object. */ + DataFlow::SourceNode activatedRoute() { + result.hasUnderlyingType("@angular/router", "ActivatedRoute") + } + + /** Gets a reference to an `ActivatedRouteSnapshot` object. */ + DataFlow::SourceNode activatedRouteSnapshot() { + result.hasUnderlyingType("@angular/router", "ActivatedRouteSnapshot") + or + result = activatedRoute().getAPropertyRead("snapshot") + } + + /** + * Gets a data flow node referring to the value of the route property `name`, accessed + * via one of the following patterns: + * ```js + * route.snapshot.name + * route.snapshot.data.name + * route.name.subscribe(x => ...) + * ``` + */ + DataFlow::SourceNode activatedRouteProp(string name) { + // this.route.snapshot.foo + result = activatedRouteSnapshot().getAPropertyRead(name) + or + // this.route.snapshot.data.foo + result = activatedRouteSnapshot().getAPropertyRead("data").getAPropertyRead(name) + or + // this.route.foo.subscribe(foo => { ... }) + result = activatedRoute().getAPropertyRead(name).getAMethodCall("subscribe").getABoundCallbackParameter(0, 0) + } + + /** Gets an array of URL segments matched by some route. */ + private DataFlow::SourceNode urlSegmentArray() { + result = activatedRouteProp("url") + } + + /** Gets a data flow node referring to a `UrlSegment` object matched by some route. */ + DataFlow::SourceNode urlSegment() { + result = getAnEnumeratedArrayElement(urlSegmentArray()) + or + result = urlSegmentArray().getAPropertyRead(any(string s | exists(s.toInt()))) + } + + /** Gets a reference to a `ParamMap` object, usually containing values from the URL. */ + DataFlow::SourceNode paramMap() { + result.hasUnderlyingType("@angular/router", "ParamMap") + or + result = activatedRouteProp(["paramMap", "queryParamMap"]) + or + result = urlSegment().getAPropertyRead("parameterMap") + } + + /** Gets a reference to a `Params` object, usually containing values from the URL. */ + DataFlow::SourceNode paramDictionaryObject() { + result.hasUnderlyingType("@angular/router", "Params") and + not result instanceof DataFlow::ObjectLiteralNode // ignore object literals found by contextual typing + or + result = activatedRouteProp(["params", "queryParams"]) + or + result = paramMap().getAPropertyRead("params") + or + result = urlSegment().getAPropertyRead("parameters") + } + + /** + * A value from `@angular/router` derived from the URL. + */ + class AngularSource extends RemoteFlowSource { + AngularSource() { + this = paramMap().getAMethodCall(["get", "getAll"]) + or + this = paramDictionaryObject() + or + this = activatedRouteProp("fragment") + or + this = urlSegment().getAPropertyRead("path") + or + // Note that Router.url and RouterStateSnapshot.url are strings, not UrlSegment[] + this = router().getAPropertyRead("url") + or + this = routerStateSnapshot().getAPropertyRead("url") + } + + override string getSourceType() { + result = "Angular route parameter" + } + } + + /** Gets a reference to a `DomSanitizer` object. */ + DataFlow::SourceNode domSanitizer() { + result.hasUnderlyingType("@angular/platform-browser", "DomSanitizer") + } + + /** A value that is about to be promoted to a trusted HTML or CSS value. */ + private class AngularXssSink extends DomBasedXss::Sink { + AngularXssSink() { this = domSanitizer().getAMethodCall(["bypassSecurityTrustHtml", "bypassSecurityTrustStyle"]).getArgument(0) } + } + + /** A value that is about to be promoted to a trusted script value. */ + private class AngularCodeInjectionSink extends CodeInjection::Sink { + AngularCodeInjectionSink() { this = domSanitizer().getAMethodCall(["bypassSecurityTrustScript"]).getArgument(0) } + } + + /** + * A value that is about to be promoted to a trusted URL or resource URL value. + */ + private class AngularUrlSink extends ClientSideUrlRedirect::Sink { + // We mark this as a client URL redirect sink for precision reasons, though its description can be a bit confusing. + AngularUrlSink() { this = domSanitizer().getAMethodCall(["bypassSecurityTrustUrl", "bypassSecurityTrustResourceUrl"]).getArgument(0) } + } + + private predicate taintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists(DataFlow::CallNode call | + call = DataFlow::moduleMember("@angular/router", "convertToParamMap").getACall() + or + call = router().getAMemberCall(["parseUrl", "serializeUrl"]) + | + pred = call.getArgument(0) and + succ = call + ) + } + + private class AngularTaintStep extends TaintTracking::AdditionalTaintStep { + AngularTaintStep() { + taintStep(_, this) + } + + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + taintStep(pred, succ) + } + } + + /** Gets a reference to an `HttpClient` object. */ + DataFlow::SourceNode httpClient() { + result.hasUnderlyingType("@angular/common/http", "HttpClient") + } + + private class AngularClientRequest extends ClientRequest::Range, DataFlow::MethodCallNode { + int argumentOffset; + + AngularClientRequest() { + this = httpClient().getAMethodCall("request") and argumentOffset = 1 + or + this = httpClient().getAMethodCall() and + not getMethodName() = "request" and + argumentOffset = 0 + } + + override DataFlow::Node getUrl() { + result = getArgument(argumentOffset) + } + + override DataFlow::Node getHost() { + none() + } + + override DataFlow::Node getADataNode() { + getMethodName() = ["patch", "post", "put"] and + result = getArgument(argumentOffset + 1) + or + result = getOptionArgument(argumentOffset + 1, "body") + } + } + + /** Gets a reference to a `DomAdapter`, which provides acess to raw DOM elements. */ + private DataFlow::SourceNode domAdapter() { + // Note: these are internal properties, prefixed with the theta character "ɵ". + // Despite being internal, some codebases do access them. + result.hasUnderlyingType("@angular/common", "ɵDomAdapter") + or + result = DataFlow::moduleImport("@angular/common").getAMemberCall("ɵgetDOM") + } + + /** A reference to the DOM location obtained through `DomAdapter.getLocation()`. */ + private class DomAdapterLocation extends DOM::LocationSource::Range { + DomAdapterLocation() { + this = domAdapter().getAMethodCall("getLocation") + } + } +} diff --git a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected index fa9f7e31959..32f378eb504 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected @@ -15,6 +15,50 @@ nodes | addEventListener.js:12:24:12:28 | event | | addEventListener.js:12:24:12:33 | event.data | | addEventListener.js:12:24:12:33 | event.data | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | +| angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | +| angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | +| angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:25:44:25:71 | this.ro ... ragment | +| angular2-client.ts:25:44:25:71 | this.ro ... ragment | +| angular2-client.ts:25:44:25:71 | this.ro ... ragment | +| angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | +| angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | +| angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | +| angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | +| angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | +| angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | +| angular2-client.ts:29:46:29:59 | map.get('foo') | +| angular2-client.ts:29:46:29:59 | map.get('foo') | +| angular2-client.ts:29:46:29:59 | map.get('foo') | +| angular2-client.ts:32:44:32:74 | this.ro ... 1].path | +| angular2-client.ts:32:44:32:74 | this.ro ... 1].path | +| angular2-client.ts:32:44:32:74 | this.ro ... 1].path | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | +| angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:34:44:34:91 | this.ro ... et('x') | +| angular2-client.ts:34:44:34:91 | this.ro ... et('x') | +| angular2-client.ts:34:44:34:91 | this.ro ... et('x') | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | +| angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:37:44:37:58 | this.router.url | +| angular2-client.ts:37:44:37:58 | this.router.url | +| angular2-client.ts:37:44:37:58 | this.router.url | +| angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | +| angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | +| angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | | exception-xss.js:2:6:2:28 | foo | | exception-xss.js:2:12:2:28 | document.location | | exception-xss.js:2:12:2:28 | document.location | @@ -505,6 +549,34 @@ edges | addEventListener.js:10:21:10:25 | event | addEventListener.js:12:24:12:28 | event | | addEventListener.js:12:24:12:28 | event | addEventListener.js:12:24:12:33 | event.data | | addEventListener.js:12:24:12:28 | event | addEventListener.js:12:24:12:33 | event.data | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:25:44:25:71 | this.ro ... ragment | angular2-client.ts:25:44:25:71 | this.ro ... ragment | +| angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | +| angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | +| angular2-client.ts:29:46:29:59 | map.get('foo') | angular2-client.ts:29:46:29:59 | map.get('foo') | +| angular2-client.ts:32:44:32:74 | this.ro ... 1].path | angular2-client.ts:32:44:32:74 | this.ro ... 1].path | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:34:44:34:91 | this.ro ... et('x') | angular2-client.ts:34:44:34:91 | this.ro ... et('x') | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:37:44:37:58 | this.router.url | angular2-client.ts:37:44:37:58 | this.router.url | +| angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | | exception-xss.js:2:6:2:28 | foo | exception-xss.js:86:17:86:19 | foo | | exception-xss.js:2:6:2:28 | foo | exception-xss.js:86:17:86:19 | foo | | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:2:6:2:28 | foo | @@ -937,6 +1009,19 @@ edges | addEventListener.js:2:20:2:29 | event.data | addEventListener.js:1:43:1:47 | event | addEventListener.js:2:20:2:29 | event.data | Cross-site scripting vulnerability due to $@. | addEventListener.js:1:43:1:47 | event | user-provided value | | addEventListener.js:6:20:6:23 | data | addEventListener.js:5:43:5:48 | {data} | addEventListener.js:6:20:6:23 | data | Cross-site scripting vulnerability due to $@. | addEventListener.js:5:43:5:48 | {data} | user-provided value | | addEventListener.js:12:24:12:33 | event.data | addEventListener.js:10:21:10:25 | event | addEventListener.js:12:24:12:33 | event.data | Cross-site scripting vulnerability due to $@. | addEventListener.js:10:21:10:25 | event | user-provided value | +| angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | Cross-site scripting vulnerability due to $@. | angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | user-provided value | +| angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | angular2-client.ts:23:44:23:69 | this.ro ... .params | angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | Cross-site scripting vulnerability due to $@. | angular2-client.ts:23:44:23:69 | this.ro ... .params | user-provided value | +| angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | angular2-client.ts:24:44:24:74 | this.ro ... yParams | angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | Cross-site scripting vulnerability due to $@. | angular2-client.ts:24:44:24:74 | this.ro ... yParams | user-provided value | +| angular2-client.ts:25:44:25:71 | this.ro ... ragment | angular2-client.ts:25:44:25:71 | this.ro ... ragment | angular2-client.ts:25:44:25:71 | this.ro ... ragment | Cross-site scripting vulnerability due to $@. | angular2-client.ts:25:44:25:71 | this.ro ... ragment | user-provided value | +| angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | user-provided value | +| angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | user-provided value | +| angular2-client.ts:29:46:29:59 | map.get('foo') | angular2-client.ts:29:46:29:59 | map.get('foo') | angular2-client.ts:29:46:29:59 | map.get('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:29:46:29:59 | map.get('foo') | user-provided value | +| angular2-client.ts:32:44:32:74 | this.ro ... 1].path | angular2-client.ts:32:44:32:74 | this.ro ... 1].path | angular2-client.ts:32:44:32:74 | this.ro ... 1].path | Cross-site scripting vulnerability due to $@. | angular2-client.ts:32:44:32:74 | this.ro ... 1].path | user-provided value | +| angular2-client.ts:33:44:33:82 | this.ro ... eters.x | angular2-client.ts:33:44:33:80 | this.ro ... ameters | angular2-client.ts:33:44:33:82 | this.ro ... eters.x | Cross-site scripting vulnerability due to $@. | angular2-client.ts:33:44:33:80 | this.ro ... ameters | user-provided value | +| angular2-client.ts:34:44:34:91 | this.ro ... et('x') | angular2-client.ts:34:44:34:91 | this.ro ... et('x') | angular2-client.ts:34:44:34:91 | this.ro ... et('x') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:34:44:34:91 | this.ro ... et('x') | user-provided value | +| angular2-client.ts:35:44:35:91 | this.ro ... arams.x | angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | Cross-site scripting vulnerability due to $@. | angular2-client.ts:35:44:35:89 | this.ro ... .params | user-provided value | +| angular2-client.ts:37:44:37:58 | this.router.url | angular2-client.ts:37:44:37:58 | this.router.url | angular2-client.ts:37:44:37:58 | this.router.url | Cross-site scripting vulnerability due to $@. | angular2-client.ts:37:44:37:58 | this.router.url | user-provided value | +| angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | user-provided value | | exception-xss.js:86:17:86:19 | foo | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:86:17:86:19 | foo | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:12:2:28 | document.location | user-provided value | | jquery.js:4:5:4:11 | tainted | jquery.js:2:17:2:33 | document.location | jquery.js:4:5:4:11 | tainted | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:33 | document.location | user-provided value | | jquery.js:7:5:7:34 | "

      " | jquery.js:2:17:2:33 | document.location | jquery.js:7:5:7:34 | "
      " | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:33 | document.location | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/angular2-client.ts b/javascript/ql/test/query-tests/Security/CWE-079/angular2-client.ts new file mode 100644 index 00000000000..5ed51722f10 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/angular2-client.ts @@ -0,0 +1,43 @@ +import { Component, OnInit } from '@angular/core'; +import { ɵgetDOM } from '@angular/common'; +import { ActivatedRoute, ActivatedRouteSnapshot, Router } from '@angular/router'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent implements OnInit { + title = 'my-app'; + + constructor( + private route: ActivatedRoute, + private sanitizer: DomSanitizer, + private router: Router + ) {} + + ngOnInit() { + this.sanitizer.bypassSecurityTrustHtml(ɵgetDOM().getLocation().href); // NOT OK + + this.sanitizer.bypassSecurityTrustHtml(this.route.snapshot.params.foo); // NOT OK + this.sanitizer.bypassSecurityTrustHtml(this.route.snapshot.queryParams.foo); // NOT OK + this.sanitizer.bypassSecurityTrustHtml(this.route.snapshot.fragment); // NOT OK + this.sanitizer.bypassSecurityTrustHtml(this.route.snapshot.paramMap.get('foo')); // NOT OK + this.sanitizer.bypassSecurityTrustHtml(this.route.snapshot.queryParamMap.get('foo')); // NOT OK + this.route.paramMap.subscribe(map => { + this.sanitizer.bypassSecurityTrustHtml(map.get('foo')); // NOT OK + }); + + this.sanitizer.bypassSecurityTrustHtml(this.route.snapshot.url[1].path); // NOT OK - though depends on route config + this.sanitizer.bypassSecurityTrustHtml(this.route.snapshot.url[1].parameters.x); // NOT OK + this.sanitizer.bypassSecurityTrustHtml(this.route.snapshot.url[1].parameterMap.get('x')); // NOT OK + this.sanitizer.bypassSecurityTrustHtml(this.route.snapshot.url[1].parameterMap.params.x); // NOT OK + + this.sanitizer.bypassSecurityTrustHtml(this.router.url); // NOT OK + } + + someMethod(routeSnapshot: ActivatedRouteSnapshot) { + this.sanitizer.bypassSecurityTrustHtml(routeSnapshot.paramMap.get('foo')); // NOT OK + } +} From 28b449226c1aeba2daee6b848275f78814358406 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Mon, 5 Oct 2020 11:51:47 +0100 Subject: [PATCH 130/166] JS: Do not import UrlConcatenation from customizations libraries --- .../security/dataflow/ClientSideUrlRedirectCustomizations.qll | 1 - .../security/dataflow/ServerSideUrlRedirectCustomizations.qll | 1 - 2 files changed, 2 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll index a74a9f58a0f..8f413619ee5 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll @@ -6,7 +6,6 @@ import javascript import semmle.javascript.security.dataflow.RemoteFlowSources -private import UrlConcatenation module ClientSideUrlRedirect { private import Xss::DomBasedXss as DomBasedXss diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll index 3b6e7db7322..d611dbf6bb6 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll @@ -6,7 +6,6 @@ import javascript import RemoteFlowSources -private import UrlConcatenation module ServerSideUrlRedirect { /** From 42fc4ff78c5d740127d8cef125a094fe5288c6b4 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 8 Oct 2020 14:03:21 +0100 Subject: [PATCH 131/166] JS: Don't create new flow labels in *Customizations.qll files --- .../security/dataflow/ClientSideUrlRedirect.qll | 5 +++++ .../dataflow/ClientSideUrlRedirectCustomizations.qll | 2 +- .../javascript/security/dataflow/InsecureDownload.qll | 8 ++++++++ .../javascript/security/dataflow/PostMessageStar.qll | 5 +++++ .../dataflow/PostMessageStarCustomizations.qll | 2 +- .../security/dataflow/PrototypePollution.qll | 5 +++++ .../dataflow/PrototypePollutionCustomizations.qll | 10 ++++++---- .../javascript/security/dataflow/TaintedPath.qll | 8 ++++++++ .../security/dataflow/TaintedPathCustomizations.qll | 4 ++-- .../security/dataflow/UnsafeDynamicMethodAccess.qll | 5 +++++ .../UnsafeDynamicMethodAccessCustomizations.qll | 6 +++++- .../security/dataflow/UnvalidatedDynamicMethodCall.qll | 8 ++++++++ .../UnvalidatedDynamicMethodCallCustomizations.qll | 4 ++-- 13 files changed, 61 insertions(+), 11 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll index f7493f44131..58f59d1316d 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirect.qll @@ -14,6 +14,11 @@ import UrlConcatenation module ClientSideUrlRedirect { import ClientSideUrlRedirectCustomizations::ClientSideUrlRedirect + // Materialize flow labels + private class ConcreteDocumentUrl extends DocumentUrl { + ConcreteDocumentUrl() { this = this } + } + /** * A taint-tracking configuration for reasoning about unvalidated URL redirections. */ diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll index 8f413619ee5..131e1ca0047 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ClientSideUrlRedirectCustomizations.qll @@ -29,7 +29,7 @@ module ClientSideUrlRedirect { * A flow label for values that represent the URL of the current document, and * hence are only partially user-controlled. */ - class DocumentUrl extends DataFlow::FlowLabel { + abstract class DocumentUrl extends DataFlow::FlowLabel { DocumentUrl() { this = "document.url" } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll index e6085ae2594..71ff8b428e1 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll @@ -14,6 +14,14 @@ import javascript module InsecureDownload { import InsecureDownloadCustomizations::InsecureDownload + // Materialize flow labels + private class ConcreteSensitiveInsecureURL extends Label::SensitiveInsecureURL { + ConcreteSensitiveInsecureURL() { this = this } + } + private class ConcreteInsecureURL extends Label::InsecureURL { + ConcreteInsecureURL() { this = this } + } + /** * A taint tracking configuration for download of sensitive file through insecure connection. */ diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/PostMessageStar.qll b/javascript/ql/src/semmle/javascript/security/dataflow/PostMessageStar.qll index d46e0e23cac..f5095cb74f1 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/PostMessageStar.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/PostMessageStar.qll @@ -12,6 +12,11 @@ import javascript module PostMessageStar { import PostMessageStarCustomizations::PostMessageStar + // Materialize flow labels + private class ConcretePartiallyTaintedObject extends PartiallyTaintedObject { + ConcretePartiallyTaintedObject() { this = this } + } + /** * A taint tracking configuration for cross-window communication with unrestricted origin. * diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/PostMessageStarCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/PostMessageStarCustomizations.qll index 7f2322b7a2d..3f8b7c7f846 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/PostMessageStarCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/PostMessageStarCustomizations.qll @@ -26,7 +26,7 @@ module PostMessageStar { /** * A flow label representing an object with at least one tainted property. */ - class PartiallyTaintedObject extends DataFlow::FlowLabel { + abstract class PartiallyTaintedObject extends DataFlow::FlowLabel { PartiallyTaintedObject() { this = "partially tainted object" } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/PrototypePollution.qll b/javascript/ql/src/semmle/javascript/security/dataflow/PrototypePollution.qll index e3e25562a17..7667f128d08 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/PrototypePollution.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/PrototypePollution.qll @@ -15,6 +15,11 @@ import semmle.javascript.dependencies.SemVer module PrototypePollution { import PrototypePollutionCustomizations::PrototypePollution + // Materialize flow labels + private class ConcreteTaintedObjectWrapper extends TaintedObjectWrapper { + ConcreteTaintedObjectWrapper() { this = this } + } + /** * A taint tracking configuration for user-controlled objects flowing into deep `extend` calls, * leading to prototype pollution. diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/PrototypePollutionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/PrototypePollutionCustomizations.qll index 9f05cfad209..387ee8173f2 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/PrototypePollutionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/PrototypePollutionCustomizations.qll @@ -24,11 +24,13 @@ module PrototypePollution { * } * ``` */ - module TaintedObjectWrapper { - private class TaintedObjectWrapper extends DataFlow::FlowLabel { - TaintedObjectWrapper() { this = "tainted-object-wrapper" } - } + abstract class TaintedObjectWrapper extends DataFlow::FlowLabel { + TaintedObjectWrapper() { this = "tainted-object-wrapper" } + } + /** Companion module to the `TaintedObjectWrapper` class. */ + module TaintedObjectWrapper { + /** Gets the instance of the `TaintedObjectWrapper` label. */ TaintedObjectWrapper label() { any() } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPath.qll b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPath.qll index 6514ffc2ad4..147aa7197cc 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPath.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPath.qll @@ -12,6 +12,14 @@ import javascript module TaintedPath { import TaintedPathCustomizations::TaintedPath + // Materialize flow labels + private class ConcretePosixPath extends Label::PosixPath { + ConcretePosixPath() { this = this } + } + private class ConcreteSplitPath extends Label::SplitPath { + ConcreteSplitPath() { this = this } + } + /** * A taint-tracking configuration for reasoning about tainted-path vulnerabilities. */ diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll index 76b9e350427..095a2e072c1 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPathCustomizations.qll @@ -55,7 +55,7 @@ module TaintedPath { * There are currently four flow labels, representing the different combinations of * normalization and absoluteness. */ - class PosixPath extends DataFlow::FlowLabel { + abstract class PosixPath extends DataFlow::FlowLabel { Normalization normalization; Relativeness relativeness; @@ -113,7 +113,7 @@ module TaintedPath { /** * A flow label representing an array of path elements that may include "..". */ - class SplitPath extends DataFlow::FlowLabel { + abstract class SplitPath extends DataFlow::FlowLabel { SplitPath() { this = "splitPath" } } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccess.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccess.qll index e7786f0111b..9e17e9d2ce4 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccess.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccess.qll @@ -14,6 +14,11 @@ module UnsafeDynamicMethodAccess { private import DataFlow::FlowLabel import UnsafeDynamicMethodAccessCustomizations::UnsafeDynamicMethodAccess + // Materialize flow labels + private class ConcreteUnsafeFunction extends UnsafeFunction { + ConcreteUnsafeFunction() { this = this } + } + /** * A taint-tracking configuration for reasoning about unsafe dynamic method access. */ diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccessCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccessCustomizations.qll index cad2db34fab..f5d690b19fc 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccessCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnsafeDynamicMethodAccessCustomizations.qll @@ -43,7 +43,11 @@ module UnsafeDynamicMethodAccess { */ UnsafeFunction unsafeFunction() { any() } - private class UnsafeFunction extends DataFlow::FlowLabel { + /** + * Flow label describing values that may refer to an unsafe + * function as a result of an attacker-controlled property name. + */ + abstract class UnsafeFunction extends DataFlow::FlowLabel { UnsafeFunction() { this = "UnsafeFunction" } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCall.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCall.qll index 3437a41d891..b028a6e1062 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCall.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCall.qll @@ -17,6 +17,14 @@ module UnvalidatedDynamicMethodCall { import UnvalidatedDynamicMethodCallCustomizations::UnvalidatedDynamicMethodCall private import DataFlow::FlowLabel + // Materialize flow labels + private class ConcreteMaybeNonFunction extends MaybeNonFunction { + ConcreteMaybeNonFunction() { this = this } + } + private class ConcreteMaybeFromProto extends MaybeFromProto { + ConcreteMaybeFromProto() { this = this } + } + /** * A taint-tracking configuration for reasoning about unvalidated dynamic method calls. */ diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCallCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCallCustomizations.qll index 6bb01bcb563..867478f7dfe 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCallCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCallCustomizations.qll @@ -43,7 +43,7 @@ module UnvalidatedDynamicMethodCall { * A flow label describing values read from a user-controlled property that * may not be functions. */ - class MaybeNonFunction extends DataFlow::FlowLabel { + abstract class MaybeNonFunction extends DataFlow::FlowLabel { MaybeNonFunction() { this = "MaybeNonFunction" } } @@ -51,7 +51,7 @@ module UnvalidatedDynamicMethodCall { * A flow label describing values read from a user-controlled property that * may originate from a prototype object. */ - class MaybeFromProto extends DataFlow::FlowLabel { + abstract class MaybeFromProto extends DataFlow::FlowLabel { MaybeFromProto() { this = "MaybeFromProto" } } From ca6cd187b7b83ec644ef543db97942e0784b50ab Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 8 Oct 2020 17:32:54 +0100 Subject: [PATCH 132/166] JS: Change note --- change-notes/1.26/analysis-javascript.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/change-notes/1.26/analysis-javascript.md b/change-notes/1.26/analysis-javascript.md index 01d35a12c2d..245d3a3130e 100644 --- a/change-notes/1.26/analysis-javascript.md +++ b/change-notes/1.26/analysis-javascript.md @@ -2,7 +2,10 @@ ## General improvements +* Angular-specific taint sources and sinks are now recognized by the security queries. + * Support for the following frameworks and libraries has been improved: + - [@angular/*](https://www.npmjs.com/package/@angular/core) - [AWS Serverless](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) - [Alibaba Serverless](https://www.alibabacloud.com/help/doc-detail/156876.htm) - [bluebird](https://www.npmjs.com/package/bluebird) From b3d8b95433e62d663470548a415d98d59bb3960a Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 9 Oct 2020 14:25:59 +0100 Subject: [PATCH 133/166] JS: Autoformat --- .../semmle/javascript/frameworks/Angular2.qll | 56 +++++++++---------- .../security/dataflow/InsecureDownload.qll | 1 + .../security/dataflow/TaintedPath.qll | 1 + .../dataflow/UnvalidatedDynamicMethodCall.qll | 1 + 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Angular2.qll b/javascript/ql/src/semmle/javascript/frameworks/Angular2.qll index 89e884d0bb1..35f8d49b030 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Angular2.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Angular2.qll @@ -13,9 +13,7 @@ private import semmle.javascript.DynamicPropertyAccess */ module Angular2 { /** Gets a reference to a `Router` object. */ - DataFlow::SourceNode router() { - result.hasUnderlyingType("@angular/router", "Router") - } + DataFlow::SourceNode router() { result.hasUnderlyingType("@angular/router", "Router") } /** Gets a reference to a `RouterState` object. */ DataFlow::SourceNode routerState() { @@ -60,13 +58,15 @@ module Angular2 { result = activatedRouteSnapshot().getAPropertyRead("data").getAPropertyRead(name) or // this.route.foo.subscribe(foo => { ... }) - result = activatedRoute().getAPropertyRead(name).getAMethodCall("subscribe").getABoundCallbackParameter(0, 0) + result = + activatedRoute() + .getAPropertyRead(name) + .getAMethodCall("subscribe") + .getABoundCallbackParameter(0, 0) } /** Gets an array of URL segments matched by some route. */ - private DataFlow::SourceNode urlSegmentArray() { - result = activatedRouteProp("url") - } + private DataFlow::SourceNode urlSegmentArray() { result = activatedRouteProp("url") } /** Gets a data flow node referring to a `UrlSegment` object matched by some route. */ DataFlow::SourceNode urlSegment() { @@ -115,9 +115,7 @@ module Angular2 { this = routerStateSnapshot().getAPropertyRead("url") } - override string getSourceType() { - result = "Angular route parameter" - } + override string getSourceType() { result = "Angular route parameter" } } /** Gets a reference to a `DomSanitizer` object. */ @@ -127,12 +125,19 @@ module Angular2 { /** A value that is about to be promoted to a trusted HTML or CSS value. */ private class AngularXssSink extends DomBasedXss::Sink { - AngularXssSink() { this = domSanitizer().getAMethodCall(["bypassSecurityTrustHtml", "bypassSecurityTrustStyle"]).getArgument(0) } + AngularXssSink() { + this = + domSanitizer() + .getAMethodCall(["bypassSecurityTrustHtml", "bypassSecurityTrustStyle"]) + .getArgument(0) + } } /** A value that is about to be promoted to a trusted script value. */ private class AngularCodeInjectionSink extends CodeInjection::Sink { - AngularCodeInjectionSink() { this = domSanitizer().getAMethodCall(["bypassSecurityTrustScript"]).getArgument(0) } + AngularCodeInjectionSink() { + this = domSanitizer().getAMethodCall(["bypassSecurityTrustScript"]).getArgument(0) + } } /** @@ -140,7 +145,12 @@ module Angular2 { */ private class AngularUrlSink extends ClientSideUrlRedirect::Sink { // We mark this as a client URL redirect sink for precision reasons, though its description can be a bit confusing. - AngularUrlSink() { this = domSanitizer().getAMethodCall(["bypassSecurityTrustUrl", "bypassSecurityTrustResourceUrl"]).getArgument(0) } + AngularUrlSink() { + this = + domSanitizer() + .getAMethodCall(["bypassSecurityTrustUrl", "bypassSecurityTrustResourceUrl"]) + .getArgument(0) + } } private predicate taintStep(DataFlow::Node pred, DataFlow::Node succ) { @@ -155,13 +165,9 @@ module Angular2 { } private class AngularTaintStep extends TaintTracking::AdditionalTaintStep { - AngularTaintStep() { - taintStep(_, this) - } + AngularTaintStep() { taintStep(_, this) } - override predicate step(DataFlow::Node pred, DataFlow::Node succ) { - taintStep(pred, succ) - } + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { taintStep(pred, succ) } } /** Gets a reference to an `HttpClient` object. */ @@ -180,13 +186,9 @@ module Angular2 { argumentOffset = 0 } - override DataFlow::Node getUrl() { - result = getArgument(argumentOffset) - } + override DataFlow::Node getUrl() { result = getArgument(argumentOffset) } - override DataFlow::Node getHost() { - none() - } + override DataFlow::Node getHost() { none() } override DataFlow::Node getADataNode() { getMethodName() = ["patch", "post", "put"] and @@ -207,8 +209,6 @@ module Angular2 { /** A reference to the DOM location obtained through `DomAdapter.getLocation()`. */ private class DomAdapterLocation extends DOM::LocationSource::Range { - DomAdapterLocation() { - this = domAdapter().getAMethodCall("getLocation") - } + DomAdapterLocation() { this = domAdapter().getAMethodCall("getLocation") } } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll index 71ff8b428e1..6fb12060d65 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/InsecureDownload.qll @@ -18,6 +18,7 @@ module InsecureDownload { private class ConcreteSensitiveInsecureURL extends Label::SensitiveInsecureURL { ConcreteSensitiveInsecureURL() { this = this } } + private class ConcreteInsecureURL extends Label::InsecureURL { ConcreteInsecureURL() { this = this } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPath.qll b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPath.qll index 147aa7197cc..37efbb5347e 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPath.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/TaintedPath.qll @@ -16,6 +16,7 @@ module TaintedPath { private class ConcretePosixPath extends Label::PosixPath { ConcretePosixPath() { this = this } } + private class ConcreteSplitPath extends Label::SplitPath { ConcreteSplitPath() { this = this } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCall.qll b/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCall.qll index b028a6e1062..5075d29186b 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCall.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/UnvalidatedDynamicMethodCall.qll @@ -21,6 +21,7 @@ module UnvalidatedDynamicMethodCall { private class ConcreteMaybeNonFunction extends MaybeNonFunction { ConcreteMaybeNonFunction() { this = this } } + private class ConcreteMaybeFromProto extends MaybeFromProto { ConcreteMaybeFromProto() { this = this } } From 4337c5adafe5289ad7dcc7ce85c2e990231b27ae Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Wed, 14 Oct 2020 18:08:36 +0100 Subject: [PATCH 134/166] JS: Workaround ascii PR check --- .../semmle/javascript/frameworks/Angular2.qll | 13 +++- .../CWE-079/XssWithAdditionalSources.expected | 72 +++++++++++++++++++ 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/frameworks/Angular2.qll b/javascript/ql/src/semmle/javascript/frameworks/Angular2.qll index 35f8d49b030..199471e9a65 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/Angular2.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/Angular2.qll @@ -198,13 +198,20 @@ module Angular2 { } } + private string getInternalName(string name) { + exists(Identifier id | + result = id.getName() and + name = result.regexpCapture("\\u0275(DomAdapter|getDOM)", 1) + ) + } + /** Gets a reference to a `DomAdapter`, which provides acess to raw DOM elements. */ private DataFlow::SourceNode domAdapter() { - // Note: these are internal properties, prefixed with the theta character "ɵ". + // Note: these are internal properties, prefixed with the "latin small letter barred O (U+0275)" character. // Despite being internal, some codebases do access them. - result.hasUnderlyingType("@angular/common", "ɵDomAdapter") + result.hasUnderlyingType("@angular/common", getInternalName("DomAdapter")) or - result = DataFlow::moduleImport("@angular/common").getAMemberCall("ɵgetDOM") + result = DataFlow::moduleImport("@angular/common").getAMemberCall(getInternalName("getDOM")) } /** A reference to the DOM location obtained through `DomAdapter.getLocation()`. */ diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected index 4182506d437..ac7a05a3b1e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected @@ -15,6 +15,50 @@ nodes | addEventListener.js:12:24:12:28 | event | | addEventListener.js:12:24:12:33 | event.data | | addEventListener.js:12:24:12:33 | event.data | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | +| angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | +| angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | +| angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:25:44:25:71 | this.ro ... ragment | +| angular2-client.ts:25:44:25:71 | this.ro ... ragment | +| angular2-client.ts:25:44:25:71 | this.ro ... ragment | +| angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | +| angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | +| angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | +| angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | +| angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | +| angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | +| angular2-client.ts:29:46:29:59 | map.get('foo') | +| angular2-client.ts:29:46:29:59 | map.get('foo') | +| angular2-client.ts:29:46:29:59 | map.get('foo') | +| angular2-client.ts:32:44:32:74 | this.ro ... 1].path | +| angular2-client.ts:32:44:32:74 | this.ro ... 1].path | +| angular2-client.ts:32:44:32:74 | this.ro ... 1].path | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | +| angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:34:44:34:91 | this.ro ... et('x') | +| angular2-client.ts:34:44:34:91 | this.ro ... et('x') | +| angular2-client.ts:34:44:34:91 | this.ro ... et('x') | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | +| angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:37:44:37:58 | this.router.url | +| angular2-client.ts:37:44:37:58 | this.router.url | +| angular2-client.ts:37:44:37:58 | this.router.url | +| angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | +| angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | +| angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | | exception-xss.js:2:6:2:28 | foo | | exception-xss.js:2:12:2:28 | document.location | | exception-xss.js:2:12:2:28 | document.location | @@ -509,6 +553,34 @@ edges | addEventListener.js:10:21:10:25 | event | addEventListener.js:12:24:12:28 | event | | addEventListener.js:12:24:12:28 | event | addEventListener.js:12:24:12:33 | event.data | | addEventListener.js:12:24:12:28 | event | addEventListener.js:12:24:12:33 | event.data | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:21:44:21:66 | \\u0275getDOM ... ation() | angular2-client.ts:21:44:21:71 | \\u0275getDOM ... ().href | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:23:44:23:69 | this.ro ... .params | angular2-client.ts:23:44:23:73 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:24:44:24:74 | this.ro ... yParams | angular2-client.ts:24:44:24:78 | this.ro ... ams.foo | +| angular2-client.ts:25:44:25:71 | this.ro ... ragment | angular2-client.ts:25:44:25:71 | this.ro ... ragment | +| angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | angular2-client.ts:26:44:26:82 | this.ro ... ('foo') | +| angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | angular2-client.ts:27:44:27:87 | this.ro ... ('foo') | +| angular2-client.ts:29:46:29:59 | map.get('foo') | angular2-client.ts:29:46:29:59 | map.get('foo') | +| angular2-client.ts:32:44:32:74 | this.ro ... 1].path | angular2-client.ts:32:44:32:74 | this.ro ... 1].path | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:33:44:33:80 | this.ro ... ameters | angular2-client.ts:33:44:33:82 | this.ro ... eters.x | +| angular2-client.ts:34:44:34:91 | this.ro ... et('x') | angular2-client.ts:34:44:34:91 | this.ro ... et('x') | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | +| angular2-client.ts:37:44:37:58 | this.router.url | angular2-client.ts:37:44:37:58 | this.router.url | +| angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | | exception-xss.js:2:6:2:28 | foo | exception-xss.js:86:17:86:19 | foo | | exception-xss.js:2:6:2:28 | foo | exception-xss.js:86:17:86:19 | foo | | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:2:6:2:28 | foo | From 583f3d7fd940d9a0f66ec87d7ec0cc9cab2f5109 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Thu, 15 Oct 2020 14:04:11 +0100 Subject: [PATCH 135/166] JS: Also materialize labels in ZipSlip --- .../src/semmle/javascript/security/dataflow/ZipSlip.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ZipSlip.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ZipSlip.qll index 56f2ea50cee..cb41ee19791 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ZipSlip.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ZipSlip.qll @@ -12,6 +12,15 @@ import javascript module ZipSlip { import ZipSlipCustomizations::ZipSlip + // Materialize flow labels + private class ConcretePosixPath extends TaintedPath::Label::PosixPath { + ConcretePosixPath() { this = this } + } + + private class ConcreteSplitPath extends TaintedPath::Label::SplitPath { + ConcreteSplitPath() { this = this } + } + /** A taint tracking configuration for unsafe archive extraction. */ class Configuration extends DataFlow::Configuration { Configuration() { this = "ZipSlip" } From 287ec0cbbb8481f3f63cdfb8561ef8976fb89906 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 16 Oct 2020 07:16:02 +0100 Subject: [PATCH 136/166] JS: Add test for default flow labels --- .../test/library-tests/FlowLabels/DefaulFlowLabels.expected | 2 ++ .../ql/test/library-tests/FlowLabels/DefaulFlowLabels.ql | 6 ++++++ javascript/ql/test/library-tests/FlowLabels/tst.js | 2 ++ 3 files changed, 10 insertions(+) create mode 100644 javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.expected create mode 100644 javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.ql create mode 100644 javascript/ql/test/library-tests/FlowLabels/tst.js diff --git a/javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.expected b/javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.expected new file mode 100644 index 00000000000..1b68f373dc3 --- /dev/null +++ b/javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.expected @@ -0,0 +1,2 @@ +| data | +| taint | diff --git a/javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.ql b/javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.ql new file mode 100644 index 00000000000..f6fefc80ffd --- /dev/null +++ b/javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.ql @@ -0,0 +1,6 @@ +import javascript + +// Check which flow labels are materialize by importing `javascript.qll`. +// If this increases, it may indicate a performance issue. + +select any(DataFlow::FlowLabel label) diff --git a/javascript/ql/test/library-tests/FlowLabels/tst.js b/javascript/ql/test/library-tests/FlowLabels/tst.js new file mode 100644 index 00000000000..3b521ed52d2 --- /dev/null +++ b/javascript/ql/test/library-tests/FlowLabels/tst.js @@ -0,0 +1,2 @@ +// The contents of this file don't matter. +let x = 1; From 0dc066c5157588dc213da73d4b4f431e8da6f412 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 29 Jul 2020 13:17:09 +0200 Subject: [PATCH 137/166] Data flow: Rename `AccessPath` to `AccessPathApprox` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 433 +++++++++--------- 1 file changed, 223 insertions(+), 210 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 8c210edbe5f..58e615eec97 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1551,13 +1551,13 @@ abstract private class AccessPath extends TAccessPath { /** * Holds if this access path has `head` at the front and may be followed by `tail`. */ - abstract predicate pop(TypedContent head, AccessPath tail); + abstract predicate pop(TypedContent head, AccessPathApprox tail); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1569,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override predicate pop(TypedContent head, AccessPathApprox tail) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1593,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override predicate pop(TypedContent head, AccessPathApprox tail) { head = tc and tail = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,7 +1617,7 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override predicate pop(TypedContent head, AccessPathApprox tail) { head = tc1 and ( tail = TConsCons(tc2, _, len - 1) @@ -1629,126 +1629,126 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { apa.pop(tc, result) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1775,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1796,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1807,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1837,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1855,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2030,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,19 +2047,19 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) + TSummaryCtxSome(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } @@ -2074,13 +2081,13 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private ParameterNode p; - private AccessPath ap; + private AccessPathApprox apa; - SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } + SummaryCtxSome() { this = TSummaryCtxSome(p, apa) } int getParameterPos() { p.isParameterOf(_, result) } - override string toString() { result = p + ": " + ap } + override string toString() { result = p + ": " + apa } predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -2090,19 +2097,19 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPathApprox apa, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,7 +2121,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, any(AccessPathApproxNil nil)) and config = unbind(mid.getConfiguration()) ) ) @@ -2232,10 +2239,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { Node node; CallContext cc; SummaryCtx sc; - AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, apa, config) } override Node getNode() { result = node } @@ -2243,7 +2250,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { SummaryCtx getSummaryCtx() { result = sc } - AccessPath getAp() { result = ap } + AccessPathApprox getAp() { result = apa } override Configuration getConfiguration() { result = config } @@ -2260,7 +2267,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getAp() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2277,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2305,46 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPathApprox apa +) { + exists(AccessPathApprox apa0, Node midnode, Configuration conf, LocalCallContext localCC | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() + apa0 = mid.getAp() | localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 + apa = apa0 or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + localFlowBigStep(midnode, node, false, apa.getFront(), conf, localCC) and + apa0 instanceof AccessPathApproxNil ) or jumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = mid.getAp() + apa = mid.getAp() or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() instanceof AccessPathApproxNil and + apa = TNil(getNodeType(node)) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and + exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, apa), tc, cc)) and sc = mid.getSummaryCtx() or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and + exists(TypedContent tc | pathReadStep(mid, node, push(tc, apa), tc, cc)) and sc = mid.getSummaryCtx() or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() + pathIntoCallable(mid, node, _, cc, sc, _) and apa = mid.getAp() or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone + pathOutOfCallable(mid, node, cc) and apa = mid.getAp() and sc instanceof SummaryCtxNone or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathThroughCallable(mid, node, cc, apa) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2346,9 +2355,9 @@ private predicate readCand(Node node1, TypedContent tc, Node node2, Configuratio pragma[nomagic] private predicate pathReadStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { - ap0 = mid.getAp() and + apa0 = mid.getAp() and readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2361,30 +2370,31 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { - ap0 = mid.getAp() and + apa0 = mid.getAp() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getAp() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2405,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2417,10 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) | - out = getAnOutNodeFlow(kind, call, ap, config) + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2429,22 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + apa = mid.getAp() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2452,11 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) } /** @@ -2458,13 +2468,13 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and + exists(int i, DataFlowCallable callable, AccessPathApprox apa | + pathIntoCallable0(mid, callable, i, outercc, call, apa) and p.isParameterOf(callable, i) and ( - sc = TSummaryCtxSome(p, ap) + sc = TSummaryCtxSome(p, apa) or - not exists(TSummaryCtxSome(p, ap)) and + not exists(TSummaryCtxSome(p, apa)) and sc = TSummaryCtxNone() ) | @@ -2477,7 +2487,8 @@ private predicate pathIntoCallable( /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2485,7 +2496,7 @@ private predicate paramFlowsThrough( cc = mid.getCallContext() and sc = mid.getSummaryCtx() and config = mid.getConfiguration() and - ap = mid.getAp() and + apa = mid.getAp() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2504,11 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2517,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, apa) and + out = getAnOutNodeFlow(kind, call, apa, mid.getConfiguration()) ) } From a35a178080d5807f39524039f6bcfd76a5ef00d3 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 29 Jul 2020 13:17:27 +0200 Subject: [PATCH 138/166] Data flow: Precise access paths --- .../csharp/dataflow/internal/DataFlowImpl.qll | 324 +++++++++++++----- 1 file changed, 241 insertions(+), 83 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 58e615eec97..04975b875b7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1548,10 +1548,8 @@ abstract private class AccessPathApprox extends TAccessPathApprox { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPathApprox tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } private class AccessPathApproxNil extends AccessPathApprox, TNil { @@ -1569,7 +1567,7 @@ private class AccessPathApproxNil extends AccessPathApprox, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPathApprox tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } abstract private class AccessPathApproxCons extends AccessPathApprox { } @@ -1593,7 +1591,7 @@ private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPathApprox tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { @@ -1617,19 +1615,19 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPathApprox tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { apa.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } @@ -2053,15 +2051,17 @@ private predicate parameterFlow( c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPathApprox apa) { - exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathApproxSome(_), apa0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2081,13 +2081,13 @@ private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { /** A summary context from which a flow summary can be generated. */ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private ParameterNode p; - private AccessPathApprox apa; + private AccessPath ap; - SummaryCtxSome() { this = TSummaryCtxSome(p, apa) } + SummaryCtxSome() { this = TSummaryCtxSome(p, ap) } int getParameterPos() { p.isParameterOf(_, result) } - override string toString() { result = p + ": " + apa } + override string toString() { result = p + ": " + ap } predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -2096,18 +2096,26 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPathApprox apa, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and + ap = TAccessPathNil(getNodeType(node)) and apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, apa) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and flow(node, _, _, apa, unbind(config)) ) @@ -2121,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathApproxNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2239,10 +2339,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { Node node; CallContext cc; SummaryCtx sc; + AccessPath ap; AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2250,12 +2351,15 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { SummaryCtx getSummaryCtx() { result = sc } - AccessPathApprox getAp() { result = apa } + AccessPath getAp() { result = ap } + + AccessPathApprox getApa() { result = apa } override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2267,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathApproxNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2306,45 +2410,75 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPathApprox apa + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa ) { - exists(AccessPathApprox apa0, Node midnode, Configuration conf, LocalCallContext localCC | + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - apa0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - apa = apa0 - or - localFlowBigStep(midnode, node, false, apa.getFront(), conf, localCC) and - apa0 instanceof AccessPathApproxNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - apa = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathApproxNil and - apa = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, apa), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, apa), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and apa = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and apa = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, apa) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2355,9 +2489,9 @@ private predicate readCand(Node node1, TypedContent tc, Node node2, Configuratio pragma[nomagic] private predicate pathReadStep( - PathNodeMid mid, Node node, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { - apa0 = mid.getAp() and + ap0 = mid.getAp() and readCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2370,9 +2504,10 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { - apa0 = mid.getAp() and + ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2384,7 +2519,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } @@ -2418,8 +2553,7 @@ private Node getAnOutNodeFlow( pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, apa, config) - | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2429,13 +2563,14 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPathApprox apa + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - apa = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } @@ -2452,13 +2587,38 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPathApprox apa + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, apa) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) } +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) +} + /** * Holds if data may flow from `mid` to `p` through `call`. The contexts * before and after entering the callable are `outercc` and `innercc`, @@ -2468,26 +2628,22 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPathApprox apa | - pathIntoCallable0(mid, callable, i, outercc, call, apa) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, apa) - or - not exists(TSummaryCtxSome(p, apa)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPathApprox apa, + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | @@ -2496,7 +2652,8 @@ private predicate paramFlowsThrough( cc = mid.getCallContext() and sc = mid.getSummaryCtx() and config = mid.getConfiguration() and - apa = mid.getAp() and + ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2504,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathApprox apa + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, apa, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2518,11 +2676,11 @@ private predicate pathThroughCallable0( */ pragma[noinline] private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPathApprox apa + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa ) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, apa) and - out = getAnOutNodeFlow(kind, call, apa, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } From d608138c0c4f43c1c584d7337dd7408bf23abb38 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 21 Aug 2020 10:28:19 +0200 Subject: [PATCH 139/166] Data flow: Sync files --- .../cpp/dataflow/internal/DataFlowImpl.qll | 631 +++++++++++------- .../cpp/dataflow/internal/DataFlowImpl2.qll | 631 +++++++++++------- .../cpp/dataflow/internal/DataFlowImpl3.qll | 631 +++++++++++------- .../cpp/dataflow/internal/DataFlowImpl4.qll | 631 +++++++++++------- .../dataflow/internal/DataFlowImplLocal.qll | 631 +++++++++++------- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 631 +++++++++++------- .../ir/dataflow/internal/DataFlowImpl2.qll | 631 +++++++++++------- .../ir/dataflow/internal/DataFlowImpl3.qll | 631 +++++++++++------- .../ir/dataflow/internal/DataFlowImpl4.qll | 631 +++++++++++------- .../dataflow/internal/DataFlowImpl2.qll | 631 +++++++++++------- .../dataflow/internal/DataFlowImpl3.qll | 631 +++++++++++------- .../dataflow/internal/DataFlowImpl4.qll | 631 +++++++++++------- .../dataflow/internal/DataFlowImpl5.qll | 631 +++++++++++------- .../java/dataflow/internal/DataFlowImpl.qll | 631 +++++++++++------- .../java/dataflow/internal/DataFlowImpl2.qll | 631 +++++++++++------- .../java/dataflow/internal/DataFlowImpl3.qll | 631 +++++++++++------- .../java/dataflow/internal/DataFlowImpl4.qll | 631 +++++++++++------- .../java/dataflow/internal/DataFlowImpl5.qll | 631 +++++++++++------- .../dataflow/internal/DataFlowImpl.qll | 631 +++++++++++------- .../dataflow/internal/DataFlowImpl2.qll | 631 +++++++++++------- 20 files changed, 8020 insertions(+), 4600 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 8c210edbe5f..04975b875b7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 8c210edbe5f..04975b875b7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 8c210edbe5f..04975b875b7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 8c210edbe5f..04975b875b7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 8c210edbe5f..04975b875b7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 8c210edbe5f..04975b875b7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 8c210edbe5f..04975b875b7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 8c210edbe5f..04975b875b7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 8c210edbe5f..04975b875b7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 8c210edbe5f..04975b875b7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 8c210edbe5f..04975b875b7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 8c210edbe5f..04975b875b7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 8c210edbe5f..04975b875b7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 8c210edbe5f..04975b875b7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 8c210edbe5f..04975b875b7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 8c210edbe5f..04975b875b7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 8c210edbe5f..04975b875b7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 8c210edbe5f..04975b875b7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index 8c210edbe5f..04975b875b7 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index 8c210edbe5f..04975b875b7 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -1523,7 +1523,7 @@ private predicate flowCandIsReturned( ) } -private newtype TAccessPath = +private newtype TAccessPathApprox = TNil(DataFlowType t) or TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { @@ -1531,13 +1531,13 @@ private newtype TAccessPath = } /** - * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first two - * elements of the list and its length are tracked. If data flows from a source to - * a given node with a given `AccessPath`, this indicates the sequence of - * dereference operations needed to get from the value in the node to the - * tracked object. The final type indicates the type of the tracked object. + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. */ -abstract private class AccessPath extends TAccessPath { +abstract private class AccessPathApprox extends TAccessPathApprox { abstract string toString(); abstract TypedContent getHead(); @@ -1548,16 +1548,14 @@ abstract private class AccessPath extends TAccessPath { abstract AccessPathFront getFront(); - /** - * Holds if this access path has `head` at the front and may be followed by `tail`. - */ - abstract predicate pop(TypedContent head, AccessPath tail); + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); } -private class AccessPathNil extends AccessPath, TNil { +private class AccessPathApproxNil extends AccessPathApprox, TNil { private DataFlowType t; - AccessPathNil() { this = TNil(t) } + AccessPathApproxNil() { this = TNil(t) } override string toString() { result = concat(": " + ppReprType(t)) } @@ -1569,16 +1567,16 @@ private class AccessPathNil extends AccessPath, TNil { override AccessPathFront getFront() { result = TFrontNil(t) } - override predicate pop(TypedContent head, AccessPath tail) { none() } + override AccessPathApprox pop(TypedContent head) { none() } } -abstract private class AccessPathCons extends AccessPath { } +abstract private class AccessPathApproxCons extends AccessPathApprox { } -private class AccessPathConsNil extends AccessPathCons, TConsNil { +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { private TypedContent tc; private DataFlowType t; - AccessPathConsNil() { this = TConsNil(tc, t) } + AccessPathApproxConsNil() { this = TConsNil(tc, t) } override string toString() { // The `concat` becomes "" if `ppReprType` has no result. @@ -1593,15 +1591,15 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil { override AccessPathFront getFront() { result = TFrontHead(tc) } - override predicate pop(TypedContent head, AccessPath tail) { head = tc and tail = TNil(t) } + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } } -private class AccessPathConsCons extends AccessPathCons, TConsCons { +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { private TypedContent tc1; private TypedContent tc2; private int len; - AccessPathConsCons() { this = TConsCons(tc1, tc2, len) } + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } override string toString() { if len = 2 @@ -1617,138 +1615,138 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons { override AccessPathFront getFront() { result = TFrontHead(tc1) } - override predicate pop(TypedContent head, AccessPath tail) { + override AccessPathApprox pop(TypedContent head) { head = tc1 and ( - tail = TConsCons(tc2, _, len - 1) + result = TConsCons(tc2, _, len - 1) or len = 2 and - tail = TConsNil(tc2, _) + result = TConsNil(tc2, _) ) } } /** Gets the access path obtained by popping `tc` from `ap`, if any. */ -private AccessPath pop(TypedContent tc, AccessPath ap) { ap.pop(tc, result) } +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } /** Gets the access path obtained by pushing `tc` onto `ap`. */ -private AccessPath push(TypedContent tc, AccessPath ap) { ap = pop(tc, result) } +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } -private newtype TAccessPathOption = - TAccessPathNone() or - TAccessPathSome(AccessPath ap) +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) -private class AccessPathOption extends TAccessPathOption { +private class AccessPathApproxOption extends TAccessPathApproxOption { string toString() { - this = TAccessPathNone() and result = "" + this = TAccessPathApproxNone() and result = "" or - this = TAccessPathSome(any(AccessPath ap | result = ap.toString())) + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) } } /** - * Holds if `node` is reachable with access path `ap` from a source in - * the configuration `config`. + * Holds if `node` is reachable with approximate access path `ap` from a source + * in the configuration `config`. * * The call context `cc` records whether the node is reached through an - * argument in a call, and if so, `argAp` records the access path of that - * argument. + * argument in a call, and if so, `argApa` records the approximate access path + * of that argument. */ private predicate flowFwd( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { - flowFwd0(node, cc, argAp, apf, ap, config) and + flowFwd0(node, cc, argApa, apf, apa, config) and flowCand(node, _, _, apf, config) } private predicate flowFwd0( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, Configuration config ) { flowCand(node, _, _, _, config) and config.isSource(node) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() or flowCand(node, _, _, _, unbind(config)) and ( exists(Node mid, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and + flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and localFlowBigStep(mid, node, true, _, config, localCC) ) or - exists(Node mid, AccessPathNil nil, LocalCallContext localCC | - flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and + exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC | + flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and localFlowBigStep(mid, node, false, apf, config, localCC) and - apf = ap.(AccessPathNil).getFront() + apf = apa.(AccessPathApproxNil).getFront() ) or exists(Node mid | - flowFwd(mid, _, _, apf, ap, config) and + flowFwd(mid, _, _, apf, apa, config) and jumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() + argApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | + exists(Node mid, AccessPathApproxNil nil | flowFwd(mid, _, _, _, nil, config) and additionalJumpStep(mid, node, config) and cc instanceof CallContextAny and - argAp = TAccessPathNone() and - ap = TNil(getNodeType(node)) and - apf = ap.(AccessPathNil).getFront() + argApa = TAccessPathApproxNone() and + apa = TNil(getNodeType(node)) and + apf = apa.(AccessPathApproxNil).getFront() ) ) or // store - exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config)) + exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config)) or // read exists(TypedContent tc | - flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and - flowFwdConsCand(tc, apf, ap, config) + flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and + flowFwdConsCand(tc, apf, apa, config) ) or // flow into a callable - flowFwdIn(_, node, _, cc, _, apf, ap, config) and + flowFwdIn(_, node, _, cc, _, apf, apa, config) and if flowCand(node, true, _, apf, config) - then argAp = TAccessPathSome(ap) - else argAp = TAccessPathNone() + then argApa = TAccessPathApproxSome(apa) + else argApa = TAccessPathApproxNone() or // flow out of a callable exists(DataFlowCall call | exists(DataFlowCallable c | - flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and + flowFwdOut(call, node, any(CallContextNoCall innercc), c, argApa, apf, apa, config) and if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() ) or - exists(AccessPath argAp0 | - flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and - flowFwdIsEntered(call, cc, argAp, argAp0, config) + exists(AccessPathApprox argApa0 | + flowFwdOutFromArg(call, node, argApa0, apf, apa, config) and + flowFwdIsEntered(call, cc, argApa, argApa0, config) ) ) } pragma[nomagic] private predicate flowFwdLocalEntry( - Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap, - LocalCallContext localCC, Configuration config + Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf, + AccessPathApprox apa, LocalCallContext localCC, Configuration config ) { - flowFwd(node, cc, argAp, apf, ap, config) and + flowFwd(node, cc, argApa, apf, apa, config) and localFlowEntry(node, config) and localCC = getLocalCallContext(cc, node.getEnclosingCallable()) } pragma[nomagic] private predicate flowFwdStore( - Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, TypedContent tc, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, AccessPathFront apf0 | - flowFwd(mid, cc, argAp, apf0, ap0, config) and + flowFwd(mid, cc, argApa, apf0, apa0, config) and flowFwdStore0(mid, tc, node, apf0, apf, config) ) } @@ -1775,20 +1773,20 @@ private predicate flowFwdStore0( pragma[nomagic] private predicate flowFwdRead0( - Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc, - AccessPathOption argAp, Configuration config + Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2, + CallContext cc, AccessPathApproxOption argApa, Configuration config ) { - flowFwd(node1, cc, argAp, apf0, ap0, config) and + flowFwd(node1, cc, argApa, apf0, apa0, config) and readCandFwd(node1, tc, apf0, node2, config) } pragma[nomagic] private predicate flowFwdRead( - Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc, - AccessPathOption argAp, Configuration config + Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc, + AccessPathApproxOption argApa, Configuration config ) { exists(Node mid, TypedContent tc | - flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and + flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and flowCand(node, _, _, apf, unbind(config)) and flowCandConsCand(tc, apf, unbind(config)) ) @@ -1796,10 +1794,10 @@ private predicate flowFwdRead( pragma[nomagic] private predicate flowFwdConsCand( - TypedContent tc, AccessPathFront apf, AccessPath ap, Configuration config + TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(Node n | - flowFwd(n, _, _, apf, ap, config) and + flowFwd(n, _, _, apf, apa, config) and flowFwdStore0(n, tc, _, apf, _, config) ) } @@ -1807,27 +1805,27 @@ private predicate flowFwdConsCand( pragma[nomagic] private predicate flowFwdIn( DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c | - flowFwd(arg, outercc, argAp, apf, ap, config) and + flowFwd(arg, outercc, argApa, apf, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and c = p.getEnclosingCallable() and c = resolveCall(call, outercc) and flowCand(p, _, _, _, unbind(config)) and if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall() | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOut( DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc, - AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config + AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, boolean allowsFieldFlow | - flowFwd(ret, innercc, argAp, apf, ap, config) and + flowFwd(ret, innercc, argApa, apf, apa, config) and flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and innerc = ret.getEnclosingCallable() and flowCand(node, _, _, _, unbind(config)) and @@ -1837,16 +1835,17 @@ private predicate flowFwdOut( innercc.(CallContextCall).matchesCall(call) ) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowFwdOutFromArg( - DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap, + DataFlowCall call, Node node, AccessPathApprox argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config ) { - flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config) + flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa, + config) } /** @@ -1854,168 +1853,174 @@ private predicate flowFwdOutFromArg( */ pragma[nomagic] private predicate flowFwdIsEntered( - DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config + DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa, + Configuration config ) { exists(ParameterNode p, AccessPathFront apf | - flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and + flowFwdIn(call, p, cc, _, argApa, apf, apa, config) and flowCand(p, true, TAccessPathFrontSome(_), apf, config) ) } /** - * Holds if `node` with access path `ap` is part of a path from a source to - * a sink in the configuration `config`. + * Holds if `node` with approximate access path `ap` is part of a path from a + * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from - * the enclosing callable in order to reach a sink, and if so, `returnAp` - * records the access path of the returned value. + * the enclosing callable in order to reach a sink, and if so, `returnApa` + * records the approximate access path of the returned value. */ private predicate flow( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flow0(node, toReturn, returnAp, ap, config) and - flowFwd(node, _, _, _, ap, config) + flow0(node, toReturn, returnApa, apa, config) and + flowFwd(node, _, _, _, apa, config) } private predicate flow0( - Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, Configuration config + Node node, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, + Configuration config ) { - flowFwd(node, _, _, _, ap, config) and + flowFwd(node, _, _, _, apa, config) and config.isSink(node) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil or exists(Node mid | localFlowBigStep(node, mid, true, _, config, _) and - flow(mid, toReturn, returnAp, ap, config) + flow(mid, toReturn, returnApa, apa, config) ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and localFlowBigStep(node, mid, false, _, config, _) and - flow(mid, toReturn, returnAp, nil, config) and - ap instanceof AccessPathNil + flow(mid, toReturn, returnApa, nil, config) and + apa instanceof AccessPathApproxNil ) or exists(Node mid | jumpStep(node, mid, config) and - flow(mid, _, _, ap, config) and + flow(mid, _, _, apa, config) and toReturn = false and - returnAp = TAccessPathNone() + returnApa = TAccessPathApproxNone() ) or - exists(Node mid, AccessPathNil nil | - flowFwd(node, _, _, _, ap, config) and + exists(Node mid, AccessPathApproxNil nil | + flowFwd(node, _, _, _, apa, config) and additionalJumpStep(node, mid, config) and flow(mid, _, _, nil, config) and toReturn = false and - returnAp = TAccessPathNone() and - ap instanceof AccessPathNil + returnApa = TAccessPathApproxNone() and + apa instanceof AccessPathApproxNil ) or // store exists(TypedContent tc | - flowStore(tc, node, toReturn, returnAp, ap, config) and - flowConsCand(tc, ap, config) + flowStore(tc, node, toReturn, returnApa, apa, config) and + flowConsCand(tc, apa, config) ) or // read - exists(Node mid, AccessPath ap0 | - readFlowFwd(node, _, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + readFlowFwd(node, _, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) or // flow into a callable exists(DataFlowCall call | - flowIn(call, node, toReturn, returnAp, ap, config) and + flowIn(call, node, toReturn, returnApa, apa, config) and toReturn = false or - exists(AccessPath returnAp0 | - flowInToReturn(call, node, returnAp0, ap, config) and - flowIsReturned(call, toReturn, returnAp, returnAp0, config) + exists(AccessPathApprox returnApa0 | + flowInToReturn(call, node, returnApa0, apa, config) and + flowIsReturned(call, toReturn, returnApa, returnApa0, config) ) ) or // flow out of a callable - flowOut(_, node, _, _, ap, config) and + flowOut(_, node, _, _, apa, config) and toReturn = true and - if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config) - then returnAp = TAccessPathSome(ap) - else returnAp = TAccessPathNone() + if flowFwd(node, any(CallContextCall ccc), TAccessPathApproxSome(_), _, apa, config) + then returnApa = TAccessPathApproxSome(apa) + else returnApa = TAccessPathApproxNone() } pragma[nomagic] private predicate storeFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { storeCand2(node1, tc, node2, _, config) and - flowFwdStore(node2, tc, ap, _, _, _, config) and - ap0 = push(tc, ap) + flowFwdStore(node2, tc, apa, _, _, _, config) and + apa0 = push(tc, apa) } pragma[nomagic] private predicate flowStore( - TypedContent tc, Node node, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + TypedContent tc, Node node, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { - exists(Node mid, AccessPath ap0 | - storeFlowFwd(node, tc, mid, ap, ap0, config) and - flow(mid, toReturn, returnAp, ap0, config) + exists(Node mid, AccessPathApprox apa0 | + storeFlowFwd(node, tc, mid, apa, apa0, config) and + flow(mid, toReturn, returnApa, apa0, config) ) } pragma[nomagic] private predicate readFlowFwd( - Node node1, TypedContent tc, Node node2, AccessPath ap, AccessPath ap0, Configuration config + Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0, + Configuration config ) { exists(AccessPathFrontHead apf | readCandFwd(node1, tc, apf, node2, config) and - flowFwdRead(node2, apf, ap, _, _, _, config) and - ap0 = pop(tc, ap) and - flowFwdConsCand(tc, _, ap0, unbind(config)) + flowFwdRead(node2, apf, apa, _, _, _, config) and + apa0 = pop(tc, apa) and + flowFwdConsCand(tc, _, apa0, unbind(config)) ) } pragma[nomagic] -private predicate flowConsCand(TypedContent tc, AccessPath ap, Configuration config) { +private predicate flowConsCand(TypedContent tc, AccessPathApprox apa, Configuration config) { exists(Node n, Node mid | - flow(mid, _, _, ap, config) and - readFlowFwd(n, tc, mid, _, ap, config) + flow(mid, _, _, apa, config) and + readFlowFwd(n, tc, mid, _, apa, config) ) } pragma[nomagic] private predicate flowOut( - DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ReturnNodeExt ret, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(Node out, boolean allowsFieldFlow | - flow(out, toReturn, returnAp, ap, config) and + flow(out, toReturn, returnApa, apa, config) and flowOutOfCallNodeCand2(call, ret, out, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowIn( - DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathOption returnAp, AccessPath ap, - Configuration config + DataFlowCall call, ArgumentNode arg, boolean toReturn, AccessPathApproxOption returnApa, + AccessPathApprox apa, Configuration config ) { exists(ParameterNode p, boolean allowsFieldFlow | - flow(p, toReturn, returnAp, ap, config) and + flow(p, toReturn, returnApa, apa, config) and flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) | - ap instanceof AccessPathNil or allowsFieldFlow = true + apa instanceof AccessPathApproxNil or allowsFieldFlow = true ) } pragma[nomagic] private predicate flowInToReturn( - DataFlowCall call, ArgumentNode arg, AccessPath returnAp, AccessPath ap, Configuration config + DataFlowCall call, ArgumentNode arg, AccessPathApprox returnApa, AccessPathApprox apa, + Configuration config ) { - flowIn(call, arg, true, TAccessPathSome(returnAp), ap, config) + flowIn(call, arg, true, TAccessPathApproxSome(returnApa), apa, config) } /** @@ -2023,12 +2028,12 @@ private predicate flowInToReturn( */ pragma[nomagic] private predicate flowIsReturned( - DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap, + DataFlowCall call, boolean toReturn, AccessPathApproxOption returnApa, AccessPathApprox apa, Configuration config ) { exists(ReturnNodeExt ret, CallContextCall ccc | - flowOut(call, ret, toReturn, returnAp, ap, config) and - flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and + flowOut(call, ret, toReturn, returnApa, apa, config) and + flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and ccc.matchesCall(call) ) } @@ -2040,21 +2045,23 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPath ap, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config ) { - flow(p, true, _, ap, config) and + flow(p, true, _, apa, config) and c = p.getEnclosingCallable() } +private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { + exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | + parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + flow(ret, true, TAccessPathApproxSome(_), apa0, config) and + flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { - exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 | - parameterFlow(p, ap, ret.getEnclosingCallable(), config) and - flow(ret, true, TAccessPathSome(_), ap0, config) and - flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config) - ) - } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2089,20 +2096,28 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + private newtype TPathNode = - TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { + TPathNodeMid( + Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, + Configuration config + ) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) and + apa = TNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap) and + pathStep(mid, node, cc, sc, ap, apa) and config = mid.getConfiguration() and - flow(node, _, _, ap, unbind(config)) + flow(node, _, _, apa, unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2114,12 +2129,104 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, any(AccessPathNil nil)) and + pathStep(mid, node, _, _, _, TNil(_)) and config = unbind(mid.getConfiguration()) ) ) } +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPath extends TAccessPath { + /** Gets the type of this access path. */ + abstract DataFlowType getType(); + + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + override DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override DataFlowType getType() { result = tail.getType() } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl() { + tail = TAccessPathNil(_) and + result = head.toString() + or + result = head + ", " + tail.(AccessPathCons).toStringImpl() + } + + override string toString() { + result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + } +} + /** * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. * Only those `PathNode`s that are reachable from a source are generated. @@ -2233,9 +2340,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; + AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } override Node getNode() { result = node } @@ -2245,10 +2353,13 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } + AccessPathApprox getApa() { result = apa } + override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), + result.getAp(), _) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2260,7 +2371,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getAp() instanceof AccessPathNil and + mid.getApa() instanceof AccessPathApproxNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2270,7 +2381,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap instanceof AccessPathNil + apa instanceof AccessPathApproxNil } } @@ -2298,44 +2409,76 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | +private predicate pathStep( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa +) { + pathStepSameAp(mid, node, cc, sc) and + ap = mid.getAp() and + apa = mid.getApa() + or + exists(DataFlowType t | + pathStepEmptyAp(mid, node, cc, sc, t) and + ap = TAccessPathNil(t) and + apa = TNil(t) + ) + or + exists(TypedContent tc, AccessPathApprox apa0 | + pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and + // Same as `apa = ap.getApprox()`, but avoids mutual recursion + apa0 = apa.pop(tc) + ) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() and + // Here the approximation cannot be created from the approximation before + // the read, so we must use `getApprox()` + apa = ap.getApprox() + or + pathThroughCallable(mid, node, cc, ap, apa) and + sc = mid.getSummaryCtx() +} + +pragma[noinline] +private predicate pathStepEmptyAp( + PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t +) { + exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | midnode = mid.getNode() and conf = mid.getConfiguration() and cc = mid.getCallContext() and sc = mid.getSummaryCtx() and localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - ap0 = mid.getAp() - | - localFlowBigStep(midnode, node, true, _, conf, localCC) and - ap = ap0 - or - localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and - ap0 instanceof AccessPathNil + mid.getAp() = TAccessPathNil(_) and + localFlowBigStep(midnode, node, false, apf, conf, localCC) and + apf.getType() = t ) or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - ap = mid.getAp() - or additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - mid.getAp() instanceof AccessPathNil and - ap = TNil(getNodeType(node)) + mid.getAp() = TAccessPathNil(_) and + t = getNodeType(node) +} + +pragma[noinline] +private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { + exists(Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + localFlowBigStep(midnode, node, true, _, conf, localCC) + ) or - exists(TypedContent tc | pathStoreStep(mid, node, pop(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone or - exists(TypedContent tc | pathReadStep(mid, node, push(tc, ap), tc, cc)) and - sc = mid.getSummaryCtx() + pathIntoCallable(mid, node, _, cc, sc, _) or - pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() - or - pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone - or - pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() + pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone } pragma[nomagic] @@ -2361,30 +2504,32 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and + apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } private predicate pathOutOfCallable0( - PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPath ap, Configuration config + PathNodeMid mid, ReturnPosition pos, CallContext innercc, AccessPathApprox apa, + Configuration config ) { pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - ap = mid.getAp() and + apa = mid.getApa() and config = mid.getConfiguration() } pragma[nomagic] private predicate pathOutOfCallable1( - PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPath ap, + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc, AccessPathApprox apa, Configuration config ) { exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | - pathOutOfCallable0(mid, pos, innercc, ap, config) and + pathOutOfCallable0(mid, pos, innercc, apa, config) and c = pos.getCallable() and kind = pos.getKind() and resolveReturn(innercc, c, call) @@ -2395,10 +2540,10 @@ private predicate pathOutOfCallable1( pragma[noinline] private Node getAnOutNodeFlow( - ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config ) { result = kind.getAnOutNode(call) and - flow(result, _, _, ap, config) + flow(result, _, _, apa, config) } /** @@ -2407,10 +2552,9 @@ private Node getAnOutNodeFlow( */ pragma[noinline] private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { - exists(ReturnKindExt kind, DataFlowCall call, AccessPath ap, Configuration config | - pathOutOfCallable1(mid, call, kind, cc, ap, config) - | - out = getAnOutNodeFlow(kind, call, ap, config) + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) ) } @@ -2419,22 +2563,23 @@ private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) { */ pragma[noinline] private predicate pathIntoArg( - PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap + PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa ) { exists(ArgumentNode arg | arg = mid.getNode() and cc = mid.getCallContext() and arg.argumentOf(call, i) and - ap = mid.getAp() + ap = mid.getAp() and + apa = mid.getApa() ) } pragma[noinline] private predicate parameterCand( - DataFlowCallable callable, int i, AccessPath ap, Configuration config + DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config ) { exists(ParameterNode p | - flow(p, _, _, ap, config) and + flow(p, _, _, apa, config) and p.isParameterOf(callable, i) ) } @@ -2442,11 +2587,36 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap + AccessPath ap, AccessPathApprox apa ) { - pathIntoArg(mid, i, outercc, call, ap) and + pathIntoArg(mid, i, outercc, call, ap, apa) and callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration()) + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) +} + +pragma[nomagic] +private predicate pathIntoCallable1( + PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, + CallContextCall innercc, DataFlowCall call +) { + exists(int i, DataFlowCallable callable | + pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and + p.isParameterOf(callable, i) and + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +pragma[nomagic] +private predicate pathIntoCallable1MayFlowThrough( + PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, + DataFlowCall call +) { + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and + parameterMayFlowThrough(p, apa) + ) } /** @@ -2458,26 +2628,23 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(int i, DataFlowCallable callable, AccessPath ap | - pathIntoCallable0(mid, callable, i, outercc, call, ap) and - p.isParameterOf(callable, i) and - ( - sc = TSummaryCtxSome(p, ap) - or - not exists(TSummaryCtxSome(p, ap)) and - sc = TSummaryCtxNone() - ) - | - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() + exists(AccessPath ap | + pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and + sc = TSummaryCtxSome(p, ap) + ) + or + exists(AccessPathApprox apa | + pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and + not parameterMayFlowThrough(p, apa) and + sc = TSummaryCtxNone() ) } /** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ pragma[nomagic] private predicate paramFlowsThrough( - ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config + ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa, + Configuration config ) { exists(PathNodeMid mid, ReturnNodeExt ret, int pos | mid.getNode() = ret and @@ -2486,6 +2653,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and + apa = mid.getApa() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2493,11 +2661,12 @@ private predicate paramFlowsThrough( pragma[nomagic] private predicate pathThroughCallable0( - DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap, + AccessPathApprox apa ) { exists(CallContext innercc, SummaryCtx sc | pathIntoCallable(mid, _, cc, innercc, sc, call) and - paramFlowsThrough(kind, innercc, sc, ap, unbind(mid.getConfiguration())) + paramFlowsThrough(kind, innercc, sc, ap, apa, unbind(mid.getConfiguration())) ) } @@ -2506,10 +2675,12 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { +private predicate pathThroughCallable( + PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa +) { exists(DataFlowCall call, ReturnKindExt kind | - pathThroughCallable0(call, mid, kind, cc, ap) and - out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration()) + pathThroughCallable0(call, mid, kind, cc, ap, apa) and + out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) } From d48a6a55552e7f758fa6305ab07ca7f888bcf414 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 2 Jul 2020 15:47:33 +0200 Subject: [PATCH 140/166] C#: Update expected test output --- .../collections/CollectionFlow.expected | 206 +++++++++--------- .../dataflow/fields/FieldFlow.expected | 154 ++++++------- 2 files changed, 180 insertions(+), 180 deletions(-) diff --git a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected index 89ab1d2a8d9..3c27e96311b 100644 --- a/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/collections/CollectionFlow.expected @@ -8,16 +8,16 @@ edges | CollectionFlow.cs:17:18:17:20 | access to local variable as [[]] : A | CollectionFlow.cs:374:40:374:41 | ts [[]] : A | | CollectionFlow.cs:18:20:18:22 | access to local variable as [[]] : A | CollectionFlow.cs:18:14:18:23 | call to method First | | CollectionFlow.cs:32:17:32:23 | object creation of type A : A | CollectionFlow.cs:33:53:33:53 | access to local variable a : A | -| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] | CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] | -| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] | CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] | -| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] | CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] | -| CollectionFlow.cs:33:45:33:55 | { ..., ... } [[]] : A | CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] : A | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] : A | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] : A | +| CollectionFlow.cs:33:45:33:55 | { ..., ... } [[]] : A | CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | | CollectionFlow.cs:33:53:33:53 | access to local variable a : A | CollectionFlow.cs:33:45:33:55 | { ..., ... } [[]] : A | -| CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] | CollectionFlow.cs:34:14:34:17 | access to field As [[]] : A | +| CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] : A | CollectionFlow.cs:34:14:34:17 | access to field As [[]] : A | | CollectionFlow.cs:34:14:34:17 | access to field As [[]] : A | CollectionFlow.cs:34:14:34:20 | access to array element | -| CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] | CollectionFlow.cs:35:18:35:21 | access to field As [[]] : A | +| CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] : A | CollectionFlow.cs:35:18:35:21 | access to field As [[]] : A | | CollectionFlow.cs:35:18:35:21 | access to field As [[]] : A | CollectionFlow.cs:374:40:374:41 | ts [[]] : A | -| CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] | CollectionFlow.cs:36:20:36:23 | access to field As [[]] : A | +| CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] : A | CollectionFlow.cs:36:20:36:23 | access to field As [[]] : A | | CollectionFlow.cs:36:20:36:23 | access to field As [[]] : A | CollectionFlow.cs:36:14:36:24 | call to method First | | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | CollectionFlow.cs:52:18:52:18 | access to local variable a : A | | CollectionFlow.cs:52:9:52:11 | [post] access to local variable as [[]] : A | CollectionFlow.cs:53:14:53:16 | access to local variable as [[]] : A | @@ -52,63 +52,63 @@ edges | CollectionFlow.cs:110:22:110:25 | access to local variable list [[]] : A | CollectionFlow.cs:376:49:376:52 | list [[]] : A | | CollectionFlow.cs:111:24:111:27 | access to local variable list [[]] : A | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | CollectionFlow.cs:127:19:127:19 | access to local variable a : A | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] | -| CollectionFlow.cs:127:19:127:19 | access to local variable a : A | CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | -| CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] | CollectionFlow.cs:128:14:128:20 | access to indexer | -| CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] | CollectionFlow.cs:378:61:378:64 | dict [[], Value] | -| CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | -| CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] | CollectionFlow.cs:131:14:131:33 | call to method DictFirstValue | -| CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:127:19:127:19 | access to local variable a : A | CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | +| CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] : A | CollectionFlow.cs:128:14:128:20 | access to indexer | +| CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] : A | CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | +| CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] : A | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | +| CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] : A | CollectionFlow.cs:131:14:131:33 | call to method DictFirstValue | +| CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] : A | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | CollectionFlow.cs:149:52:149:52 | access to local variable a : A | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] | -| CollectionFlow.cs:149:52:149:52 | access to local variable a : A | CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | -| CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] | CollectionFlow.cs:150:14:150:20 | access to indexer | -| CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] | CollectionFlow.cs:378:61:378:64 | dict [[], Value] | -| CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | -| CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] | CollectionFlow.cs:153:14:153:33 | call to method DictFirstValue | -| CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:149:52:149:52 | access to local variable a : A | CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | +| CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] : A | CollectionFlow.cs:150:14:150:20 | access to indexer | +| CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] : A | CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | +| CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] : A | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | +| CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] : A | CollectionFlow.cs:153:14:153:33 | call to method DictFirstValue | +| CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] : A | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | | CollectionFlow.cs:169:17:169:23 | object creation of type A : A | CollectionFlow.cs:170:53:170:53 | access to local variable a : A | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] | -| CollectionFlow.cs:170:53:170:53 | access to local variable a : A | CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | -| CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] | CollectionFlow.cs:171:14:171:20 | access to indexer | -| CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] | CollectionFlow.cs:378:61:378:64 | dict [[], Value] | -| CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | -| CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] | CollectionFlow.cs:174:14:174:33 | call to method DictFirstValue | -| CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] : A | +| CollectionFlow.cs:170:53:170:53 | access to local variable a : A | CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | +| CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] : A | CollectionFlow.cs:171:14:171:20 | access to indexer | +| CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] : A | CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | +| CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] : A | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | +| CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] : A | CollectionFlow.cs:174:14:174:33 | call to method DictFirstValue | +| CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] : A | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | | CollectionFlow.cs:191:17:191:23 | object creation of type A : A | CollectionFlow.cs:192:49:192:49 | access to local variable a : A | -| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] | -| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] | -| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] | -| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] | -| CollectionFlow.cs:192:49:192:49 | access to local variable a : A | CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | -| CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] | CollectionFlow.cs:193:14:193:22 | access to property Keys [[]] : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:192:49:192:49 | access to local variable a : A | CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | +| CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] : A | CollectionFlow.cs:193:14:193:22 | access to property Keys [[]] : A | | CollectionFlow.cs:193:14:193:22 | access to property Keys [[]] : A | CollectionFlow.cs:193:14:193:30 | call to method First | -| CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] | CollectionFlow.cs:380:59:380:62 | dict [[], Key] | -| CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] | CollectionFlow.cs:195:14:195:32 | call to method DictKeysFirst | -| CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] | CollectionFlow.cs:196:14:196:31 | call to method DictFirstKey | +| CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] : A | CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | +| CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] : A | CollectionFlow.cs:195:14:195:32 | call to method DictKeysFirst | +| CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] : A | CollectionFlow.cs:196:14:196:31 | call to method DictFirstKey | | CollectionFlow.cs:210:17:210:23 | object creation of type A : A | CollectionFlow.cs:211:48:211:48 | access to local variable a : A | -| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] | -| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] | -| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] | -| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] | -| CollectionFlow.cs:211:48:211:48 | access to local variable a : A | CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | -| CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] | CollectionFlow.cs:212:14:212:22 | access to property Keys [[]] : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] : A | +| CollectionFlow.cs:211:48:211:48 | access to local variable a : A | CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | +| CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] : A | CollectionFlow.cs:212:14:212:22 | access to property Keys [[]] : A | | CollectionFlow.cs:212:14:212:22 | access to property Keys [[]] : A | CollectionFlow.cs:212:14:212:30 | call to method First | -| CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] | CollectionFlow.cs:380:59:380:62 | dict [[], Key] | -| CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] | CollectionFlow.cs:214:14:214:32 | call to method DictKeysFirst | -| CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] | CollectionFlow.cs:215:14:215:31 | call to method DictFirstKey | +| CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] : A | CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | +| CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] : A | CollectionFlow.cs:214:14:214:32 | call to method DictKeysFirst | +| CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] : A | CollectionFlow.cs:215:14:215:31 | call to method DictFirstKey | | CollectionFlow.cs:229:17:229:23 | object creation of type A : A | CollectionFlow.cs:230:27:230:27 | access to local variable a : A | | CollectionFlow.cs:230:25:230:29 | { ..., ... } [[]] : A | CollectionFlow.cs:231:27:231:29 | access to local variable as [[]] : A | | CollectionFlow.cs:230:27:230:27 | access to local variable a : A | CollectionFlow.cs:230:25:230:29 | { ..., ... } [[]] : A | @@ -127,10 +127,10 @@ edges | CollectionFlow.cs:264:26:264:45 | call to method GetEnumerator [Current] : A | CollectionFlow.cs:266:18:266:27 | access to local variable enumerator [Current] : A | | CollectionFlow.cs:266:18:266:27 | access to local variable enumerator [Current] : A | CollectionFlow.cs:266:18:266:35 | access to property Current | | CollectionFlow.cs:280:17:280:23 | object creation of type A : A | CollectionFlow.cs:282:43:282:43 | access to local variable a : A | -| CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] | CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] | -| CollectionFlow.cs:282:18:282:47 | object creation of type KeyValuePair [Key] : A | CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] | +| CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] : A | CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] : A | +| CollectionFlow.cs:282:18:282:47 | object creation of type KeyValuePair [Key] : A | CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] : A | | CollectionFlow.cs:282:43:282:43 | access to local variable a : A | CollectionFlow.cs:282:18:282:47 | object creation of type KeyValuePair [Key] : A | -| CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] | CollectionFlow.cs:283:21:283:23 | kvp [Key] : A | +| CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] : A | CollectionFlow.cs:283:21:283:23 | kvp [Key] : A | | CollectionFlow.cs:283:21:283:23 | kvp [Key] : A | CollectionFlow.cs:285:18:285:20 | access to parameter kvp [Key] : A | | CollectionFlow.cs:285:18:285:20 | access to parameter kvp [Key] : A | CollectionFlow.cs:285:18:285:24 | access to property Key | | CollectionFlow.cs:306:17:306:23 | object creation of type A : A | CollectionFlow.cs:308:23:308:23 | access to local variable a : A | @@ -167,10 +167,10 @@ edges | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | CollectionFlow.cs:376:63:376:69 | access to indexer | | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | CollectionFlow.cs:376:63:376:69 | access to indexer | | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | CollectionFlow.cs:376:63:376:69 | access to indexer | -| CollectionFlow.cs:378:61:378:64 | dict [[], Value] | CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] | -| CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] | CollectionFlow.cs:378:75:378:81 | access to indexer | -| CollectionFlow.cs:380:59:380:62 | dict [[], Key] | CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] | -| CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] | CollectionFlow.cs:380:73:380:81 | access to property Keys [[]] : A | +| CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] : A | +| CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] : A | CollectionFlow.cs:378:75:378:81 | access to indexer | +| CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] : A | +| CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] : A | CollectionFlow.cs:380:73:380:81 | access to property Keys [[]] : A | | CollectionFlow.cs:380:73:380:81 | access to property Keys [[]] : A | CollectionFlow.cs:380:73:380:89 | call to method First | | CollectionFlow.cs:396:49:396:52 | args [[]] : A | CollectionFlow.cs:396:63:396:66 | access to parameter args [[]] : A | | CollectionFlow.cs:396:49:396:52 | args [[]] : A | CollectionFlow.cs:396:63:396:66 | access to parameter args [[]] : A | @@ -186,16 +186,16 @@ nodes | CollectionFlow.cs:18:14:18:23 | call to method First | semmle.label | call to method First | | CollectionFlow.cs:18:20:18:22 | access to local variable as [[]] : A | semmle.label | access to local variable as [[]] : A | | CollectionFlow.cs:32:17:32:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] | semmle.label | { ..., ... } [As, []] | +| CollectionFlow.cs:33:38:33:57 | { ..., ... } [As, []] : A | semmle.label | { ..., ... } [As, []] : A | | CollectionFlow.cs:33:45:33:55 | { ..., ... } [[]] : A | semmle.label | { ..., ... } [[]] : A | | CollectionFlow.cs:33:53:33:53 | access to local variable a : A | semmle.label | access to local variable a : A | -| CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] | semmle.label | access to local variable c [As, []] | +| CollectionFlow.cs:34:14:34:14 | access to local variable c [As, []] : A | semmle.label | access to local variable c [As, []] : A | | CollectionFlow.cs:34:14:34:17 | access to field As [[]] : A | semmle.label | access to field As [[]] : A | | CollectionFlow.cs:34:14:34:20 | access to array element | semmle.label | access to array element | -| CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] | semmle.label | access to local variable c [As, []] | +| CollectionFlow.cs:35:18:35:18 | access to local variable c [As, []] : A | semmle.label | access to local variable c [As, []] : A | | CollectionFlow.cs:35:18:35:21 | access to field As [[]] : A | semmle.label | access to field As [[]] : A | | CollectionFlow.cs:36:14:36:24 | call to method First | semmle.label | call to method First | -| CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] | semmle.label | access to local variable c [As, []] | +| CollectionFlow.cs:36:20:36:20 | access to local variable c [As, []] : A | semmle.label | access to local variable c [As, []] : A | | CollectionFlow.cs:36:20:36:23 | access to field As [[]] : A | semmle.label | access to field As [[]] : A | | CollectionFlow.cs:50:17:50:23 | object creation of type A : A | semmle.label | object creation of type A : A | | CollectionFlow.cs:52:9:52:11 | [post] access to local variable as [[]] : A | semmle.label | [post] access to local variable as [[]] : A | @@ -230,63 +230,63 @@ nodes | CollectionFlow.cs:111:14:111:28 | call to method ListFirst | semmle.label | call to method ListFirst | | CollectionFlow.cs:111:24:111:27 | access to local variable list [[]] : A | semmle.label | access to local variable list [[]] : A | | CollectionFlow.cs:125:17:125:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] | semmle.label | [post] access to local variable dict [[], Value] | +| CollectionFlow.cs:127:9:127:12 | [post] access to local variable dict [[], Value] : A | semmle.label | [post] access to local variable dict [[], Value] : A | | CollectionFlow.cs:127:19:127:19 | access to local variable a : A | semmle.label | access to local variable a : A | -| CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:128:14:128:17 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:128:14:128:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:129:23:129:26 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:130:14:130:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:130:28:130:31 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:131:14:131:33 | call to method DictFirstValue | semmle.label | call to method DictFirstValue | -| CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:131:29:131:32 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:132:14:132:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:132:30:132:33 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:148:17:148:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] | semmle.label | { ..., ... } [[], Value] | +| CollectionFlow.cs:149:45:149:56 | { ..., ... } [[], Value] : A | semmle.label | { ..., ... } [[], Value] : A | | CollectionFlow.cs:149:52:149:52 | access to local variable a : A | semmle.label | access to local variable a : A | -| CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:150:14:150:17 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:150:14:150:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:151:23:151:26 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:152:14:152:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:152:28:152:31 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:153:14:153:33 | call to method DictFirstValue | semmle.label | call to method DictFirstValue | -| CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:153:29:153:32 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:154:14:154:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:154:30:154:33 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:169:17:169:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] | semmle.label | { ..., ... } [[], Value] | +| CollectionFlow.cs:170:45:170:55 | { ..., ... } [[], Value] : A | semmle.label | { ..., ... } [[], Value] : A | | CollectionFlow.cs:170:53:170:53 | access to local variable a : A | semmle.label | access to local variable a : A | -| CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:171:14:171:17 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:171:14:171:20 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:172:23:172:26 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:173:14:173:32 | call to method DictIndexZero | semmle.label | call to method DictIndexZero | -| CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:173:28:173:31 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:174:14:174:33 | call to method DictFirstValue | semmle.label | call to method DictFirstValue | -| CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:174:29:174:32 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:175:14:175:34 | call to method DictValuesFirst | semmle.label | call to method DictValuesFirst | -| CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] | semmle.label | access to local variable dict [[], Value] | +| CollectionFlow.cs:175:30:175:33 | access to local variable dict [[], Value] : A | semmle.label | access to local variable dict [[], Value] : A | | CollectionFlow.cs:191:17:191:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] | semmle.label | { ..., ... } [[], Key] | +| CollectionFlow.cs:192:45:192:56 | { ..., ... } [[], Key] : A | semmle.label | { ..., ... } [[], Key] : A | | CollectionFlow.cs:192:49:192:49 | access to local variable a : A | semmle.label | access to local variable a : A | -| CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:193:14:193:17 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:193:14:193:22 | access to property Keys [[]] : A | semmle.label | access to property Keys [[]] : A | | CollectionFlow.cs:193:14:193:30 | call to method First | semmle.label | call to method First | -| CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:194:21:194:24 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:195:14:195:32 | call to method DictKeysFirst | semmle.label | call to method DictKeysFirst | -| CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:195:28:195:31 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:196:14:196:31 | call to method DictFirstKey | semmle.label | call to method DictFirstKey | -| CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:196:27:196:30 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:210:17:210:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] | semmle.label | { ..., ... } [[], Key] | +| CollectionFlow.cs:211:45:211:55 | { ..., ... } [[], Key] : A | semmle.label | { ..., ... } [[], Key] : A | | CollectionFlow.cs:211:48:211:48 | access to local variable a : A | semmle.label | access to local variable a : A | -| CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:212:14:212:17 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:212:14:212:22 | access to property Keys [[]] : A | semmle.label | access to property Keys [[]] : A | | CollectionFlow.cs:212:14:212:30 | call to method First | semmle.label | call to method First | -| CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:213:21:213:24 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:214:14:214:32 | call to method DictKeysFirst | semmle.label | call to method DictKeysFirst | -| CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:214:28:214:31 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:215:14:215:31 | call to method DictFirstKey | semmle.label | call to method DictFirstKey | -| CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] | semmle.label | access to local variable dict [[], Key] | +| CollectionFlow.cs:215:27:215:30 | access to local variable dict [[], Key] : A | semmle.label | access to local variable dict [[], Key] : A | | CollectionFlow.cs:229:17:229:23 | object creation of type A : A | semmle.label | object creation of type A : A | | CollectionFlow.cs:230:25:230:29 | { ..., ... } [[]] : A | semmle.label | { ..., ... } [[]] : A | | CollectionFlow.cs:230:27:230:27 | access to local variable a : A | semmle.label | access to local variable a : A | @@ -308,10 +308,10 @@ nodes | CollectionFlow.cs:266:18:266:27 | access to local variable enumerator [Current] : A | semmle.label | access to local variable enumerator [Current] : A | | CollectionFlow.cs:266:18:266:35 | access to property Current | semmle.label | access to property Current | | CollectionFlow.cs:280:17:280:23 | object creation of type A : A | semmle.label | object creation of type A : A | -| CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] | semmle.label | [post] access to local variable list [[], Key] | +| CollectionFlow.cs:282:9:282:12 | [post] access to local variable list [[], Key] : A | semmle.label | [post] access to local variable list [[], Key] : A | | CollectionFlow.cs:282:18:282:47 | object creation of type KeyValuePair [Key] : A | semmle.label | object creation of type KeyValuePair [Key] : A | | CollectionFlow.cs:282:43:282:43 | access to local variable a : A | semmle.label | access to local variable a : A | -| CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] | semmle.label | access to local variable list [[], Key] | +| CollectionFlow.cs:283:9:283:12 | access to local variable list [[], Key] : A | semmle.label | access to local variable list [[], Key] : A | | CollectionFlow.cs:283:21:283:23 | kvp [Key] : A | semmle.label | kvp [Key] : A | | CollectionFlow.cs:285:18:285:20 | access to parameter kvp [Key] : A | semmle.label | access to parameter kvp [Key] : A | | CollectionFlow.cs:285:18:285:24 | access to property Key | semmle.label | access to property Key | @@ -351,11 +351,11 @@ nodes | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | semmle.label | access to parameter list [[]] : A | | CollectionFlow.cs:376:63:376:66 | access to parameter list [[]] : A | semmle.label | access to parameter list [[]] : A | | CollectionFlow.cs:376:63:376:69 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:378:61:378:64 | dict [[], Value] | semmle.label | dict [[], Value] | -| CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] | semmle.label | access to parameter dict [[], Value] | +| CollectionFlow.cs:378:61:378:64 | dict [[], Value] : A | semmle.label | dict [[], Value] : A | +| CollectionFlow.cs:378:75:378:78 | access to parameter dict [[], Value] : A | semmle.label | access to parameter dict [[], Value] : A | | CollectionFlow.cs:378:75:378:81 | access to indexer | semmle.label | access to indexer | -| CollectionFlow.cs:380:59:380:62 | dict [[], Key] | semmle.label | dict [[], Key] | -| CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] | semmle.label | access to parameter dict [[], Key] | +| CollectionFlow.cs:380:59:380:62 | dict [[], Key] : A | semmle.label | dict [[], Key] : A | +| CollectionFlow.cs:380:73:380:76 | access to parameter dict [[], Key] : A | semmle.label | access to parameter dict [[], Key] : A | | CollectionFlow.cs:380:73:380:81 | access to property Keys [[]] : A | semmle.label | access to property Keys [[]] : A | | CollectionFlow.cs:380:73:380:89 | call to method First | semmle.label | call to method First | | CollectionFlow.cs:396:49:396:52 | args [[]] : A | semmle.label | args [[]] : A | diff --git a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected index ff675293cf7..8f1a95d58c7 100644 --- a/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/fields/FieldFlow.expected @@ -27,49 +27,49 @@ edges | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | A.cs:98:22:98:36 | ... ? ... : ... [c] : C | | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | A.cs:105:23:105:23 | [post] access to local variable b [c] : C | | A.cs:97:19:97:25 | object creation of type C : C | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | -| A.cs:98:13:98:16 | [post] this access [b, c] | A.cs:105:17:105:29 | object creation of type D [b, c] | +| A.cs:98:13:98:16 | [post] this access [b, c] : C | A.cs:105:17:105:29 | object creation of type D [b, c] : C | | A.cs:98:13:98:16 | [post] this access [b] : B | A.cs:105:17:105:29 | object creation of type D [b] : B | | A.cs:98:22:98:36 | ... ? ... : ... : B | A.cs:98:13:98:16 | [post] this access [b] : B | -| A.cs:98:22:98:36 | ... ? ... : ... [c] : C | A.cs:98:13:98:16 | [post] this access [b, c] | +| A.cs:98:22:98:36 | ... ? ... : ... [c] : C | A.cs:98:13:98:16 | [post] this access [b, c] : C | | A.cs:98:30:98:36 | object creation of type B : B | A.cs:98:22:98:36 | ... ? ... : ... : B | | A.cs:104:17:104:23 | object creation of type B : B | A.cs:105:23:105:23 | access to local variable b : B | -| A.cs:105:17:105:29 | object creation of type D [b, c] | A.cs:107:14:107:14 | access to local variable d [b, c] | +| A.cs:105:17:105:29 | object creation of type D [b, c] : C | A.cs:107:14:107:14 | access to local variable d [b, c] : C | | A.cs:105:17:105:29 | object creation of type D [b] : B | A.cs:106:14:106:14 | access to local variable d [b] : B | | A.cs:105:23:105:23 | [post] access to local variable b [c] : C | A.cs:108:14:108:14 | access to local variable b [c] : C | | A.cs:105:23:105:23 | access to local variable b : B | A.cs:105:17:105:29 | object creation of type D [b] : B | | A.cs:106:14:106:14 | access to local variable d [b] : B | A.cs:106:14:106:16 | access to field b | -| A.cs:107:14:107:14 | access to local variable d [b, c] | A.cs:107:14:107:16 | access to field b [c] : C | +| A.cs:107:14:107:14 | access to local variable d [b, c] : C | A.cs:107:14:107:16 | access to field b [c] : C | | A.cs:107:14:107:16 | access to field b [c] : C | A.cs:107:14:107:18 | access to field c | | A.cs:108:14:108:14 | access to local variable b [c] : C | A.cs:108:14:108:16 | access to field c | | A.cs:113:17:113:23 | object creation of type B : B | A.cs:114:29:114:29 | access to local variable b : B | | A.cs:114:18:114:54 | object creation of type MyList [head] : B | A.cs:115:35:115:36 | access to local variable l1 [head] : B | | A.cs:114:29:114:29 | access to local variable b : B | A.cs:114:18:114:54 | object creation of type MyList [head] : B | -| A.cs:115:18:115:37 | object creation of type MyList [next, head] | A.cs:116:35:116:36 | access to local variable l2 [next, head] | -| A.cs:115:35:115:36 | access to local variable l1 [head] : B | A.cs:115:18:115:37 | object creation of type MyList [next, head] | -| A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | A.cs:119:14:119:15 | access to local variable l3 [next, next, ... (3)] | -| A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | A.cs:121:41:121:41 | access to local variable l [next, next, ... (3)] | -| A.cs:116:35:116:36 | access to local variable l2 [next, head] | A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | -| A.cs:119:14:119:15 | access to local variable l3 [next, next, ... (3)] | A.cs:119:14:119:20 | access to field next [next, head] | -| A.cs:119:14:119:20 | access to field next [next, head] | A.cs:119:14:119:25 | access to field next [head] : B | +| A.cs:115:18:115:37 | object creation of type MyList [next, head] : B | A.cs:116:35:116:36 | access to local variable l2 [next, head] : B | +| A.cs:115:35:115:36 | access to local variable l1 [head] : B | A.cs:115:18:115:37 | object creation of type MyList [next, head] : B | +| A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | A.cs:119:14:119:15 | access to local variable l3 [next, next, head] : B | +| A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | A.cs:121:41:121:41 | access to local variable l [next, next, head] : B | +| A.cs:116:35:116:36 | access to local variable l2 [next, head] : B | A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | +| A.cs:119:14:119:15 | access to local variable l3 [next, next, head] : B | A.cs:119:14:119:20 | access to field next [next, head] : B | +| A.cs:119:14:119:20 | access to field next [next, head] : B | A.cs:119:14:119:25 | access to field next [head] : B | | A.cs:119:14:119:25 | access to field next [head] : B | A.cs:119:14:119:30 | access to field head | -| A.cs:121:41:121:41 | access to local variable l [next, head] | A.cs:121:41:121:46 | access to field next [head] : B | -| A.cs:121:41:121:41 | access to local variable l [next, next, ... (3)] | A.cs:121:41:121:46 | access to field next [next, head] | +| A.cs:121:41:121:41 | access to local variable l [next, head] : B | A.cs:121:41:121:46 | access to field next [head] : B | +| A.cs:121:41:121:41 | access to local variable l [next, next, head] : B | A.cs:121:41:121:46 | access to field next [next, head] : B | | A.cs:121:41:121:46 | access to field next [head] : B | A.cs:123:18:123:18 | access to local variable l [head] : B | -| A.cs:121:41:121:46 | access to field next [next, head] | A.cs:121:41:121:41 | access to local variable l [next, head] | +| A.cs:121:41:121:46 | access to field next [next, head] : B | A.cs:121:41:121:41 | access to local variable l [next, head] : B | | A.cs:123:18:123:18 | access to local variable l [head] : B | A.cs:123:18:123:23 | access to field head | | B.cs:5:17:5:26 | object creation of type Elem : Elem | B.cs:6:27:6:27 | access to local variable e : Elem | | B.cs:6:18:6:34 | object creation of type Box1 [elem1] : Elem | B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | B.cs:6:18:6:34 | object creation of type Box1 [elem1] : Elem | -| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] | B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] | -| B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] | -| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | +| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] : Elem | B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] : Elem | +| B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] : Elem | +| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] : Elem | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | B.cs:8:14:8:26 | access to field elem1 | | B.cs:14:17:14:26 | object creation of type Elem : Elem | B.cs:15:33:15:33 | access to local variable e : Elem | | B.cs:15:18:15:34 | object creation of type Box1 [elem2] : Elem | B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | B.cs:15:18:15:34 | object creation of type Box1 [elem2] : Elem | -| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] | B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] | -| B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] | -| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | +| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] : Elem | B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] : Elem | +| B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] : Elem | +| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] : Elem | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | B.cs:18:14:18:26 | access to field elem2 | | C.cs:3:18:3:19 | [post] this access [s1] : Elem | C.cs:12:15:12:21 | object creation of type C [s1] : Elem | | C.cs:3:23:3:32 | object creation of type Elem : Elem | C.cs:3:18:3:19 | [post] this access [s1] : Elem | @@ -138,37 +138,37 @@ edges | F.cs:23:32:23:32 | access to local variable o : Object | F.cs:23:21:23:34 | { ..., ... } [Field2] : Object | | F.cs:25:14:25:14 | access to local variable f [Field2] : Object | F.cs:25:14:25:21 | access to field Field2 | | G.cs:7:18:7:27 | object creation of type Elem : Elem | G.cs:9:23:9:23 | access to local variable e : Elem | -| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] | G.cs:10:18:10:18 | access to local variable b [Box1, Elem] | -| G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:10:18:10:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:9:23:9:23 | access to local variable e : Elem | G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | -| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | +| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | | G.cs:15:18:15:27 | object creation of type Elem : Elem | G.cs:17:24:17:24 | access to local variable e : Elem | -| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] | G.cs:18:18:18:18 | access to local variable b [Box1, Elem] | -| G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:18:18:18:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | -| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | +| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | | G.cs:23:18:23:27 | object creation of type Elem : Elem | G.cs:25:28:25:28 | access to local variable e : Elem | -| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] | G.cs:26:18:26:18 | access to local variable b [Box1, Elem] | -| G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:26:18:26:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:25:28:25:28 | access to local variable e : Elem | G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | -| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | +| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | | G.cs:31:18:31:27 | object creation of type Elem : Elem | G.cs:33:29:33:29 | access to local variable e : Elem | -| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] | G.cs:34:18:34:18 | access to local variable b [Box1, Elem] | -| G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] | +| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] : Elem | G.cs:34:18:34:18 | access to local variable b [Box1, Elem] : Elem | +| G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | -| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] | G.cs:37:38:37:39 | b2 [Box1, Elem] | -| G.cs:37:38:37:39 | b2 [Box1, Elem] | G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] | -| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | +| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] : Elem | G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | +| G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] : Elem | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | G.cs:39:14:39:35 | call to method GetElem | | G.cs:44:18:44:27 | object creation of type Elem : Elem | G.cs:46:30:46:30 | access to local variable e : Elem | -| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] | G.cs:46:9:46:16 | [post] this access [boxfield, Box1, ... (3)] | -| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, ... (3)] | G.cs:47:9:47:13 | this access [boxfield, Box1, ... (3)] | -| G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] | +| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] : Elem | G.cs:46:9:46:16 | [post] this access [boxfield, Box1, Elem] : Elem | +| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, Elem] : Elem | G.cs:47:9:47:13 | this access [boxfield, Box1, Elem] : Elem | +| G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] : Elem | | G.cs:46:30:46:30 | access to local variable e : Elem | G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | -| G.cs:47:9:47:13 | this access [boxfield, Box1, ... (3)] | G.cs:50:18:50:20 | this [boxfield, Box1, ... (3)] | -| G.cs:50:18:50:20 | this [boxfield, Box1, ... (3)] | G.cs:52:14:52:21 | this access [boxfield, Box1, ... (3)] | -| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | -| G.cs:52:14:52:21 | this access [boxfield, Box1, ... (3)] | G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] | +| G.cs:47:9:47:13 | this access [boxfield, Box1, Elem] : Elem | G.cs:50:18:50:20 | this [boxfield, Box1, Elem] : Elem | +| G.cs:50:18:50:20 | this [boxfield, Box1, Elem] : Elem | G.cs:52:14:52:21 | this access [boxfield, Box1, Elem] : Elem | +| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] : Elem | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | +| G.cs:52:14:52:21 | this access [boxfield, Box1, Elem] : Elem | G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] : Elem | | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | G.cs:52:14:52:31 | access to field Elem | | H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object | H.cs:24:27:24:27 | access to local variable a [FieldA] : Object | | H.cs:23:20:23:31 | object creation of type Object : Object | H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object | @@ -206,12 +206,12 @@ edges | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | H.cs:164:19:164:19 | [post] access to local variable a [FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | | H.cs:163:17:163:28 | object creation of type Object : Object | H.cs:164:22:164:22 | access to local variable o : Object | -| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] | H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] | +| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] : Object | H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] : Object | | H.cs:164:19:164:19 | [post] access to local variable a [FieldA] : B | H.cs:165:21:165:21 | access to local variable a [FieldA] : B | -| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] | +| H.cs:164:22:164:22 | access to local variable o : Object | H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] : Object | | H.cs:165:17:165:28 | (...) ... : B | H.cs:166:14:166:14 | access to local variable b | | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | H.cs:167:14:167:14 | access to local variable b [FieldB] : Object | -| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | +| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] : Object | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | | H.cs:165:21:165:21 | access to local variable a [FieldA] : B | H.cs:165:21:165:28 | access to field FieldA : B | | H.cs:165:21:165:28 | access to field FieldA : B | H.cs:165:17:165:28 | (...) ... : B | | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | @@ -273,19 +273,19 @@ nodes | A.cs:89:14:89:16 | access to field c | semmle.label | access to field c | | A.cs:97:13:97:13 | [post] access to parameter b [c] : C | semmle.label | [post] access to parameter b [c] : C | | A.cs:97:19:97:25 | object creation of type C : C | semmle.label | object creation of type C : C | -| A.cs:98:13:98:16 | [post] this access [b, c] | semmle.label | [post] this access [b, c] | +| A.cs:98:13:98:16 | [post] this access [b, c] : C | semmle.label | [post] this access [b, c] : C | | A.cs:98:13:98:16 | [post] this access [b] : B | semmle.label | [post] this access [b] : B | | A.cs:98:22:98:36 | ... ? ... : ... : B | semmle.label | ... ? ... : ... : B | | A.cs:98:22:98:36 | ... ? ... : ... [c] : C | semmle.label | ... ? ... : ... [c] : C | | A.cs:98:30:98:36 | object creation of type B : B | semmle.label | object creation of type B : B | | A.cs:104:17:104:23 | object creation of type B : B | semmle.label | object creation of type B : B | -| A.cs:105:17:105:29 | object creation of type D [b, c] | semmle.label | object creation of type D [b, c] | +| A.cs:105:17:105:29 | object creation of type D [b, c] : C | semmle.label | object creation of type D [b, c] : C | | A.cs:105:17:105:29 | object creation of type D [b] : B | semmle.label | object creation of type D [b] : B | | A.cs:105:23:105:23 | [post] access to local variable b [c] : C | semmle.label | [post] access to local variable b [c] : C | | A.cs:105:23:105:23 | access to local variable b : B | semmle.label | access to local variable b : B | | A.cs:106:14:106:14 | access to local variable d [b] : B | semmle.label | access to local variable d [b] : B | | A.cs:106:14:106:16 | access to field b | semmle.label | access to field b | -| A.cs:107:14:107:14 | access to local variable d [b, c] | semmle.label | access to local variable d [b, c] | +| A.cs:107:14:107:14 | access to local variable d [b, c] : C | semmle.label | access to local variable d [b, c] : C | | A.cs:107:14:107:16 | access to field b [c] : C | semmle.label | access to field b [c] : C | | A.cs:107:14:107:18 | access to field c | semmle.label | access to field c | | A.cs:108:14:108:14 | access to local variable b [c] : C | semmle.label | access to local variable b [c] : C | @@ -293,34 +293,34 @@ nodes | A.cs:113:17:113:23 | object creation of type B : B | semmle.label | object creation of type B : B | | A.cs:114:18:114:54 | object creation of type MyList [head] : B | semmle.label | object creation of type MyList [head] : B | | A.cs:114:29:114:29 | access to local variable b : B | semmle.label | access to local variable b : B | -| A.cs:115:18:115:37 | object creation of type MyList [next, head] | semmle.label | object creation of type MyList [next, head] | +| A.cs:115:18:115:37 | object creation of type MyList [next, head] : B | semmle.label | object creation of type MyList [next, head] : B | | A.cs:115:35:115:36 | access to local variable l1 [head] : B | semmle.label | access to local variable l1 [head] : B | -| A.cs:116:18:116:37 | object creation of type MyList [next, next, ... (3)] | semmle.label | object creation of type MyList [next, next, ... (3)] | -| A.cs:116:35:116:36 | access to local variable l2 [next, head] | semmle.label | access to local variable l2 [next, head] | -| A.cs:119:14:119:15 | access to local variable l3 [next, next, ... (3)] | semmle.label | access to local variable l3 [next, next, ... (3)] | -| A.cs:119:14:119:20 | access to field next [next, head] | semmle.label | access to field next [next, head] | +| A.cs:116:18:116:37 | object creation of type MyList [next, next, head] : B | semmle.label | object creation of type MyList [next, next, head] : B | +| A.cs:116:35:116:36 | access to local variable l2 [next, head] : B | semmle.label | access to local variable l2 [next, head] : B | +| A.cs:119:14:119:15 | access to local variable l3 [next, next, head] : B | semmle.label | access to local variable l3 [next, next, head] : B | +| A.cs:119:14:119:20 | access to field next [next, head] : B | semmle.label | access to field next [next, head] : B | | A.cs:119:14:119:25 | access to field next [head] : B | semmle.label | access to field next [head] : B | | A.cs:119:14:119:30 | access to field head | semmle.label | access to field head | -| A.cs:121:41:121:41 | access to local variable l [next, head] | semmle.label | access to local variable l [next, head] | -| A.cs:121:41:121:41 | access to local variable l [next, next, ... (3)] | semmle.label | access to local variable l [next, next, ... (3)] | +| A.cs:121:41:121:41 | access to local variable l [next, head] : B | semmle.label | access to local variable l [next, head] : B | +| A.cs:121:41:121:41 | access to local variable l [next, next, head] : B | semmle.label | access to local variable l [next, next, head] : B | | A.cs:121:41:121:46 | access to field next [head] : B | semmle.label | access to field next [head] : B | -| A.cs:121:41:121:46 | access to field next [next, head] | semmle.label | access to field next [next, head] | +| A.cs:121:41:121:46 | access to field next [next, head] : B | semmle.label | access to field next [next, head] : B | | A.cs:123:18:123:18 | access to local variable l [head] : B | semmle.label | access to local variable l [head] : B | | A.cs:123:18:123:23 | access to field head | semmle.label | access to field head | | B.cs:5:17:5:26 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | | B.cs:6:18:6:34 | object creation of type Box1 [elem1] : Elem | semmle.label | object creation of type Box1 [elem1] : Elem | | B.cs:6:27:6:27 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] | semmle.label | object creation of type Box2 [box1, elem1] | +| B.cs:7:18:7:29 | object creation of type Box2 [box1, elem1] : Elem | semmle.label | object creation of type Box2 [box1, elem1] : Elem | | B.cs:7:27:7:28 | access to local variable b1 [elem1] : Elem | semmle.label | access to local variable b1 [elem1] : Elem | -| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] | semmle.label | access to local variable b2 [box1, elem1] | +| B.cs:8:14:8:15 | access to local variable b2 [box1, elem1] : Elem | semmle.label | access to local variable b2 [box1, elem1] : Elem | | B.cs:8:14:8:20 | access to field box1 [elem1] : Elem | semmle.label | access to field box1 [elem1] : Elem | | B.cs:8:14:8:26 | access to field elem1 | semmle.label | access to field elem1 | | B.cs:14:17:14:26 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | | B.cs:15:18:15:34 | object creation of type Box1 [elem2] : Elem | semmle.label | object creation of type Box1 [elem2] : Elem | | B.cs:15:33:15:33 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] | semmle.label | object creation of type Box2 [box1, elem2] | +| B.cs:16:18:16:29 | object creation of type Box2 [box1, elem2] : Elem | semmle.label | object creation of type Box2 [box1, elem2] : Elem | | B.cs:16:27:16:28 | access to local variable b1 [elem2] : Elem | semmle.label | access to local variable b1 [elem2] : Elem | -| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] | semmle.label | access to local variable b2 [box1, elem2] | +| B.cs:18:14:18:15 | access to local variable b2 [box1, elem2] : Elem | semmle.label | access to local variable b2 [box1, elem2] : Elem | | B.cs:18:14:18:20 | access to field box1 [elem2] : Elem | semmle.label | access to field box1 [elem2] : Elem | | B.cs:18:14:18:26 | access to field elem2 | semmle.label | access to field elem2 | | C.cs:3:18:3:19 | [post] this access [s1] : Elem | semmle.label | [post] this access [s1] : Elem | @@ -399,38 +399,38 @@ nodes | F.cs:25:14:25:14 | access to local variable f [Field2] : Object | semmle.label | access to local variable f [Field2] : Object | | F.cs:25:14:25:21 | access to field Field2 | semmle.label | access to field Field2 | | G.cs:7:18:7:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:9:9:9:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:9:9:9:14 | [post] access to field Box1 [Elem] : Elem | semmle.label | [post] access to field Box1 [Elem] : Elem | | G.cs:9:23:9:23 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | +| G.cs:10:18:10:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | | G.cs:15:18:15:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:17:9:17:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:17:9:17:14 | [post] access to field Box1 [Elem] : Elem | semmle.label | [post] access to field Box1 [Elem] : Elem | | G.cs:17:24:17:24 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | +| G.cs:18:18:18:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | | G.cs:23:18:23:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:25:9:25:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:25:9:25:19 | [post] call to method GetBox1 [Elem] : Elem | semmle.label | [post] call to method GetBox1 [Elem] : Elem | | G.cs:25:28:25:28 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | +| G.cs:26:18:26:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | | G.cs:31:18:31:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] | semmle.label | [post] access to local variable b [Box1, Elem] | +| G.cs:33:9:33:9 | [post] access to local variable b [Box1, Elem] : Elem | semmle.label | [post] access to local variable b [Box1, Elem] : Elem | | G.cs:33:9:33:19 | [post] call to method GetBox1 [Elem] : Elem | semmle.label | [post] call to method GetBox1 [Elem] : Elem | | G.cs:33:29:33:29 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] | semmle.label | access to local variable b [Box1, Elem] | -| G.cs:37:38:37:39 | b2 [Box1, Elem] | semmle.label | b2 [Box1, Elem] | -| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] | semmle.label | access to parameter b2 [Box1, Elem] | +| G.cs:34:18:34:18 | access to local variable b [Box1, Elem] : Elem | semmle.label | access to local variable b [Box1, Elem] : Elem | +| G.cs:37:38:37:39 | b2 [Box1, Elem] : Elem | semmle.label | b2 [Box1, Elem] : Elem | +| G.cs:39:14:39:15 | access to parameter b2 [Box1, Elem] : Elem | semmle.label | access to parameter b2 [Box1, Elem] : Elem | | G.cs:39:14:39:25 | call to method GetBox1 [Elem] : Elem | semmle.label | call to method GetBox1 [Elem] : Elem | | G.cs:39:14:39:35 | call to method GetElem | semmle.label | call to method GetElem | | G.cs:44:18:44:27 | object creation of type Elem : Elem | semmle.label | object creation of type Elem : Elem | -| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] | semmle.label | [post] access to field boxfield [Box1, Elem] | -| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, ... (3)] | semmle.label | [post] this access [boxfield, Box1, ... (3)] | +| G.cs:46:9:46:16 | [post] access to field boxfield [Box1, Elem] : Elem | semmle.label | [post] access to field boxfield [Box1, Elem] : Elem | +| G.cs:46:9:46:16 | [post] this access [boxfield, Box1, Elem] : Elem | semmle.label | [post] this access [boxfield, Box1, Elem] : Elem | | G.cs:46:9:46:21 | [post] access to field Box1 [Elem] : Elem | semmle.label | [post] access to field Box1 [Elem] : Elem | | G.cs:46:30:46:30 | access to local variable e : Elem | semmle.label | access to local variable e : Elem | -| G.cs:47:9:47:13 | this access [boxfield, Box1, ... (3)] | semmle.label | this access [boxfield, Box1, ... (3)] | -| G.cs:50:18:50:20 | this [boxfield, Box1, ... (3)] | semmle.label | this [boxfield, Box1, ... (3)] | -| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] | semmle.label | access to field boxfield [Box1, Elem] | -| G.cs:52:14:52:21 | this access [boxfield, Box1, ... (3)] | semmle.label | this access [boxfield, Box1, ... (3)] | +| G.cs:47:9:47:13 | this access [boxfield, Box1, Elem] : Elem | semmle.label | this access [boxfield, Box1, Elem] : Elem | +| G.cs:50:18:50:20 | this [boxfield, Box1, Elem] : Elem | semmle.label | this [boxfield, Box1, Elem] : Elem | +| G.cs:52:14:52:21 | access to field boxfield [Box1, Elem] : Elem | semmle.label | access to field boxfield [Box1, Elem] : Elem | +| G.cs:52:14:52:21 | this access [boxfield, Box1, Elem] : Elem | semmle.label | this access [boxfield, Box1, Elem] : Elem | | G.cs:52:14:52:26 | access to field Box1 [Elem] : Elem | semmle.label | access to field Box1 [Elem] : Elem | | G.cs:52:14:52:31 | access to field Elem | semmle.label | access to field Elem | | H.cs:23:9:23:9 | [post] access to local variable a [FieldA] : Object | semmle.label | [post] access to local variable a [FieldA] : Object | @@ -476,12 +476,12 @@ nodes | H.cs:157:9:157:9 | [post] access to parameter a [FieldA] : B | semmle.label | [post] access to parameter a [FieldA] : B | | H.cs:157:20:157:20 | access to local variable b : B | semmle.label | access to local variable b : B | | H.cs:163:17:163:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | -| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] | semmle.label | [post] access to local variable a [FieldA, FieldB] | +| H.cs:164:19:164:19 | [post] access to local variable a [FieldA, FieldB] : Object | semmle.label | [post] access to local variable a [FieldA, FieldB] : Object | | H.cs:164:19:164:19 | [post] access to local variable a [FieldA] : B | semmle.label | [post] access to local variable a [FieldA] : B | | H.cs:164:22:164:22 | access to local variable o : Object | semmle.label | access to local variable o : Object | | H.cs:165:17:165:28 | (...) ... : B | semmle.label | (...) ... : B | | H.cs:165:17:165:28 | (...) ... [FieldB] : Object | semmle.label | (...) ... [FieldB] : Object | -| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] | semmle.label | access to local variable a [FieldA, FieldB] | +| H.cs:165:21:165:21 | access to local variable a [FieldA, FieldB] : Object | semmle.label | access to local variable a [FieldA, FieldB] : Object | | H.cs:165:21:165:21 | access to local variable a [FieldA] : B | semmle.label | access to local variable a [FieldA] : B | | H.cs:165:21:165:28 | access to field FieldA : B | semmle.label | access to field FieldA : B | | H.cs:165:21:165:28 | access to field FieldA [FieldB] : Object | semmle.label | access to field FieldA [FieldB] : Object | From 570b624eb705f9e9c2dd442e5545b2ae69c91f14 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 2 Jul 2020 15:47:43 +0200 Subject: [PATCH 141/166] C++: Update expected test output --- .../library-tests/dataflow/fields/complex.cpp | 9 - .../dataflow/fields/flow-diff.expected | 4 - .../dataflow/fields/flow.expected | 12 + .../dataflow/fields/ir-flow.expected | 16 +- .../dataflow/fields/ir-path-flow.expected | 60 ++--- .../fields/partial-definition-diff.expected | 44 ++-- .../fields/partial-definition.expected | 44 ++-- .../dataflow/fields/path-flow.expected | 217 +++++++++--------- 8 files changed, 206 insertions(+), 200 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp index 13769912d30..7820457b011 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp @@ -39,15 +39,6 @@ void sink(int x) void bar(Outer &b) { - // The library correctly finds that the four `user_input` sources can make it - // to the `sink` calls, but it also finds some source/sink combinations that - // are impossible. Those false positives here are a consequence of how the - // shared data flow library overapproximates field flow. The library only - // tracks the final two fields (`f` and `inner`) and the length (3) of the field - // access path, and then it tracks that both `a_` and `b_` have followed `f.inner` - // in _some_ access path somewhere in the search. That makes the library conclude - // that there could be flow to `b.inner.f.a_` even when the flow was actually to - // `b.inner.f.b_`. sink(b.inner.f.a()); // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 sink(b.inner.f.b()); // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 } diff --git a/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected index 2686f48f236..242c09f3a3b 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected @@ -34,10 +34,6 @@ | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:115:27:115:27 | a | AST only | | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:131:25:131:25 | a | AST only | | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | AST only | -| complex.cpp:62:19:62:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | AST only | -| complex.cpp:63:19:63:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | AST only | -| complex.cpp:64:19:64:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | AST only | -| complex.cpp:65:19:65:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | AST only | | qualifiers.cpp:22:27:22:36 | call to user_input | qualifiers.cpp:23:23:23:23 | a | AST only | | qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:28:23:28:23 | a | AST only | | qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:33:23:33:23 | a | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/flow.expected b/cpp/ql/test/library-tests/dataflow/fields/flow.expected index e69de29bb2d..0eb62024689 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/flow.expected @@ -0,0 +1,12 @@ +| complex.cpp:42:18:42:18 | call to a | Unexpected result: ast=53:19 | +| complex.cpp:42:18:42:18 | call to a | Unexpected result: ast=55:19 | +| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ast=63:19 | +| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ast=65:19 | +| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Missing result:ast=62:19 | +| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Missing result:ast=64:19 | +| complex.cpp:43:18:43:18 | call to b | Unexpected result: ast=54:19 | +| complex.cpp:43:18:43:18 | call to b | Unexpected result: ast=56:19 | +| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ast=62:19 | +| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ast=64:19 | +| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Missing result:ast=63:19 | +| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Missing result:ast=65:19 | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected index d24c311a65b..9e241750a0f 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected @@ -1,4 +1,12 @@ -| complex.cpp:51:24:51:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ir=63:19 | -| complex.cpp:51:24:51:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ir=65:19 | -| complex.cpp:52:24:52:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ir=62:19 | -| complex.cpp:52:24:52:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ir=64:19 | +| complex.cpp:42:18:42:18 | call to a | Unexpected result: ir=53:19 | +| complex.cpp:42:18:42:18 | call to a | Unexpected result: ir=55:19 | +| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ir=63:19 | +| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ir=65:19 | +| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Missing result:ir=62:19 | +| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Missing result:ir=64:19 | +| complex.cpp:43:18:43:18 | call to b | Unexpected result: ir=54:19 | +| complex.cpp:43:18:43:18 | call to b | Unexpected result: ir=56:19 | +| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ir=62:19 | +| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ir=64:19 | +| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Missing result:ir=63:19 | +| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Missing result:ir=65:19 | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index ed18d6a633e..61770c0aca3 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected @@ -140,20 +140,20 @@ edges | by_reference.cpp:128:15:128:23 | Chi | by_reference.cpp:128:15:128:23 | Chi [a] | | by_reference.cpp:128:15:128:23 | Chi [a] | by_reference.cpp:136:16:136:16 | a | | by_reference.cpp:128:15:128:23 | taint_a_ref output argument [array content] | by_reference.cpp:128:15:128:23 | Chi | -| complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:51:18:51:18 | call to a | -| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:51:16:51:16 | a output argument [b_] | -| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:52:18:52:18 | call to b | -| complex.cpp:51:16:51:16 | a output argument [b_] | complex.cpp:52:18:52:18 | call to b | -| complex.cpp:62:12:62:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | -| complex.cpp:62:19:62:28 | call to user_input | complex.cpp:62:12:62:12 | setA output argument [a_] | -| complex.cpp:63:12:63:12 | setB output argument [b_] | complex.cpp:40:17:40:17 | *b [b_] | -| complex.cpp:63:19:63:28 | call to user_input | complex.cpp:63:12:63:12 | setB output argument [b_] | -| complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | -| complex.cpp:64:12:64:12 | setA output argument [a_] | complex.cpp:65:12:65:12 | setB output argument [a_] | -| complex.cpp:64:19:64:28 | call to user_input | complex.cpp:64:12:64:12 | setA output argument [a_] | -| complex.cpp:65:12:65:12 | setB output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | -| complex.cpp:65:12:65:12 | setB output argument [b_] | complex.cpp:40:17:40:17 | *b [b_] | -| complex.cpp:65:19:65:28 | call to user_input | complex.cpp:65:12:65:12 | setB output argument [b_] | +| complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:42:18:42:18 | call to a | +| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:42:16:42:16 | a output argument [b_] | +| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:43:18:43:18 | call to b | +| complex.cpp:42:16:42:16 | a output argument [b_] | complex.cpp:43:18:43:18 | call to b | +| complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | +| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:12:53:12 | setA output argument [a_] | +| complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:40:17:40:17 | *b [b_] | +| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:12:54:12 | setB output argument [b_] | +| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | +| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:56:12:56:12 | setB output argument [a_] | +| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:12:55:12 | setA output argument [a_] | +| complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:40:17:40:17 | *b [a_] | +| complex.cpp:56:12:56:12 | setB output argument [b_] | complex.cpp:40:17:40:17 | *b [b_] | +| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:12:56:12 | setB output argument [b_] | | constructors.cpp:26:15:26:15 | *f [a_] | constructors.cpp:28:12:28:12 | call to a | | constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:28:10:28:10 | a output argument [b_] | | constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:29:12:29:12 | call to b | @@ -371,18 +371,18 @@ nodes | by_reference.cpp:136:16:136:16 | a | semmle.label | a | | complex.cpp:40:17:40:17 | *b [a_] | semmle.label | *b [a_] | | complex.cpp:40:17:40:17 | *b [b_] | semmle.label | *b [b_] | -| complex.cpp:51:16:51:16 | a output argument [b_] | semmle.label | a output argument [b_] | -| complex.cpp:51:18:51:18 | call to a | semmle.label | call to a | -| complex.cpp:52:18:52:18 | call to b | semmle.label | call to b | -| complex.cpp:62:12:62:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | -| complex.cpp:62:19:62:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:63:12:63:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | -| complex.cpp:63:19:63:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:64:12:64:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | -| complex.cpp:64:19:64:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:65:12:65:12 | setB output argument [a_] | semmle.label | setB output argument [a_] | -| complex.cpp:65:12:65:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | -| complex.cpp:65:19:65:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:42:16:42:16 | a output argument [b_] | semmle.label | a output argument [b_] | +| complex.cpp:42:18:42:18 | call to a | semmle.label | call to a | +| complex.cpp:43:18:43:18 | call to b | semmle.label | call to b | +| complex.cpp:53:12:53:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| complex.cpp:53:19:53:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:54:12:54:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| complex.cpp:54:19:54:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:55:12:55:12 | setA output argument [a_] | semmle.label | setA output argument [a_] | +| complex.cpp:55:19:55:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:56:12:56:12 | setB output argument [a_] | semmle.label | setB output argument [a_] | +| complex.cpp:56:12:56:12 | setB output argument [b_] | semmle.label | setB output argument [b_] | +| complex.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input | | constructors.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] | | constructors.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] | | constructors.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] | @@ -478,10 +478,10 @@ nodes | by_reference.cpp:132:14:132:14 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:132:14:132:14 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:136:16:136:16 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:53:19:53:28 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:56:19:56:28 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input | | constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected index a311a77a196..51b43e6be1c 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected @@ -293,28 +293,28 @@ | by_reference.cpp:136:16:136:16 | a | AST only | | complex.cpp:11:22:11:23 | a_ | AST only | | complex.cpp:12:22:12:23 | b_ | AST only | -| complex.cpp:51:8:51:8 | b | AST only | -| complex.cpp:51:10:51:14 | inner | AST only | -| complex.cpp:51:16:51:16 | f | AST only | -| complex.cpp:52:8:52:8 | b | AST only | -| complex.cpp:52:10:52:14 | inner | AST only | -| complex.cpp:52:16:52:16 | f | AST only | -| complex.cpp:62:3:62:4 | b1 | AST only | -| complex.cpp:62:6:62:10 | inner | AST only | -| complex.cpp:62:12:62:12 | f | AST only | -| complex.cpp:63:3:63:4 | b2 | AST only | -| complex.cpp:63:6:63:10 | inner | AST only | -| complex.cpp:63:12:63:12 | f | AST only | -| complex.cpp:64:3:64:4 | b3 | AST only | -| complex.cpp:64:6:64:10 | inner | AST only | -| complex.cpp:64:12:64:12 | f | AST only | -| complex.cpp:65:3:65:4 | b3 | AST only | -| complex.cpp:65:6:65:10 | inner | AST only | -| complex.cpp:65:12:65:12 | f | AST only | -| complex.cpp:68:7:68:8 | b1 | AST only | -| complex.cpp:71:7:71:8 | b2 | AST only | -| complex.cpp:74:7:74:8 | b3 | AST only | -| complex.cpp:77:7:77:8 | b4 | AST only | +| complex.cpp:42:8:42:8 | b | AST only | +| complex.cpp:42:10:42:14 | inner | AST only | +| complex.cpp:42:16:42:16 | f | AST only | +| complex.cpp:43:8:43:8 | b | AST only | +| complex.cpp:43:10:43:14 | inner | AST only | +| complex.cpp:43:16:43:16 | f | AST only | +| complex.cpp:53:3:53:4 | b1 | AST only | +| complex.cpp:53:6:53:10 | inner | AST only | +| complex.cpp:53:12:53:12 | f | AST only | +| complex.cpp:54:3:54:4 | b2 | AST only | +| complex.cpp:54:6:54:10 | inner | AST only | +| complex.cpp:54:12:54:12 | f | AST only | +| complex.cpp:55:3:55:4 | b3 | AST only | +| complex.cpp:55:6:55:10 | inner | AST only | +| complex.cpp:55:12:55:12 | f | AST only | +| complex.cpp:56:3:56:4 | b3 | AST only | +| complex.cpp:56:6:56:10 | inner | AST only | +| complex.cpp:56:12:56:12 | f | AST only | +| complex.cpp:59:7:59:8 | b1 | AST only | +| complex.cpp:62:7:62:8 | b2 | AST only | +| complex.cpp:65:7:65:8 | b3 | AST only | +| complex.cpp:68:7:68:8 | b4 | AST only | | constructors.cpp:20:24:20:25 | a_ | AST only | | constructors.cpp:21:24:21:25 | b_ | AST only | | constructors.cpp:28:10:28:10 | f | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected index 707b15b7f7d..6c58d51ff74 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected @@ -344,28 +344,28 @@ | complex.cpp:11:22:11:23 | this | | complex.cpp:12:22:12:23 | b_ | | complex.cpp:12:22:12:23 | this | -| complex.cpp:51:8:51:8 | b | -| complex.cpp:51:10:51:14 | inner | -| complex.cpp:51:16:51:16 | f | -| complex.cpp:52:8:52:8 | b | -| complex.cpp:52:10:52:14 | inner | -| complex.cpp:52:16:52:16 | f | -| complex.cpp:62:3:62:4 | b1 | -| complex.cpp:62:6:62:10 | inner | -| complex.cpp:62:12:62:12 | f | -| complex.cpp:63:3:63:4 | b2 | -| complex.cpp:63:6:63:10 | inner | -| complex.cpp:63:12:63:12 | f | -| complex.cpp:64:3:64:4 | b3 | -| complex.cpp:64:6:64:10 | inner | -| complex.cpp:64:12:64:12 | f | -| complex.cpp:65:3:65:4 | b3 | -| complex.cpp:65:6:65:10 | inner | -| complex.cpp:65:12:65:12 | f | -| complex.cpp:68:7:68:8 | b1 | -| complex.cpp:71:7:71:8 | b2 | -| complex.cpp:74:7:74:8 | b3 | -| complex.cpp:77:7:77:8 | b4 | +| complex.cpp:42:8:42:8 | b | +| complex.cpp:42:10:42:14 | inner | +| complex.cpp:42:16:42:16 | f | +| complex.cpp:43:8:43:8 | b | +| complex.cpp:43:10:43:14 | inner | +| complex.cpp:43:16:43:16 | f | +| complex.cpp:53:3:53:4 | b1 | +| complex.cpp:53:6:53:10 | inner | +| complex.cpp:53:12:53:12 | f | +| complex.cpp:54:3:54:4 | b2 | +| complex.cpp:54:6:54:10 | inner | +| complex.cpp:54:12:54:12 | f | +| complex.cpp:55:3:55:4 | b3 | +| complex.cpp:55:6:55:10 | inner | +| complex.cpp:55:12:55:12 | f | +| complex.cpp:56:3:56:4 | b3 | +| complex.cpp:56:6:56:10 | inner | +| complex.cpp:56:12:56:12 | f | +| complex.cpp:59:7:59:8 | b1 | +| complex.cpp:62:7:62:8 | b2 | +| complex.cpp:65:7:65:8 | b3 | +| complex.cpp:68:7:68:8 | b4 | | constructors.cpp:20:24:20:25 | a_ | | constructors.cpp:20:24:20:25 | this | | constructors.cpp:21:24:21:25 | b_ | diff --git a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected index bfab3380b4e..b932e0395f4 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected @@ -51,14 +51,14 @@ edges | A.cpp:160:29:160:29 | b | A.cpp:160:18:160:60 | call to MyList [head] | | A.cpp:161:18:161:40 | call to MyList [next, head] | A.cpp:162:38:162:39 | l2 [next, head] | | A.cpp:161:38:161:39 | l1 [head] | A.cpp:161:18:161:40 | call to MyList [next, head] | -| A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | A.cpp:165:10:165:11 | l3 [next, next, ... (3)] | -| A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | A.cpp:167:44:167:44 | l [next, next, ... (3)] | -| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | -| A.cpp:165:10:165:11 | l3 [next, next, ... (3)] | A.cpp:165:14:165:17 | next [next, head] | +| A.cpp:162:18:162:40 | call to MyList [next, next, head] | A.cpp:165:10:165:11 | l3 [next, next, head] | +| A.cpp:162:18:162:40 | call to MyList [next, next, head] | A.cpp:167:44:167:44 | l [next, next, head] | +| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:162:18:162:40 | call to MyList [next, next, head] | +| A.cpp:165:10:165:11 | l3 [next, next, head] | A.cpp:165:14:165:17 | next [next, head] | | A.cpp:165:14:165:17 | next [next, head] | A.cpp:165:20:165:23 | next [head] | | A.cpp:165:20:165:23 | next [head] | A.cpp:165:26:165:29 | head | | A.cpp:167:44:167:44 | l [next, head] | A.cpp:167:47:167:50 | next [head] | -| A.cpp:167:44:167:44 | l [next, next, ... (3)] | A.cpp:167:47:167:50 | next [next, head] | +| A.cpp:167:44:167:44 | l [next, next, head] | A.cpp:167:47:167:50 | next [next, head] | | A.cpp:167:47:167:50 | next [head] | A.cpp:169:12:169:12 | l [head] | | A.cpp:167:47:167:50 | next [next, head] | A.cpp:167:44:167:44 | l [next, head] | | A.cpp:169:12:169:12 | l [head] | A.cpp:169:15:169:18 | head | @@ -113,14 +113,14 @@ edges | D.cpp:51:27:51:27 | e | D.cpp:51:8:51:14 | ref arg call to getBox1 [elem] | | D.cpp:52:14:52:14 | b [box, elem] | D.cpp:21:30:21:31 | b2 [box, elem] | | D.cpp:56:15:56:24 | new | D.cpp:58:5:58:27 | ... = ... | -| D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | D.cpp:58:5:58:12 | this [post update] [boxfield, box, ... (3)] | -| D.cpp:58:5:58:12 | this [post update] [boxfield, box, ... (3)] | D.cpp:59:5:59:7 | this [boxfield, box, ... (3)] | +| D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] | +| D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] | D.cpp:59:5:59:7 | this [boxfield, box, elem] | | D.cpp:58:5:58:27 | ... = ... | D.cpp:58:15:58:17 | box [post update] [elem] | | D.cpp:58:15:58:17 | box [post update] [elem] | D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | -| D.cpp:59:5:59:7 | this [boxfield, box, ... (3)] | D.cpp:63:8:63:10 | this [boxfield, box, ... (3)] | -| D.cpp:63:8:63:10 | this [boxfield, box, ... (3)] | D.cpp:64:10:64:17 | this [boxfield, box, ... (3)] | +| D.cpp:59:5:59:7 | this [boxfield, box, elem] | D.cpp:63:8:63:10 | this [boxfield, box, elem] | +| D.cpp:63:8:63:10 | this [boxfield, box, elem] | D.cpp:64:10:64:17 | this [boxfield, box, elem] | | D.cpp:64:10:64:17 | boxfield [box, elem] | D.cpp:64:20:64:22 | box [elem] | -| D.cpp:64:10:64:17 | this [boxfield, box, ... (3)] | D.cpp:64:10:64:17 | boxfield [box, elem] | +| D.cpp:64:10:64:17 | this [boxfield, box, elem] | D.cpp:64:10:64:17 | boxfield [box, elem] | | D.cpp:64:20:64:22 | box [elem] | D.cpp:64:25:64:28 | elem | | E.cpp:19:27:19:27 | p [data, buffer] | E.cpp:21:10:21:10 | p [data, buffer] | | E.cpp:21:10:21:10 | p [data, buffer] | E.cpp:21:13:21:16 | data [buffer] | @@ -193,33 +193,33 @@ edges | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... | | arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:16:8:16:13 | access to array | | arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:17:8:17:13 | access to array | -| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, ... (3)] | arrays.cpp:37:8:37:8 | o [nested, arr, ... (3)] | -| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, ... (3)] | arrays.cpp:38:8:38:8 | o [nested, arr, ... (3)] | +| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:37:8:37:8 | o [nested, arr, data] | +| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:38:8:38:8 | o [nested, arr, data] | | arrays.cpp:36:3:36:17 | access to array [post update] [data] | arrays.cpp:36:12:36:14 | arr [inner post update] [data] | | arrays.cpp:36:3:36:37 | ... = ... | arrays.cpp:36:3:36:17 | access to array [post update] [data] | -| arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | arrays.cpp:36:3:36:3 | o [post update] [nested, arr, ... (3)] | +| arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | | arrays.cpp:36:12:36:14 | arr [inner post update] [data] | arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | | arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:36:3:36:37 | ... = ... | -| arrays.cpp:37:8:37:8 | o [nested, arr, ... (3)] | arrays.cpp:37:10:37:15 | nested [arr, data] | +| arrays.cpp:37:8:37:8 | o [nested, arr, data] | arrays.cpp:37:10:37:15 | nested [arr, data] | | arrays.cpp:37:8:37:22 | access to array [data] | arrays.cpp:37:24:37:27 | data | | arrays.cpp:37:10:37:15 | nested [arr, data] | arrays.cpp:37:17:37:19 | arr [data] | | arrays.cpp:37:17:37:19 | arr [data] | arrays.cpp:37:8:37:22 | access to array [data] | -| arrays.cpp:38:8:38:8 | o [nested, arr, ... (3)] | arrays.cpp:38:10:38:15 | nested [arr, data] | +| arrays.cpp:38:8:38:8 | o [nested, arr, data] | arrays.cpp:38:10:38:15 | nested [arr, data] | | arrays.cpp:38:8:38:22 | access to array [data] | arrays.cpp:38:24:38:27 | data | | arrays.cpp:38:10:38:15 | nested [arr, data] | arrays.cpp:38:17:38:19 | arr [data] | | arrays.cpp:38:17:38:19 | arr [data] | arrays.cpp:38:8:38:22 | access to array [data] | -| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, ... (3)] | arrays.cpp:43:8:43:8 | o [indirect, arr, ... (3)] | -| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, ... (3)] | arrays.cpp:44:8:44:8 | o [indirect, arr, ... (3)] | +| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:43:8:43:8 | o [indirect, arr, data] | +| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:44:8:44:8 | o [indirect, arr, data] | | arrays.cpp:42:3:42:20 | access to array [post update] [data] | arrays.cpp:42:15:42:17 | arr [inner post update] [data] | | arrays.cpp:42:3:42:40 | ... = ... | arrays.cpp:42:3:42:20 | access to array [post update] [data] | -| arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, ... (3)] | +| arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | | arrays.cpp:42:15:42:17 | arr [inner post update] [data] | arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | | arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:42:3:42:40 | ... = ... | -| arrays.cpp:43:8:43:8 | o [indirect, arr, ... (3)] | arrays.cpp:43:10:43:17 | indirect [arr, data] | +| arrays.cpp:43:8:43:8 | o [indirect, arr, data] | arrays.cpp:43:10:43:17 | indirect [arr, data] | | arrays.cpp:43:8:43:25 | access to array [data] | arrays.cpp:43:27:43:30 | data | | arrays.cpp:43:10:43:17 | indirect [arr, data] | arrays.cpp:43:20:43:22 | arr [data] | | arrays.cpp:43:20:43:22 | arr [data] | arrays.cpp:43:8:43:25 | access to array [data] | -| arrays.cpp:44:8:44:8 | o [indirect, arr, ... (3)] | arrays.cpp:44:10:44:17 | indirect [arr, data] | +| arrays.cpp:44:8:44:8 | o [indirect, arr, data] | arrays.cpp:44:10:44:17 | indirect [arr, data] | | arrays.cpp:44:8:44:25 | access to array [data] | arrays.cpp:44:27:44:30 | data | | arrays.cpp:44:10:44:17 | indirect [arr, data] | arrays.cpp:44:20:44:22 | arr [data] | | arrays.cpp:44:20:44:22 | arr [data] | arrays.cpp:44:8:44:25 | access to array [data] | @@ -308,33 +308,34 @@ edges | by_reference.cpp:135:8:135:13 | pouter [inner_ptr, a] | by_reference.cpp:135:16:135:24 | inner_ptr [a] | | by_reference.cpp:135:16:135:24 | inner_ptr [a] | by_reference.cpp:135:27:135:27 | a | | by_reference.cpp:136:8:136:13 | pouter [a] | by_reference.cpp:136:16:136:16 | a | -| complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | complex.cpp:51:8:51:8 | b [inner, f, ... (3)] | -| complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | complex.cpp:52:8:52:8 | b [inner, f, ... (3)] | -| complex.cpp:51:8:51:8 | b [inner, f, ... (3)] | complex.cpp:51:10:51:14 | inner [f, a_] | -| complex.cpp:51:10:51:14 | inner [f, a_] | complex.cpp:51:16:51:16 | f [a_] | -| complex.cpp:51:16:51:16 | f [a_] | complex.cpp:51:18:51:18 | call to a | -| complex.cpp:52:8:52:8 | b [inner, f, ... (3)] | complex.cpp:52:10:52:14 | inner [f, b_] | -| complex.cpp:52:10:52:14 | inner [f, b_] | complex.cpp:52:16:52:16 | f [b_] | -| complex.cpp:52:16:52:16 | f [b_] | complex.cpp:52:18:52:18 | call to b | -| complex.cpp:62:3:62:4 | b1 [post update] [inner, f, ... (3)] | complex.cpp:68:7:68:8 | b1 [inner, f, ... (3)] | -| complex.cpp:62:6:62:10 | inner [post update] [f, a_] | complex.cpp:62:3:62:4 | b1 [post update] [inner, f, ... (3)] | -| complex.cpp:62:12:62:12 | ref arg f [a_] | complex.cpp:62:6:62:10 | inner [post update] [f, a_] | -| complex.cpp:62:19:62:28 | call to user_input | complex.cpp:62:12:62:12 | ref arg f [a_] | -| complex.cpp:63:3:63:4 | b2 [post update] [inner, f, ... (3)] | complex.cpp:71:7:71:8 | b2 [inner, f, ... (3)] | -| complex.cpp:63:6:63:10 | inner [post update] [f, b_] | complex.cpp:63:3:63:4 | b2 [post update] [inner, f, ... (3)] | -| complex.cpp:63:12:63:12 | ref arg f [b_] | complex.cpp:63:6:63:10 | inner [post update] [f, b_] | -| complex.cpp:63:19:63:28 | call to user_input | complex.cpp:63:12:63:12 | ref arg f [b_] | -| complex.cpp:64:3:64:4 | b3 [post update] [inner, f, ... (3)] | complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | -| complex.cpp:64:6:64:10 | inner [post update] [f, a_] | complex.cpp:64:3:64:4 | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:64:12:64:12 | ref arg f [a_] | complex.cpp:64:6:64:10 | inner [post update] [f, a_] | -| complex.cpp:64:19:64:28 | call to user_input | complex.cpp:64:12:64:12 | ref arg f [a_] | -| complex.cpp:65:3:65:4 | b3 [post update] [inner, f, ... (3)] | complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | -| complex.cpp:65:6:65:10 | inner [post update] [f, b_] | complex.cpp:65:3:65:4 | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:65:12:65:12 | ref arg f [b_] | complex.cpp:65:6:65:10 | inner [post update] [f, b_] | -| complex.cpp:65:19:65:28 | call to user_input | complex.cpp:65:12:65:12 | ref arg f [b_] | -| complex.cpp:68:7:68:8 | b1 [inner, f, ... (3)] | complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | -| complex.cpp:71:7:71:8 | b2 [inner, f, ... (3)] | complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | -| complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | +| complex.cpp:40:17:40:17 | b [inner, f, a_] | complex.cpp:42:8:42:8 | b [inner, f, a_] | +| complex.cpp:40:17:40:17 | b [inner, f, b_] | complex.cpp:43:8:43:8 | b [inner, f, b_] | +| complex.cpp:42:8:42:8 | b [inner, f, a_] | complex.cpp:42:10:42:14 | inner [f, a_] | +| complex.cpp:42:10:42:14 | inner [f, a_] | complex.cpp:42:16:42:16 | f [a_] | +| complex.cpp:42:16:42:16 | f [a_] | complex.cpp:42:18:42:18 | call to a | +| complex.cpp:43:8:43:8 | b [inner, f, b_] | complex.cpp:43:10:43:14 | inner [f, b_] | +| complex.cpp:43:10:43:14 | inner [f, b_] | complex.cpp:43:16:43:16 | f [b_] | +| complex.cpp:43:16:43:16 | f [b_] | complex.cpp:43:18:43:18 | call to b | +| complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | complex.cpp:59:7:59:8 | b1 [inner, f, a_] | +| complex.cpp:53:6:53:10 | inner [post update] [f, a_] | complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | +| complex.cpp:53:12:53:12 | ref arg f [a_] | complex.cpp:53:6:53:10 | inner [post update] [f, a_] | +| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:12:53:12 | ref arg f [a_] | +| complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | complex.cpp:62:7:62:8 | b2 [inner, f, b_] | +| complex.cpp:54:6:54:10 | inner [post update] [f, b_] | complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | +| complex.cpp:54:12:54:12 | ref arg f [b_] | complex.cpp:54:6:54:10 | inner [post update] [f, b_] | +| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:12:54:12 | ref arg f [b_] | +| complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | complex.cpp:65:7:65:8 | b3 [inner, f, a_] | +| complex.cpp:55:6:55:10 | inner [post update] [f, a_] | complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | +| complex.cpp:55:12:55:12 | ref arg f [a_] | complex.cpp:55:6:55:10 | inner [post update] [f, a_] | +| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:12:55:12 | ref arg f [a_] | +| complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | complex.cpp:65:7:65:8 | b3 [inner, f, b_] | +| complex.cpp:56:6:56:10 | inner [post update] [f, b_] | complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | +| complex.cpp:56:12:56:12 | ref arg f [b_] | complex.cpp:56:6:56:10 | inner [post update] [f, b_] | +| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:12:56:12 | ref arg f [b_] | +| complex.cpp:59:7:59:8 | b1 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] | +| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] | | constructors.cpp:26:15:26:15 | f [a_] | constructors.cpp:28:10:28:10 | f [a_] | | constructors.cpp:26:15:26:15 | f [b_] | constructors.cpp:29:10:29:10 | f [b_] | | constructors.cpp:28:10:28:10 | f [a_] | constructors.cpp:28:12:28:12 | call to a | @@ -386,16 +387,16 @@ edges | qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:47:5:47:42 | ... = ... | | qualifiers.cpp:48:10:48:14 | outer [inner, a] | qualifiers.cpp:48:16:48:20 | inner [a] | | qualifiers.cpp:48:16:48:20 | inner [a] | qualifiers.cpp:48:23:48:23 | a | -| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, ... (4)] | realistic.cpp:61:21:61:23 | foo [bar, baz, ... (4)] | -| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, ... (3)] | realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, ... (3)] | +| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | +| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, bufferLen] | | realistic.cpp:53:9:53:66 | ... = ... | realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | -| realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, ... (3)] | realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, ... (4)] | -| realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, ... (3)] | +| realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, bufferLen] | realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | +| realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | | realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | | realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:53:9:53:66 | ... = ... | -| realistic.cpp:61:21:61:23 | foo [bar, baz, ... (4)] | realistic.cpp:61:25:61:27 | bar [baz, userInput, ... (3)] | -| realistic.cpp:61:21:61:30 | access to array [baz, userInput, ... (3)] | realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | -| realistic.cpp:61:25:61:27 | bar [baz, userInput, ... (3)] | realistic.cpp:61:21:61:30 | access to array [baz, userInput, ... (3)] | +| realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | +| realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | +| realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | | realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | realistic.cpp:61:37:61:45 | userInput [bufferLen] | | realistic.cpp:61:37:61:45 | userInput [bufferLen] | realistic.cpp:61:47:61:55 | bufferLen | | simple.cpp:26:15:26:15 | f [a_] | simple.cpp:28:10:28:10 | f [a_] | @@ -517,14 +518,14 @@ nodes | A.cpp:160:29:160:29 | b | semmle.label | b | | A.cpp:161:18:161:40 | call to MyList [next, head] | semmle.label | call to MyList [next, head] | | A.cpp:161:38:161:39 | l1 [head] | semmle.label | l1 [head] | -| A.cpp:162:18:162:40 | call to MyList [next, next, ... (3)] | semmle.label | call to MyList [next, next, ... (3)] | +| A.cpp:162:18:162:40 | call to MyList [next, next, head] | semmle.label | call to MyList [next, next, head] | | A.cpp:162:38:162:39 | l2 [next, head] | semmle.label | l2 [next, head] | -| A.cpp:165:10:165:11 | l3 [next, next, ... (3)] | semmle.label | l3 [next, next, ... (3)] | +| A.cpp:165:10:165:11 | l3 [next, next, head] | semmle.label | l3 [next, next, head] | | A.cpp:165:14:165:17 | next [next, head] | semmle.label | next [next, head] | | A.cpp:165:20:165:23 | next [head] | semmle.label | next [head] | | A.cpp:165:26:165:29 | head | semmle.label | head | | A.cpp:167:44:167:44 | l [next, head] | semmle.label | l [next, head] | -| A.cpp:167:44:167:44 | l [next, next, ... (3)] | semmle.label | l [next, next, ... (3)] | +| A.cpp:167:44:167:44 | l [next, next, head] | semmle.label | l [next, next, head] | | A.cpp:167:47:167:50 | next [head] | semmle.label | next [head] | | A.cpp:167:47:167:50 | next [next, head] | semmle.label | next [next, head] | | A.cpp:169:12:169:12 | l [head] | semmle.label | l [head] | @@ -586,13 +587,13 @@ nodes | D.cpp:52:14:52:14 | b [box, elem] | semmle.label | b [box, elem] | | D.cpp:56:15:56:24 | new | semmle.label | new | | D.cpp:58:5:58:12 | boxfield [post update] [box, elem] | semmle.label | boxfield [post update] [box, elem] | -| D.cpp:58:5:58:12 | this [post update] [boxfield, box, ... (3)] | semmle.label | this [post update] [boxfield, box, ... (3)] | +| D.cpp:58:5:58:12 | this [post update] [boxfield, box, elem] | semmle.label | this [post update] [boxfield, box, elem] | | D.cpp:58:5:58:27 | ... = ... | semmle.label | ... = ... | | D.cpp:58:15:58:17 | box [post update] [elem] | semmle.label | box [post update] [elem] | -| D.cpp:59:5:59:7 | this [boxfield, box, ... (3)] | semmle.label | this [boxfield, box, ... (3)] | -| D.cpp:63:8:63:10 | this [boxfield, box, ... (3)] | semmle.label | this [boxfield, box, ... (3)] | +| D.cpp:59:5:59:7 | this [boxfield, box, elem] | semmle.label | this [boxfield, box, elem] | +| D.cpp:63:8:63:10 | this [boxfield, box, elem] | semmle.label | this [boxfield, box, elem] | | D.cpp:64:10:64:17 | boxfield [box, elem] | semmle.label | boxfield [box, elem] | -| D.cpp:64:10:64:17 | this [boxfield, box, ... (3)] | semmle.label | this [boxfield, box, ... (3)] | +| D.cpp:64:10:64:17 | this [boxfield, box, elem] | semmle.label | this [boxfield, box, elem] | | D.cpp:64:20:64:22 | box [elem] | semmle.label | box [elem] | | D.cpp:64:25:64:28 | elem | semmle.label | elem | | E.cpp:19:27:19:27 | p [data, buffer] | semmle.label | p [data, buffer] | @@ -675,34 +676,34 @@ nodes | arrays.cpp:15:14:15:23 | call to user_input | semmle.label | call to user_input | | arrays.cpp:16:8:16:13 | access to array | semmle.label | access to array | | arrays.cpp:17:8:17:13 | access to array | semmle.label | access to array | -| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, ... (3)] | semmle.label | o [post update] [nested, arr, ... (3)] | +| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | semmle.label | o [post update] [nested, arr, data] | | arrays.cpp:36:3:36:17 | access to array [post update] [data] | semmle.label | access to array [post update] [data] | | arrays.cpp:36:3:36:37 | ... = ... | semmle.label | ... = ... | | arrays.cpp:36:5:36:10 | nested [post update] [arr, data] | semmle.label | nested [post update] [arr, data] | | arrays.cpp:36:12:36:14 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] | | arrays.cpp:36:26:36:35 | call to user_input | semmle.label | call to user_input | -| arrays.cpp:37:8:37:8 | o [nested, arr, ... (3)] | semmle.label | o [nested, arr, ... (3)] | +| arrays.cpp:37:8:37:8 | o [nested, arr, data] | semmle.label | o [nested, arr, data] | | arrays.cpp:37:8:37:22 | access to array [data] | semmle.label | access to array [data] | | arrays.cpp:37:10:37:15 | nested [arr, data] | semmle.label | nested [arr, data] | | arrays.cpp:37:17:37:19 | arr [data] | semmle.label | arr [data] | | arrays.cpp:37:24:37:27 | data | semmle.label | data | -| arrays.cpp:38:8:38:8 | o [nested, arr, ... (3)] | semmle.label | o [nested, arr, ... (3)] | +| arrays.cpp:38:8:38:8 | o [nested, arr, data] | semmle.label | o [nested, arr, data] | | arrays.cpp:38:8:38:22 | access to array [data] | semmle.label | access to array [data] | | arrays.cpp:38:10:38:15 | nested [arr, data] | semmle.label | nested [arr, data] | | arrays.cpp:38:17:38:19 | arr [data] | semmle.label | arr [data] | | arrays.cpp:38:24:38:27 | data | semmle.label | data | -| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, ... (3)] | semmle.label | o [post update] [indirect, arr, ... (3)] | +| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | semmle.label | o [post update] [indirect, arr, data] | | arrays.cpp:42:3:42:20 | access to array [post update] [data] | semmle.label | access to array [post update] [data] | | arrays.cpp:42:3:42:40 | ... = ... | semmle.label | ... = ... | | arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] | semmle.label | indirect [post update] [arr, data] | | arrays.cpp:42:15:42:17 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] | | arrays.cpp:42:29:42:38 | call to user_input | semmle.label | call to user_input | -| arrays.cpp:43:8:43:8 | o [indirect, arr, ... (3)] | semmle.label | o [indirect, arr, ... (3)] | +| arrays.cpp:43:8:43:8 | o [indirect, arr, data] | semmle.label | o [indirect, arr, data] | | arrays.cpp:43:8:43:25 | access to array [data] | semmle.label | access to array [data] | | arrays.cpp:43:10:43:17 | indirect [arr, data] | semmle.label | indirect [arr, data] | | arrays.cpp:43:20:43:22 | arr [data] | semmle.label | arr [data] | | arrays.cpp:43:27:43:30 | data | semmle.label | data | -| arrays.cpp:44:8:44:8 | o [indirect, arr, ... (3)] | semmle.label | o [indirect, arr, ... (3)] | +| arrays.cpp:44:8:44:8 | o [indirect, arr, data] | semmle.label | o [indirect, arr, data] | | arrays.cpp:44:8:44:25 | access to array [data] | semmle.label | access to array [data] | | arrays.cpp:44:10:44:17 | indirect [arr, data] | semmle.label | indirect [arr, data] | | arrays.cpp:44:20:44:22 | arr [data] | semmle.label | arr [data] | @@ -796,34 +797,36 @@ nodes | by_reference.cpp:135:27:135:27 | a | semmle.label | a | | by_reference.cpp:136:8:136:13 | pouter [a] | semmle.label | pouter [a] | | by_reference.cpp:136:16:136:16 | a | semmle.label | a | -| complex.cpp:40:17:40:17 | b [inner, f, ... (3)] | semmle.label | b [inner, f, ... (3)] | -| complex.cpp:51:8:51:8 | b [inner, f, ... (3)] | semmle.label | b [inner, f, ... (3)] | -| complex.cpp:51:10:51:14 | inner [f, a_] | semmle.label | inner [f, a_] | -| complex.cpp:51:16:51:16 | f [a_] | semmle.label | f [a_] | -| complex.cpp:51:18:51:18 | call to a | semmle.label | call to a | -| complex.cpp:52:8:52:8 | b [inner, f, ... (3)] | semmle.label | b [inner, f, ... (3)] | -| complex.cpp:52:10:52:14 | inner [f, b_] | semmle.label | inner [f, b_] | -| complex.cpp:52:16:52:16 | f [b_] | semmle.label | f [b_] | -| complex.cpp:52:18:52:18 | call to b | semmle.label | call to b | -| complex.cpp:62:3:62:4 | b1 [post update] [inner, f, ... (3)] | semmle.label | b1 [post update] [inner, f, ... (3)] | -| complex.cpp:62:6:62:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | -| complex.cpp:62:12:62:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | -| complex.cpp:62:19:62:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:63:3:63:4 | b2 [post update] [inner, f, ... (3)] | semmle.label | b2 [post update] [inner, f, ... (3)] | -| complex.cpp:63:6:63:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | -| complex.cpp:63:12:63:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | -| complex.cpp:63:19:63:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:64:3:64:4 | b3 [post update] [inner, f, ... (3)] | semmle.label | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:64:6:64:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | -| complex.cpp:64:12:64:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | -| complex.cpp:64:19:64:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:65:3:65:4 | b3 [post update] [inner, f, ... (3)] | semmle.label | b3 [post update] [inner, f, ... (3)] | -| complex.cpp:65:6:65:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | -| complex.cpp:65:12:65:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | -| complex.cpp:65:19:65:28 | call to user_input | semmle.label | call to user_input | -| complex.cpp:68:7:68:8 | b1 [inner, f, ... (3)] | semmle.label | b1 [inner, f, ... (3)] | -| complex.cpp:71:7:71:8 | b2 [inner, f, ... (3)] | semmle.label | b2 [inner, f, ... (3)] | -| complex.cpp:74:7:74:8 | b3 [inner, f, ... (3)] | semmle.label | b3 [inner, f, ... (3)] | +| complex.cpp:40:17:40:17 | b [inner, f, a_] | semmle.label | b [inner, f, a_] | +| complex.cpp:40:17:40:17 | b [inner, f, b_] | semmle.label | b [inner, f, b_] | +| complex.cpp:42:8:42:8 | b [inner, f, a_] | semmle.label | b [inner, f, a_] | +| complex.cpp:42:10:42:14 | inner [f, a_] | semmle.label | inner [f, a_] | +| complex.cpp:42:16:42:16 | f [a_] | semmle.label | f [a_] | +| complex.cpp:42:18:42:18 | call to a | semmle.label | call to a | +| complex.cpp:43:8:43:8 | b [inner, f, b_] | semmle.label | b [inner, f, b_] | +| complex.cpp:43:10:43:14 | inner [f, b_] | semmle.label | inner [f, b_] | +| complex.cpp:43:16:43:16 | f [b_] | semmle.label | f [b_] | +| complex.cpp:43:18:43:18 | call to b | semmle.label | call to b | +| complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | semmle.label | b1 [post update] [inner, f, a_] | +| complex.cpp:53:6:53:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | +| complex.cpp:53:12:53:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | +| complex.cpp:53:19:53:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | semmle.label | b2 [post update] [inner, f, b_] | +| complex.cpp:54:6:54:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | +| complex.cpp:54:12:54:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | +| complex.cpp:54:19:54:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | semmle.label | b3 [post update] [inner, f, a_] | +| complex.cpp:55:6:55:10 | inner [post update] [f, a_] | semmle.label | inner [post update] [f, a_] | +| complex.cpp:55:12:55:12 | ref arg f [a_] | semmle.label | ref arg f [a_] | +| complex.cpp:55:19:55:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | semmle.label | b3 [post update] [inner, f, b_] | +| complex.cpp:56:6:56:10 | inner [post update] [f, b_] | semmle.label | inner [post update] [f, b_] | +| complex.cpp:56:12:56:12 | ref arg f [b_] | semmle.label | ref arg f [b_] | +| complex.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input | +| complex.cpp:59:7:59:8 | b1 [inner, f, a_] | semmle.label | b1 [inner, f, a_] | +| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | semmle.label | b2 [inner, f, b_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | semmle.label | b3 [inner, f, a_] | +| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | semmle.label | b3 [inner, f, b_] | | constructors.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] | | constructors.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] | | constructors.cpp:28:10:28:10 | f [a_] | semmle.label | f [a_] | @@ -883,16 +886,16 @@ nodes | qualifiers.cpp:48:10:48:14 | outer [inner, a] | semmle.label | outer [inner, a] | | qualifiers.cpp:48:16:48:20 | inner [a] | semmle.label | inner [a] | | qualifiers.cpp:48:23:48:23 | a | semmle.label | a | -| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, ... (4)] | semmle.label | foo [post update] [bar, baz, ... (4)] | -| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, ... (3)] | semmle.label | access to array [post update] [baz, userInput, ... (3)] | +| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | semmle.label | foo [post update] [bar, baz, userInput, bufferLen] | +| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | semmle.label | access to array [post update] [baz, userInput, bufferLen] | | realistic.cpp:53:9:53:66 | ... = ... | semmle.label | ... = ... | -| realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, ... (3)] | semmle.label | bar [inner post update] [baz, userInput, ... (3)] | +| realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, bufferLen] | semmle.label | bar [inner post update] [baz, userInput, bufferLen] | | realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] | semmle.label | baz [post update] [userInput, bufferLen] | | realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | semmle.label | userInput [post update] [bufferLen] | | realistic.cpp:53:55:53:64 | call to user_input | semmle.label | call to user_input | -| realistic.cpp:61:21:61:23 | foo [bar, baz, ... (4)] | semmle.label | foo [bar, baz, ... (4)] | -| realistic.cpp:61:21:61:30 | access to array [baz, userInput, ... (3)] | semmle.label | access to array [baz, userInput, ... (3)] | -| realistic.cpp:61:25:61:27 | bar [baz, userInput, ... (3)] | semmle.label | bar [baz, userInput, ... (3)] | +| realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | semmle.label | foo [bar, baz, userInput, bufferLen] | +| realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | semmle.label | access to array [baz, userInput, bufferLen] | +| realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | semmle.label | bar [baz, userInput, bufferLen] | | realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | semmle.label | baz [userInput, bufferLen] | | realistic.cpp:61:37:61:45 | userInput [bufferLen] | semmle.label | userInput [bufferLen] | | realistic.cpp:61:47:61:55 | bufferLen | semmle.label | bufferLen | @@ -1021,14 +1024,10 @@ nodes | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:135:27:135:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:136:16:136:16 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input | -| complex.cpp:51:18:51:18 | call to a | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:51:18:51:18 | call to a | call to a flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:62:19:62:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:62:19:62:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:63:19:63:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:63:19:63:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:64:19:64:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:64:19:64:28 | call to user_input | call to user_input | -| complex.cpp:52:18:52:18 | call to b | complex.cpp:65:19:65:28 | call to user_input | complex.cpp:52:18:52:18 | call to b | call to b flows from $@ | complex.cpp:65:19:65:28 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:53:19:53:28 | call to user_input | call to user_input | +| complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input | +| complex.cpp:43:18:43:18 | call to b | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:56:19:56:28 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input | | constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input | | constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input | From ca534ccb03b3a635bb14266d482a796f84f68d25 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 21 Aug 2020 11:24:47 +0200 Subject: [PATCH 142/166] C++: Update inline expectation comments --- .../test/library-tests/dataflow/fields/complex.cpp | 4 ++-- .../test/library-tests/dataflow/fields/flow.expected | 12 ------------ .../library-tests/dataflow/fields/ir-flow.expected | 12 ------------ 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp index 7820457b011..cb55bdd2863 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp @@ -39,8 +39,8 @@ void sink(int x) void bar(Outer &b) { - sink(b.inner.f.a()); // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 - sink(b.inner.f.b()); // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 + sink(b.inner.f.a()); // $ast=53:19 $ast=55:19 $ir=53:19 $ir=55:19 + sink(b.inner.f.b()); // $ast=54:19 $ast=56:19 $ir=54:19 $ir=56:19 } void foo() diff --git a/cpp/ql/test/library-tests/dataflow/fields/flow.expected b/cpp/ql/test/library-tests/dataflow/fields/flow.expected index 0eb62024689..e69de29bb2d 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/flow.expected @@ -1,12 +0,0 @@ -| complex.cpp:42:18:42:18 | call to a | Unexpected result: ast=53:19 | -| complex.cpp:42:18:42:18 | call to a | Unexpected result: ast=55:19 | -| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ast=63:19 | -| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ast=65:19 | -| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Missing result:ast=62:19 | -| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Missing result:ast=64:19 | -| complex.cpp:43:18:43:18 | call to b | Unexpected result: ast=54:19 | -| complex.cpp:43:18:43:18 | call to b | Unexpected result: ast=56:19 | -| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ast=62:19 | -| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ast=64:19 | -| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Missing result:ast=63:19 | -| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Missing result:ast=65:19 | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected index 9e241750a0f..e69de29bb2d 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected @@ -1,12 +0,0 @@ -| complex.cpp:42:18:42:18 | call to a | Unexpected result: ir=53:19 | -| complex.cpp:42:18:42:18 | call to a | Unexpected result: ir=55:19 | -| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ir=63:19 | -| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Fixed false positive:ir=65:19 | -| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Missing result:ir=62:19 | -| complex.cpp:42:24:42:121 | // $ast=62:19 $f+:ast=63:19 $ast=64:19 $f+:ast=65:19 $ir=62:19 $f+:ir=63:19 $ir=64:19 $f+:ir=65:19 | Missing result:ir=64:19 | -| complex.cpp:43:18:43:18 | call to b | Unexpected result: ir=54:19 | -| complex.cpp:43:18:43:18 | call to b | Unexpected result: ir=56:19 | -| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ir=62:19 | -| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Fixed false positive:ir=64:19 | -| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Missing result:ir=63:19 | -| complex.cpp:43:24:43:121 | // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f+:ir=62:19 $ir=63:19 $f+:ir=64:19 $ir=65:19 | Missing result:ir=65:19 | From 4e2f7860403f270e7633433cd53df367cb75310c Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 10 Sep 2020 16:30:24 +0200 Subject: [PATCH 143/166] Dataflow: Precalculate AccessPath to avoid massive recursion. --- java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 04975b875b7..c1097538d2b 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -2098,7 +2098,7 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = TPathNodeMid( From 98f10b29b85d007c1943d0f9d16dbfac6ffde927 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 11 Sep 2020 10:54:24 +0200 Subject: [PATCH 144/166] Dataflow: Simplify SCC: remove some apa params. --- .../java/dataflow/internal/DataFlowImpl.qll | 64 ++++++------------- 1 file changed, 21 insertions(+), 43 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index c1097538d2b..dbe041b15ce 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -2101,23 +2101,19 @@ private newtype TAccessPath = TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2340,10 +2336,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2348,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2363,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2373,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2401,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2485,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2499,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2550,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2633,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2655,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) From d88c551f640dfe6e1b9a82257b8ec5ebe224ff20 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 17 Sep 2020 10:09:56 +0200 Subject: [PATCH 145/166] Dataflow: qldoc fix --- .../src/semmle/code/java/dataflow/internal/DataFlowImpl.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index dbe041b15ce..103f05d90c2 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from From b4ecfaeda3b1bb1b45f159e3a558d92aa573c445 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 17 Sep 2020 10:19:04 +0200 Subject: [PATCH 146/166] Dataflow: Remove inconsistent AccessPath.getType(). --- .../code/java/dataflow/internal/DataFlowImpl.qll | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 103f05d90c2..77e777126c7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -2138,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2174,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2195,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2212,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } From 94f110f739cae6ef1adf451b1f2c1d4618797837 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 17 Sep 2020 10:50:14 +0200 Subject: [PATCH 147/166] Sync. --- .../cpp/dataflow/internal/DataFlowImpl.qll | 85 +++++++------------ .../cpp/dataflow/internal/DataFlowImpl2.qll | 85 +++++++------------ .../cpp/dataflow/internal/DataFlowImpl3.qll | 85 +++++++------------ .../cpp/dataflow/internal/DataFlowImpl4.qll | 85 +++++++------------ .../dataflow/internal/DataFlowImplLocal.qll | 85 +++++++------------ .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 85 +++++++------------ .../ir/dataflow/internal/DataFlowImpl2.qll | 85 +++++++------------ .../ir/dataflow/internal/DataFlowImpl3.qll | 85 +++++++------------ .../ir/dataflow/internal/DataFlowImpl4.qll | 85 +++++++------------ .../csharp/dataflow/internal/DataFlowImpl.qll | 85 +++++++------------ .../dataflow/internal/DataFlowImpl2.qll | 85 +++++++------------ .../dataflow/internal/DataFlowImpl3.qll | 85 +++++++------------ .../dataflow/internal/DataFlowImpl4.qll | 85 +++++++------------ .../dataflow/internal/DataFlowImpl5.qll | 85 +++++++------------ .../java/dataflow/internal/DataFlowImpl2.qll | 85 +++++++------------ .../java/dataflow/internal/DataFlowImpl3.qll | 85 +++++++------------ .../java/dataflow/internal/DataFlowImpl4.qll | 85 +++++++------------ .../java/dataflow/internal/DataFlowImpl5.qll | 85 +++++++------------ .../dataflow/internal/DataFlowImpl.qll | 85 +++++++------------ .../dataflow/internal/DataFlowImpl2.qll | 85 +++++++------------ 20 files changed, 600 insertions(+), 1100 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 04975b875b7..77e777126c7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 04975b875b7..77e777126c7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 04975b875b7..77e777126c7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 04975b875b7..77e777126c7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 04975b875b7..77e777126c7 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 04975b875b7..77e777126c7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 04975b875b7..77e777126c7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 04975b875b7..77e777126c7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 04975b875b7..77e777126c7 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 04975b875b7..77e777126c7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 04975b875b7..77e777126c7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 04975b875b7..77e777126c7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 04975b875b7..77e777126c7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 04975b875b7..77e777126c7 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 04975b875b7..77e777126c7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 04975b875b7..77e777126c7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 04975b875b7..77e777126c7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 04975b875b7..77e777126c7 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index 04975b875b7..77e777126c7 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index 04975b875b7..77e777126c7 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -1645,7 +1645,7 @@ private class AccessPathApproxOption extends TAccessPathApproxOption { } /** - * Holds if `node` is reachable with approximate access path `ap` from a source + * Holds if `node` is reachable with approximate access path `apa` from a source * in the configuration `config`. * * The call context `cc` records whether the node is reached through an @@ -1863,7 +1863,7 @@ private predicate flowFwdIsEntered( } /** - * Holds if `node` with approximate access path `ap` is part of a path from a + * Holds if `node` with approximate access path `apa` is part of a path from a * source to a sink in the configuration `config`. * * The Boolean `toReturn` records whether the node must be returned from @@ -2098,26 +2098,22 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { pathStoreStep(_, _, tail, _, head, _) } + TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } private newtype TPathNode = - TPathNodeMid( - Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa, - Configuration config - ) { + TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { // A PathNode is introduced by a source ... flow(node, config) and config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - ap = TAccessPathNil(getNodeType(node)) and - apa = TNil(getNodeType(node)) + ap = TAccessPathNil(getNodeType(node)) or // ... or a step from an existing PathNode to another node. exists(PathNodeMid mid | - pathStep(mid, node, cc, sc, ap, apa) and + pathStep(mid, node, cc, sc, ap) and config = mid.getConfiguration() and - flow(node, _, _, apa, unbind(config)) + flow(node, _, _, ap.getApprox(), unbind(config)) ) } or TPathNodeSink(Node node, Configuration config) { @@ -2129,7 +2125,7 @@ private newtype TPathNode = or // ... or a sink that can be reached from a source exists(PathNodeMid mid | - pathStep(mid, node, _, _, _, TNil(_)) and + pathStep(mid, node, _, _, TAccessPathNil(_)) and config = unbind(mid.getConfiguration()) ) ) @@ -2142,9 +2138,6 @@ private newtype TPathNode = * tracked object. The final type indicates the type of the tracked object. */ abstract private class AccessPath extends TAccessPath { - /** Gets the type of this access path. */ - abstract DataFlowType getType(); - /** Gets the head of this access path, if any. */ abstract TypedContent getHead(); @@ -2178,7 +2171,7 @@ private class AccessPathNil extends AccessPath, TAccessPathNil { AccessPathNil() { this = TAccessPathNil(t) } - override DataFlowType getType() { result = t } + DataFlowType getType() { result = t } override TypedContent getHead() { none() } @@ -2199,8 +2192,6 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { AccessPathCons() { this = TAccessPathCons(head, tail) } - override DataFlowType getType() { result = tail.getType() } - override TypedContent getHead() { result = head } override AccessPath getTail() { result = tail } @@ -2216,14 +2207,16 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { override int length() { result = 1 + tail.length() } private string toStringImpl() { - tail = TAccessPathNil(_) and - result = head.toString() + exists(DataFlowType t | + tail = TAccessPathNil(t) and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) or result = head + ", " + tail.(AccessPathCons).toStringImpl() } override string toString() { - result = "[" + this.toStringImpl() + "]" + concat(" : " + ppReprType(this.getType())) + result = "[" + this.toStringImpl() } } @@ -2340,10 +2333,9 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { CallContext cc; SummaryCtx sc; AccessPath ap; - AccessPathApprox apa; Configuration config; - PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, apa, config) } + PathNodeMid() { this = TPathNodeMid(node, cc, sc, ap, config) } override Node getNode() { result = node } @@ -2353,13 +2345,10 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { AccessPath getAp() { result = ap } - AccessPathApprox getApa() { result = apa } - override Configuration getConfiguration() { result = config } private PathNodeMid getSuccMid() { - pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), - result.getAp(), _) and + pathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx(), result.getAp()) and result.getConfiguration() = unbind(this.getConfiguration()) } @@ -2371,7 +2360,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { exists(PathNodeMid mid, PathNodeSink sink | mid = getSuccMid() and mid.getNode() = sink.getNode() and - mid.getApa() instanceof AccessPathApproxNil and + mid.getAp() instanceof AccessPathNil and sink.getConfiguration() = unbind(mid.getConfiguration()) and result = sink ) @@ -2381,7 +2370,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid { config.isSource(node) and cc instanceof CallContextAny and sc instanceof SummaryCtxNone and - apa instanceof AccessPathApproxNil + ap instanceof AccessPathNil } } @@ -2409,33 +2398,22 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * Holds if data may flow from `mid` to `node`. The last step in or out of * a callable is recorded by `cc`. */ -private predicate pathStep( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap, AccessPathApprox apa -) { +private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { pathStepSameAp(mid, node, cc, sc) and - ap = mid.getAp() and - apa = mid.getApa() + ap = mid.getAp() or exists(DataFlowType t | pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) and - apa = TNil(t) + ap = TAccessPathNil(t) ) or - exists(TypedContent tc, AccessPathApprox apa0 | - pathStoreStep(mid, node, ap.pop(tc), apa0, tc, cc) and - // Same as `apa = ap.getApprox()`, but avoids mutual recursion - apa0 = apa.pop(tc) - ) and + exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() or exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and - sc = mid.getSummaryCtx() and - // Here the approximation cannot be created from the approximation before - // the read, so we must use `getApprox()` - apa = ap.getApprox() + sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap, apa) and + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } @@ -2504,10 +2482,9 @@ private predicate storeCand(Node node1, TypedContent tc, Node node2, Configurati pragma[nomagic] private predicate pathStoreStep( - PathNodeMid mid, Node node, AccessPath ap0, AccessPathApprox apa0, TypedContent tc, CallContext cc + PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc ) { ap0 = mid.getAp() and - apa0 = mid.getApa() and storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and cc = mid.getCallContext() } @@ -2519,7 +2496,7 @@ private predicate pathOutOfCallable0( pos = getReturnPosition(mid.getNode()) and innercc = mid.getCallContext() and innercc instanceof CallContextNoCall and - apa = mid.getApa() and + apa = mid.getAp().getApprox() and config = mid.getConfiguration() } @@ -2570,7 +2547,7 @@ private predicate pathIntoArg( cc = mid.getCallContext() and arg.argumentOf(call, i) and ap = mid.getAp() and - apa = mid.getApa() + apa = ap.getApprox() ) } @@ -2653,7 +2630,7 @@ private predicate paramFlowsThrough( sc = mid.getSummaryCtx() and config = mid.getConfiguration() and ap = mid.getAp() and - apa = mid.getApa() and + apa = ap.getApprox() and pos = sc.getParameterPos() and not kind.(ParamUpdateReturnKind).getPosition() = pos ) @@ -2675,10 +2652,8 @@ private predicate pathThroughCallable0( * The context `cc` is restored to its value prior to entering the callable. */ pragma[noinline] -private predicate pathThroughCallable( - PathNodeMid mid, Node out, CallContext cc, AccessPath ap, AccessPathApprox apa -) { - exists(DataFlowCall call, ReturnKindExt kind | +private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa | pathThroughCallable0(call, mid, kind, cc, ap, apa) and out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration())) ) From 82e56d4ebb26f255055814c92538c59029a07f46 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 21 Sep 2020 10:27:38 +0200 Subject: [PATCH 148/166] Data flow: Simplify `pathStep` and `pathIntoCallable` --- .../csharp/dataflow/internal/DataFlowImpl.qll | 130 ++++++------------ 1 file changed, 44 insertions(+), 86 deletions(-) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 77e777126c7..8bc3d75ff86 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } From 5f01fda1ef78e5f8b65fd4be94247e61879d34b6 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 21 Sep 2020 10:29:00 +0200 Subject: [PATCH 149/166] Data flow: Sync files --- .../cpp/dataflow/internal/DataFlowImpl.qll | 130 ++++++------------ .../cpp/dataflow/internal/DataFlowImpl2.qll | 130 ++++++------------ .../cpp/dataflow/internal/DataFlowImpl3.qll | 130 ++++++------------ .../cpp/dataflow/internal/DataFlowImpl4.qll | 130 ++++++------------ .../dataflow/internal/DataFlowImplLocal.qll | 130 ++++++------------ .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 130 ++++++------------ .../ir/dataflow/internal/DataFlowImpl2.qll | 130 ++++++------------ .../ir/dataflow/internal/DataFlowImpl3.qll | 130 ++++++------------ .../ir/dataflow/internal/DataFlowImpl4.qll | 130 ++++++------------ .../dataflow/internal/DataFlowImpl2.qll | 130 ++++++------------ .../dataflow/internal/DataFlowImpl3.qll | 130 ++++++------------ .../dataflow/internal/DataFlowImpl4.qll | 130 ++++++------------ .../dataflow/internal/DataFlowImpl5.qll | 130 ++++++------------ .../java/dataflow/internal/DataFlowImpl.qll | 130 ++++++------------ .../java/dataflow/internal/DataFlowImpl2.qll | 130 ++++++------------ .../java/dataflow/internal/DataFlowImpl3.qll | 130 ++++++------------ .../java/dataflow/internal/DataFlowImpl4.qll | 130 ++++++------------ .../java/dataflow/internal/DataFlowImpl5.qll | 130 ++++++------------ .../dataflow/internal/DataFlowImpl.qll | 130 ++++++------------ .../dataflow/internal/DataFlowImpl2.qll | 130 ++++++------------ 20 files changed, 880 insertions(+), 1720 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 77e777126c7..8bc3d75ff86 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 77e777126c7..8bc3d75ff86 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 77e777126c7..8bc3d75ff86 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 77e777126c7..8bc3d75ff86 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 77e777126c7..8bc3d75ff86 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 77e777126c7..8bc3d75ff86 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 77e777126c7..8bc3d75ff86 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 77e777126c7..8bc3d75ff86 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 77e777126c7..8bc3d75ff86 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 77e777126c7..8bc3d75ff86 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 77e777126c7..8bc3d75ff86 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 77e777126c7..8bc3d75ff86 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 77e777126c7..8bc3d75ff86 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 77e777126c7..8bc3d75ff86 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 77e777126c7..8bc3d75ff86 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 77e777126c7..8bc3d75ff86 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 77e777126c7..8bc3d75ff86 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 77e777126c7..8bc3d75ff86 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index 77e777126c7..8bc3d75ff86 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index 77e777126c7..8bc3d75ff86 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -2215,9 +2215,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = head + ", " + tail.(AccessPathCons).toStringImpl() } - override string toString() { - result = "[" + this.toStringImpl() - } + override string toString() { result = "[" + this.toStringImpl() } } /** @@ -2399,13 +2397,31 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink { * a callable is recorded by `cc`. */ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, AccessPath ap) { - pathStepSameAp(mid, node, cc, sc) and + exists(AccessPath ap0, Node midnode, Configuration conf, LocalCallContext localCC | + midnode = mid.getNode() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and + ap0 = mid.getAp() + | + localFlowBigStep(midnode, node, true, _, conf, localCC) and + ap = ap0 + or + localFlowBigStep(midnode, node, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and ap = mid.getAp() or - exists(DataFlowType t | - pathStepEmptyAp(mid, node, cc, sc, t) and - ap = TAccessPathNil(t) - ) + additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(getNodeType(node)) or exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and sc = mid.getSummaryCtx() @@ -2413,50 +2429,11 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and sc = mid.getSummaryCtx() or - pathThroughCallable(mid, node, cc, ap) and - sc = mid.getSummaryCtx() -} - -pragma[noinline] -private predicate pathStepEmptyAp( - PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc, DataFlowType t -) { - exists(Node midnode, Configuration conf, LocalCallContext localCC, AccessPathFront apf | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - mid.getAp() = TAccessPathNil(_) and - localFlowBigStep(midnode, node, false, apf, conf, localCC) and - apf.getType() = t - ) + pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp() or - additionalJumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone and - mid.getAp() = TAccessPathNil(_) and - t = getNodeType(node) -} - -pragma[noinline] -private predicate pathStepSameAp(PathNodeMid mid, Node node, CallContext cc, SummaryCtx sc) { - exists(Node midnode, Configuration conf, LocalCallContext localCC | - midnode = mid.getNode() and - conf = mid.getConfiguration() and - cc = mid.getCallContext() and - sc = mid.getSummaryCtx() and - localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and - localFlowBigStep(midnode, node, true, _, conf, localCC) - ) + pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone or - jumpStep(mid.getNode(), node, mid.getConfiguration()) and - cc instanceof CallContextAny and - sc instanceof SummaryCtxNone - or - pathIntoCallable(mid, node, _, cc, sc, _) - or - pathOutOfCallable(mid, node, cc) and sc instanceof SummaryCtxNone + pathThroughCallable(mid, node, cc, ap) and sc = mid.getSummaryCtx() } pragma[nomagic] @@ -2564,35 +2541,12 @@ private predicate parameterCand( pragma[nomagic] private predicate pathIntoCallable0( PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call, - AccessPath ap, AccessPathApprox apa -) { - pathIntoArg(mid, i, outercc, call, ap, apa) and - callable = resolveCall(call, outercc) and - parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) -} - -pragma[nomagic] -private predicate pathIntoCallable1( - PathNodeMid mid, ParameterNode p, AccessPath ap, AccessPathApprox apa, CallContext outercc, - CallContextCall innercc, DataFlowCall call -) { - exists(int i, DataFlowCallable callable | - pathIntoCallable0(mid, callable, i, outercc, call, ap, apa) and - p.isParameterOf(callable, i) and - if recordDataFlowCallSite(call, callable) - then innercc = TSpecificCall(call) - else innercc = TSomeCall() - ) -} - -pragma[nomagic] -private predicate pathIntoCallable1MayFlowThrough( - PathNodeMid mid, ParameterNode p, AccessPath ap, CallContext outercc, CallContextCall innercc, - DataFlowCall call + AccessPath ap ) { exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, ap, apa, outercc, innercc, call) and - parameterMayFlowThrough(p, apa) + pathIntoArg(mid, i, outercc, call, ap, apa) and + callable = resolveCall(call, outercc) and + parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration()) ) } @@ -2605,15 +2559,19 @@ private predicate pathIntoCallable( PathNodeMid mid, ParameterNode p, CallContext outercc, CallContextCall innercc, SummaryCtx sc, DataFlowCall call ) { - exists(AccessPath ap | - pathIntoCallable1MayFlowThrough(mid, p, ap, outercc, innercc, call) and - sc = TSummaryCtxSome(p, ap) - ) - or - exists(AccessPathApprox apa | - pathIntoCallable1(mid, p, _, apa, outercc, innercc, call) and - not parameterMayFlowThrough(p, apa) and - sc = TSummaryCtxNone() + exists(int i, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, i, outercc, call, ap) and + p.isParameterOf(callable, i) and + ( + sc = TSummaryCtxSome(p, ap) + or + not exists(TSummaryCtxSome(p, ap)) and + sc = TSummaryCtxNone() + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() ) } From 27fc610c0d99dc76e186efc5b9042f7875392021 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Mon, 21 Sep 2020 11:26:59 +0200 Subject: [PATCH 150/166] Python: Update expected test output --- .../dataflow/coverage/dataflow.expected | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected index d30d71e7028..dc085955408 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow.expected @@ -128,14 +128,14 @@ edges | test.py:184:10:184:10 | ControlFlowNode for x [List element] | test.py:184:10:184:13 | ControlFlowNode for Subscript | | test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | test.py:189:10:189:10 | ControlFlowNode for x [List element] | | test.py:188:10:188:10 | ControlFlowNode for y | test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | -| test.py:188:16:188:16 | SSA variable v [List element, List element, ... (3)] | test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, ... (3)] | -| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, ... (4)] | test.py:188:16:188:16 | SSA variable v [List element, List element, ... (3)] | -| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, ... (3)] | test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, ... (4)] | -| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, ... (3)] | +| test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | +| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | +| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | +| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | | test.py:188:24:188:31 | ControlFlowNode for List [List element] | test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | | test.py:188:25:188:30 | ControlFlowNode for SOURCE | test.py:188:24:188:31 | ControlFlowNode for List [List element] | | test.py:188:40:188:40 | SSA variable u [List element, List element] | test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | -| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, ... (3)] | test.py:188:40:188:40 | SSA variable u [List element, List element] | +| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | test.py:188:40:188:40 | SSA variable u [List element, List element] | | test.py:188:51:188:51 | SSA variable z [List element] | test.py:188:67:188:67 | ControlFlowNode for z [List element] | | test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | test.py:188:51:188:51 | SSA variable z [List element] | | test.py:188:62:188:62 | SSA variable y | test.py:188:10:188:10 | ControlFlowNode for y | @@ -285,14 +285,14 @@ nodes | test.py:184:10:184:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | | test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | | test.py:188:10:188:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:188:16:188:16 | SSA variable v [List element, List element, ... (3)] | semmle.label | SSA variable v [List element, List element, ... (3)] | -| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, ... (4)] | semmle.label | ControlFlowNode for List [List element, List element, ... (4)] | -| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, ... (3)] | semmle.label | ControlFlowNode for List [List element, List element, ... (3)] | +| test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | semmle.label | SSA variable v [List element, List element, List element] | +| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element, List element] | +| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element] | | test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | | test.py:188:24:188:31 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | | test.py:188:25:188:30 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | | test.py:188:40:188:40 | SSA variable u [List element, List element] | semmle.label | SSA variable u [List element, List element] | -| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, ... (3)] | semmle.label | ControlFlowNode for v [List element, List element, ... (3)] | +| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | semmle.label | ControlFlowNode for v [List element, List element, List element] | | test.py:188:51:188:51 | SSA variable z [List element] | semmle.label | SSA variable z [List element] | | test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | semmle.label | ControlFlowNode for u [List element, List element] | | test.py:188:62:188:62 | SSA variable y | semmle.label | SSA variable y | From b0f0f89dbc08fdf1849c490184e3d9e9012c0331 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 8 Oct 2020 15:16:41 +0200 Subject: [PATCH 151/166] Dataflow: Minor pruning improvements. --- .../code/java/dataflow/internal/DataFlowImpl.qll | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 8bc3d75ff86..7a58159949b 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -2045,15 +2046,17 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) @@ -2061,7 +2064,7 @@ private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through From 8f055f56b87289799903aea26cdc6ea3ce13c708 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 8 Oct 2020 15:34:14 +0200 Subject: [PATCH 152/166] Dataflow: Adaptive field flow precision. --- .../java/dataflow/internal/DataFlowImpl.qll | 287 +++++++++++++++++- .../dataflow/internal/DataFlowImplCommon.qll | 24 ++ 2 files changed, 305 insertions(+), 6 deletions(-) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 7a58159949b..3fa78ec28ed 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -1524,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1623,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2062,6 +2144,14 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } @@ -2099,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2205,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll index 892250f44bb..efde95bd16a 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @@ -2,6 +2,30 @@ private import DataFlowImplSpecific::Private private import DataFlowImplSpecific::Public import Cached +/** + * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision during pruning. + */ +predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { + apLimit = 10 and + tupleLimit = 10000 +} + +/** + * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision. + */ +predicate accessPathCostLimits(int apLimit, int tupleLimit) { + apLimit = 5 and + tupleLimit = 1000 +} + cached private module Cached { /** From 6aae51fa4f2d15aa49ac08f0d71817deb9ed181b Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 8 Oct 2020 16:10:24 +0200 Subject: [PATCH 153/166] Dataflow: Sync. --- .../cpp/dataflow/internal/DataFlowImpl.qll | 300 +++++++++++++++++- .../cpp/dataflow/internal/DataFlowImpl2.qll | 300 +++++++++++++++++- .../cpp/dataflow/internal/DataFlowImpl3.qll | 300 +++++++++++++++++- .../cpp/dataflow/internal/DataFlowImpl4.qll | 300 +++++++++++++++++- .../dataflow/internal/DataFlowImplCommon.qll | 24 ++ .../dataflow/internal/DataFlowImplLocal.qll | 300 +++++++++++++++++- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 300 +++++++++++++++++- .../ir/dataflow/internal/DataFlowImpl2.qll | 300 +++++++++++++++++- .../ir/dataflow/internal/DataFlowImpl3.qll | 300 +++++++++++++++++- .../ir/dataflow/internal/DataFlowImpl4.qll | 300 +++++++++++++++++- .../dataflow/internal/DataFlowImplCommon.qll | 24 ++ .../csharp/dataflow/internal/DataFlowImpl.qll | 300 +++++++++++++++++- .../dataflow/internal/DataFlowImpl2.qll | 300 +++++++++++++++++- .../dataflow/internal/DataFlowImpl3.qll | 300 +++++++++++++++++- .../dataflow/internal/DataFlowImpl4.qll | 300 +++++++++++++++++- .../dataflow/internal/DataFlowImpl5.qll | 300 +++++++++++++++++- .../dataflow/internal/DataFlowImplCommon.qll | 24 ++ .../java/dataflow/internal/DataFlowImpl2.qll | 300 +++++++++++++++++- .../java/dataflow/internal/DataFlowImpl3.qll | 300 +++++++++++++++++- .../java/dataflow/internal/DataFlowImpl4.qll | 300 +++++++++++++++++- .../java/dataflow/internal/DataFlowImpl5.qll | 300 +++++++++++++++++- .../dataflow/internal/DataFlowImpl.qll | 300 +++++++++++++++++- .../dataflow/internal/DataFlowImpl2.qll | 300 +++++++++++++++++- .../dataflow/internal/DataFlowImplCommon.qll | 24 ++ 24 files changed, 5876 insertions(+), 220 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll index 892250f44bb..efde95bd16a 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll @@ -2,6 +2,30 @@ private import DataFlowImplSpecific::Private private import DataFlowImplSpecific::Public import Cached +/** + * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision during pruning. + */ +predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { + apLimit = 10 and + tupleLimit = 10000 +} + +/** + * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision. + */ +predicate accessPathCostLimits(int apLimit, int tupleLimit) { + apLimit = 5 and + tupleLimit = 1000 +} + cached private module Cached { /** diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll index 892250f44bb..efde95bd16a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -2,6 +2,30 @@ private import DataFlowImplSpecific::Private private import DataFlowImplSpecific::Public import Cached +/** + * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision during pruning. + */ +predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { + apLimit = 10 and + tupleLimit = 10000 +} + +/** + * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision. + */ +predicate accessPathCostLimits(int apLimit, int tupleLimit) { + apLimit = 5 and + tupleLimit = 1000 +} + cached private module Cached { /** diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll index 892250f44bb..efde95bd16a 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll @@ -2,6 +2,30 @@ private import DataFlowImplSpecific::Private private import DataFlowImplSpecific::Public import Cached +/** + * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision during pruning. + */ +predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { + apLimit = 10 and + tupleLimit = 10000 +} + +/** + * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision. + */ +predicate accessPathCostLimits(int apLimit, int tupleLimit) { + apLimit = 5 and + tupleLimit = 1000 +} + cached private module Cached { /** diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index 8bc3d75ff86..3fa78ec28ed 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -535,6 +535,7 @@ private predicate nodeCand1(Node node, Configuration config) { nodeCand1(node, _ private predicate throughFlowNodeCand1(Node node, Configuration config) { nodeCand1(node, true, config) and + nodeCandFwd1(node, true, config) and not fullBarrier(node, config) and not inBarrier(node, config) and not outBarrier(node, config) @@ -1523,11 +1524,50 @@ private predicate flowCandIsReturned( ) } +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx(Node node, AccessPathFront argApf, Configuration config) { + exists(AccessPathFront apf | + flowCand(node, true, _, apf, config) and + flowCandFwd(node, true, TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | flowCandConsCand(tc, apf, config)) and + nodes = + strictcount(Node n | + flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) + or + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes + ) +} + private newtype TAccessPathApprox = TNil(DataFlowType t) or - TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or + TConsNil(TypedContent tc, DataFlowType t) { + flowCandConsCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or TConsCons(TypedContent tc1, TypedContent tc2, int len) { - flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()] + flowCandConsCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) } /** @@ -1622,6 +1662,49 @@ private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { or len = 2 and result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | flowCandConsCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + flowCandConsCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) ) } } @@ -2045,23 +2128,33 @@ private predicate flow(Node n, Configuration config) { flow(n, _, _, _, config) pragma[noinline] private predicate parameterFlow( - ParameterNode p, AccessPathApprox apa, DataFlowCallable c, Configuration config + ParameterNode p, AccessPathApprox apa, AccessPathApprox apa0, DataFlowCallable c, + Configuration config ) { - flow(p, true, _, apa, config) and + flow(p, true, TAccessPathApproxSome(apa0), apa, config) and c = p.getEnclosingCallable() } -private predicate parameterMayFlowThrough(ParameterNode p, AccessPathApprox apa) { +private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, AccessPathApprox apa) { exists(ReturnNodeExt ret, Configuration config, AccessPathApprox apa0 | - parameterFlow(p, apa, ret.getEnclosingCallable(), config) and + parameterFlow(p, apa, apa0, c, config) and + c = ret.getEnclosingCallable() and flow(ret, true, TAccessPathApproxSome(_), apa0, config) and flowFwd(ret, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) ) } +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { + exists(DataFlowCallable c, AccessPathApprox apa0 | + parameterMayFlowThrough(_, c, apa) and + flow(n, true, _, apa0, _) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + ) +} + private newtype TSummaryCtx = TSummaryCtxNone() or - TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) } + TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, _, ap.getApprox()) } /** * A context for generating flow summaries. This represents flow entry through @@ -2096,9 +2189,118 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { } } +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + flowConsCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, config) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + flowConsCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = + strictcount(Node n | + flow(n, _, _, apa, _) + or + nodeMayUseSummary(n, apa) + ) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + private newtype TAccessPath = TAccessPathNil(DataFlowType t) or - TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) } + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } private newtype TPathNode = TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) { @@ -2202,20 +2404,96 @@ private class AccessPathCons extends AccessPath, TAccessPathCons { result = TConsNil(head, tail.(AccessPathNil).getType()) or result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) } override int length() { result = 1 + tail.length() } - private string toStringImpl() { + private string toStringImpl(boolean needsSuffix) { exists(DataFlowType t | tail = TAccessPathNil(t) and + needsSuffix = false and result = head.toString() + "]" + concat(" : " + ppReprType(t)) ) or - result = head + ", " + tail.(AccessPathCons).toStringImpl() + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) } - override string toString() { result = "[" + this.toStringImpl() } + override string toString() { + result = "[" + this.toStringImpl(true) + length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + flowConsCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + flowConsCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } } /** diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll index 892250f44bb..efde95bd16a 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll @@ -2,6 +2,30 @@ private import DataFlowImplSpecific::Private private import DataFlowImplSpecific::Public import Cached +/** + * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision during pruning. + */ +predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { + apLimit = 10 and + tupleLimit = 10000 +} + +/** + * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision. + */ +predicate accessPathCostLimits(int apLimit, int tupleLimit) { + apLimit = 5 and + tupleLimit = 1000 +} + cached private module Cached { /** From 1501a40de8cbbf44d516911f4d8b89e14fec33d5 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 13 Oct 2020 10:17:32 +0200 Subject: [PATCH 154/166] Dataflow: Count callables instead of nodes for fieldFlowBranchLimit. --- .../code/cpp/dataflow/internal/DataFlowImpl.qll | 16 ++++++++++++---- .../code/cpp/dataflow/internal/DataFlowImpl2.qll | 16 ++++++++++++---- .../code/cpp/dataflow/internal/DataFlowImpl3.qll | 16 ++++++++++++---- .../code/cpp/dataflow/internal/DataFlowImpl4.qll | 16 ++++++++++++---- .../cpp/dataflow/internal/DataFlowImplLocal.qll | 16 ++++++++++++---- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 16 ++++++++++++---- .../cpp/ir/dataflow/internal/DataFlowImpl2.qll | 16 ++++++++++++---- .../cpp/ir/dataflow/internal/DataFlowImpl3.qll | 16 ++++++++++++---- .../cpp/ir/dataflow/internal/DataFlowImpl4.qll | 16 ++++++++++++---- .../csharp/dataflow/internal/DataFlowImpl.qll | 16 ++++++++++++---- .../csharp/dataflow/internal/DataFlowImpl2.qll | 16 ++++++++++++---- .../csharp/dataflow/internal/DataFlowImpl3.qll | 16 ++++++++++++---- .../csharp/dataflow/internal/DataFlowImpl4.qll | 16 ++++++++++++---- .../csharp/dataflow/internal/DataFlowImpl5.qll | 16 ++++++++++++---- .../code/java/dataflow/internal/DataFlowImpl.qll | 16 ++++++++++++---- .../java/dataflow/internal/DataFlowImpl2.qll | 16 ++++++++++++---- .../java/dataflow/internal/DataFlowImpl3.qll | 16 ++++++++++++---- .../java/dataflow/internal/DataFlowImpl4.qll | 16 ++++++++++++---- .../java/dataflow/internal/DataFlowImpl5.qll | 16 ++++++++++++---- .../dataflow/internal/DataFlowImpl.qll | 16 ++++++++++++---- .../dataflow/internal/DataFlowImpl2.qll | 16 ++++++++++++---- 21 files changed, 252 insertions(+), 84 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 3fa78ec28ed..189bbeee096 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 3fa78ec28ed..189bbeee096 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 3fa78ec28ed..189bbeee096 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 3fa78ec28ed..189bbeee096 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 3fa78ec28ed..189bbeee096 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 3fa78ec28ed..189bbeee096 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 3fa78ec28ed..189bbeee096 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 3fa78ec28ed..189bbeee096 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 3fa78ec28ed..189bbeee096 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 3fa78ec28ed..189bbeee096 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 3fa78ec28ed..189bbeee096 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 3fa78ec28ed..189bbeee096 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 3fa78ec28ed..189bbeee096 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 3fa78ec28ed..189bbeee096 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 3fa78ec28ed..189bbeee096 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 3fa78ec28ed..189bbeee096 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 3fa78ec28ed..189bbeee096 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 3fa78ec28ed..189bbeee096 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 3fa78ec28ed..189bbeee096 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index 3fa78ec28ed..189bbeee096 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index 3fa78ec28ed..189bbeee096 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -649,8 +649,12 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + | + callable = n.getEnclosingCallable() + ) ) } @@ -661,8 +665,12 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + strictcount(DataFlowCallable callable | + exists(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + | + callable = n.getEnclosingCallable() + ) ) } From 664f04020f3a05b18f4cd7e04bcdaf3983a22498 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 16 Oct 2020 12:51:50 +0200 Subject: [PATCH 155/166] Revert "Dataflow: Count callables instead of nodes for fieldFlowBranchLimit." This reverts commit 1501a40de8cbbf44d516911f4d8b89e14fec33d5. --- .../code/cpp/dataflow/internal/DataFlowImpl.qll | 16 ++++------------ .../code/cpp/dataflow/internal/DataFlowImpl2.qll | 16 ++++------------ .../code/cpp/dataflow/internal/DataFlowImpl3.qll | 16 ++++------------ .../code/cpp/dataflow/internal/DataFlowImpl4.qll | 16 ++++------------ .../cpp/dataflow/internal/DataFlowImplLocal.qll | 16 ++++------------ .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 16 ++++------------ .../cpp/ir/dataflow/internal/DataFlowImpl2.qll | 16 ++++------------ .../cpp/ir/dataflow/internal/DataFlowImpl3.qll | 16 ++++------------ .../cpp/ir/dataflow/internal/DataFlowImpl4.qll | 16 ++++------------ .../csharp/dataflow/internal/DataFlowImpl.qll | 16 ++++------------ .../csharp/dataflow/internal/DataFlowImpl2.qll | 16 ++++------------ .../csharp/dataflow/internal/DataFlowImpl3.qll | 16 ++++------------ .../csharp/dataflow/internal/DataFlowImpl4.qll | 16 ++++------------ .../csharp/dataflow/internal/DataFlowImpl5.qll | 16 ++++------------ .../code/java/dataflow/internal/DataFlowImpl.qll | 16 ++++------------ .../java/dataflow/internal/DataFlowImpl2.qll | 16 ++++------------ .../java/dataflow/internal/DataFlowImpl3.qll | 16 ++++------------ .../java/dataflow/internal/DataFlowImpl4.qll | 16 ++++------------ .../java/dataflow/internal/DataFlowImpl5.qll | 16 ++++------------ .../dataflow/internal/DataFlowImpl.qll | 16 ++++------------ .../dataflow/internal/DataFlowImpl2.qll | 16 ++++------------ 21 files changed, 84 insertions(+), 252 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 189bbeee096..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 189bbeee096..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 189bbeee096..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 189bbeee096..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 189bbeee096..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 189bbeee096..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 189bbeee096..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 189bbeee096..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 189bbeee096..3fa78ec28ed 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 189bbeee096..3fa78ec28ed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 189bbeee096..3fa78ec28ed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 189bbeee096..3fa78ec28ed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 189bbeee096..3fa78ec28ed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 189bbeee096..3fa78ec28ed 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 189bbeee096..3fa78ec28ed 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 189bbeee096..3fa78ec28ed 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 189bbeee096..3fa78ec28ed 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 189bbeee096..3fa78ec28ed 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 189bbeee096..3fa78ec28ed 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index 189bbeee096..3fa78ec28ed 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index 189bbeee096..3fa78ec28ed 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -649,12 +649,8 @@ private predicate flowIntoCallNodeCand1( */ private int branch(Node n1, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) ) } @@ -665,12 +661,8 @@ private int branch(Node n1, Configuration conf) { */ private int join(Node n2, Configuration conf) { result = - strictcount(DataFlowCallable callable | - exists(Node n | - flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) - | - callable = n.getEnclosingCallable() - ) + strictcount(Node n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) ) } From b352605d126c04153ea49315de95cb3be38501dc Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 16 Oct 2020 13:45:51 +0200 Subject: [PATCH 156/166] Dataflow: Code review fixes. --- .../cpp/dataflow/internal/DataFlowImpl.qll | 27 ++++++++----------- .../cpp/dataflow/internal/DataFlowImpl2.qll | 27 ++++++++----------- .../cpp/dataflow/internal/DataFlowImpl3.qll | 27 ++++++++----------- .../cpp/dataflow/internal/DataFlowImpl4.qll | 27 ++++++++----------- .../dataflow/internal/DataFlowImplLocal.qll | 27 ++++++++----------- .../cpp/ir/dataflow/internal/DataFlowImpl.qll | 27 ++++++++----------- .../ir/dataflow/internal/DataFlowImpl2.qll | 27 ++++++++----------- .../ir/dataflow/internal/DataFlowImpl3.qll | 27 ++++++++----------- .../ir/dataflow/internal/DataFlowImpl4.qll | 27 ++++++++----------- .../csharp/dataflow/internal/DataFlowImpl.qll | 27 ++++++++----------- .../dataflow/internal/DataFlowImpl2.qll | 27 ++++++++----------- .../dataflow/internal/DataFlowImpl3.qll | 27 ++++++++----------- .../dataflow/internal/DataFlowImpl4.qll | 27 ++++++++----------- .../dataflow/internal/DataFlowImpl5.qll | 27 ++++++++----------- .../java/dataflow/internal/DataFlowImpl.qll | 27 ++++++++----------- .../java/dataflow/internal/DataFlowImpl2.qll | 27 ++++++++----------- .../java/dataflow/internal/DataFlowImpl3.qll | 27 ++++++++----------- .../java/dataflow/internal/DataFlowImpl4.qll | 27 ++++++++----------- .../java/dataflow/internal/DataFlowImpl5.qll | 27 ++++++++----------- .../dataflow/internal/DataFlowImpl.qll | 27 ++++++++----------- .../dataflow/internal/DataFlowImpl2.qll | 27 ++++++++----------- 21 files changed, 231 insertions(+), 336 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) diff --git a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll index 3fa78ec28ed..a79a2419ba8 100644 --- a/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll +++ b/python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll @@ -1546,7 +1546,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) strictcount(Node n | flowCand(n, _, _, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) or - flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), _) + flowCandSummaryCtx(n, any(AccessPathFrontHead apf | apf.headUsesContent(tc)), config) ) and accessPathApproxCostLimits(apLimit, tupleLimit) and apLimit < tails and @@ -2144,11 +2144,12 @@ private predicate parameterMayFlowThrough(ParameterNode p, DataFlowCallable c, A ) } -private predicate nodeMayUseSummary(Node n, AccessPathApprox apa) { +private predicate nodeMayUseSummary(Node n, AccessPathApprox apa, Configuration config) { exists(DataFlowCallable c, AccessPathApprox apa0 | parameterMayFlowThrough(_, c, apa) and - flow(n, true, _, apa0, _) and - flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, _) + flow(n, true, _, apa0, config) and + flowFwd(n, any(CallContextCall ccc), TAccessPathApproxSome(apa), _, apa0, config) and + n.getEnclosingCallable() = c ) } @@ -2204,6 +2205,10 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { ) } +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = strictcount(Node n | flow(n, _, _, apa, config) or nodeMayUseSummary(n, apa, config)) +} + /** * Holds if a length 2 access path approximation matching `apa` is expected * to be expensive. @@ -2211,12 +2216,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = count1to2unfold(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, config) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and apLimit < aps and tupleLimit < (aps - 1) * nodes @@ -2237,12 +2237,7 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { exists(int aps, int nodes, int apLimit, int tupleLimit | aps = countPotentialAps(apa, config) and - nodes = - strictcount(Node n | - flow(n, _, _, apa, _) - or - nodeMayUseSummary(n, apa) - ) and + nodes = countNodesUsingAccessPath(apa, config) and accessPathCostLimits(apLimit, tupleLimit) and if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true ) From 5a480bfb1350cf9931d74bff877d93a4eff313c2 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Fri, 16 Oct 2020 16:17:10 +0100 Subject: [PATCH 157/166] Give query an id and PathGraph query predicates --- .../experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql index 74e6c060c98..23a6e0a7003 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-749/UnsafeAndroidAccess.ql @@ -1,5 +1,6 @@ /** * @name Unsafe resource fetching in Android webview + * @id java/android/unsafe-android-webview-fetch * @description JavaScript rendered inside WebViews can access any protected application file and web resource from any origin * @kind path-problem * @tags security @@ -11,6 +12,7 @@ import java import semmle.code.java.frameworks.android.Intent import semmle.code.java.frameworks.android.WebView import semmle.code.java.dataflow.FlowSources +import DataFlow::PathGraph /** * Methods allowing any-local-file and cross-origin access in the WebSettings class From 6a6eadcf50ee7cc71637fd986b8869fa38704d30 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 16 Oct 2020 11:53:27 -0400 Subject: [PATCH 158/166] C++: Print static call target for `Call` instruction in dumps --- .../aliased_ssa/Instruction.qll | 6 + .../cpp/ir/implementation/raw/Instruction.qll | 6 + .../unaliased_ssa/Instruction.qll | 6 + .../test/library-tests/ir/ir/raw_ir.expected | 316 +++++++++--------- .../ir/ssa/aliased_ssa_ir.expected | 58 ++-- .../ir/ssa/aliased_ssa_ir_unsound.expected | 58 ++-- .../ir/ssa/unaliased_ssa_ir.expected | 58 ++-- .../ir/ssa/unaliased_ssa_ir_unsound.expected | 58 ++-- .../GlobalValueNumbering/ir_gvn.expected | 6 +- .../ir/implementation/raw/Instruction.qll | 6 + .../unaliased_ssa/Instruction.qll | 6 + .../test/experimental/ir/ir/raw_ir.expected | 136 ++++---- 12 files changed, 375 insertions(+), 345 deletions(-) diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 620b23b942e..135b91c0dec 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -1501,6 +1501,12 @@ class SwitchInstruction extends Instruction { class CallInstruction extends Instruction { CallInstruction() { getOpcode() instanceof Opcode::Call } + final override string getImmediateString() { + result = getStaticCallTarget().toString() + or + not exists(getStaticCallTarget()) and result = "?" + } + /** * Gets the operand the specifies the target function of the call. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 620b23b942e..135b91c0dec 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -1501,6 +1501,12 @@ class SwitchInstruction extends Instruction { class CallInstruction extends Instruction { CallInstruction() { getOpcode() instanceof Opcode::Call } + final override string getImmediateString() { + result = getStaticCallTarget().toString() + or + not exists(getStaticCallTarget()) and result = "?" + } + /** * Gets the operand the specifies the target function of the call. */ diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 620b23b942e..135b91c0dec 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -1501,6 +1501,12 @@ class SwitchInstruction extends Instruction { class CallInstruction extends Instruction { CallInstruction() { getOpcode() instanceof Opcode::Call } + final override string getImmediateString() { + result = getStaticCallTarget().toString() + or + not exists(getStaticCallTarget()) and result = "?" + } + /** * Gets the operand the specifies the target function of the call. */ diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index a2be838ae48..1e2970cf2c4 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -40,7 +40,7 @@ bad_asts.cpp: # 16| r16_1(glval) = VariableAddress[s] : # 16| r16_2(glval) = FunctionAddress[MemberFunction] : # 16| r16_3(int) = Constant[1] : -# 16| r16_4(int) = Call : func:r16_2, this:r16_1, 0:r16_3 +# 16| r16_4(int) = Call[MemberFunction] : func:r16_2, this:r16_1, 0:r16_3 # 16| mu16_5(unknown) = ^CallSideEffect : ~m? # 16| v16_6(void) = ^BufferReadSideEffect[-1] : &:r16_1, ~m? # 16| mu16_7(S) = ^IndirectMayWriteSideEffect[-1] : &:r16_1 @@ -2304,7 +2304,7 @@ ir.cpp: # 372| mu372_2(unknown) = AliasedDefinition : # 372| mu372_3(unknown) = InitializeNonLocal : # 373| r373_1(glval) = FunctionAddress[VoidFunc] : -# 373| v373_2(void) = Call : func:r373_1 +# 373| v373_2(void) = Call[VoidFunc] : func:r373_1 # 373| mu373_3(unknown) = ^CallSideEffect : ~m? # 374| v374_1(void) = NoOp : # 372| v372_4(void) = ReturnVoid : @@ -2326,7 +2326,7 @@ ir.cpp: # 377| r377_4(int) = Load : &:r377_3, ~m? # 377| r377_5(glval) = VariableAddress[y] : # 377| r377_6(int) = Load : &:r377_5, ~m? -# 377| r377_7(int) = Call : func:r377_2, 0:r377_4, 1:r377_6 +# 377| r377_7(int) = Call[Add] : func:r377_2, 0:r377_4, 1:r377_6 # 377| mu377_8(unknown) = ^CallSideEffect : ~m? # 377| mu377_9(int) = Store : &:r377_1, r377_7 # 376| r376_8(glval) = VariableAddress[#return] : @@ -2345,14 +2345,14 @@ ir.cpp: # 380| mu380_7(int) = InitializeParameter[y] : &:r380_6 # 381| r381_1(glval) = VariableAddress[#return] : # 381| r381_2(glval) = FunctionAddress[VoidFunc] : -# 381| v381_3(void) = Call : func:r381_2 +# 381| v381_3(void) = Call[VoidFunc] : func:r381_2 # 381| mu381_4(unknown) = ^CallSideEffect : ~m? # 381| r381_5(glval) = FunctionAddress[CallAdd] : # 381| r381_6(glval) = VariableAddress[x] : # 381| r381_7(int) = Load : &:r381_6, ~m? # 381| r381_8(glval) = VariableAddress[y] : # 381| r381_9(int) = Load : &:r381_8, ~m? -# 381| r381_10(int) = Call : func:r381_5, 0:r381_7, 1:r381_9 +# 381| r381_10(int) = Call[CallAdd] : func:r381_5, 0:r381_7, 1:r381_9 # 381| mu381_11(unknown) = ^CallSideEffect : ~m? # 381| r381_12(int) = CopyValue : r381_10 # 381| mu381_13(int) = Store : &:r381_1, r381_12 @@ -2866,7 +2866,7 @@ ir.cpp: # 493| Block 1 # 493| r493_4(glval) = FunctionAddress[VoidFunc] : -# 493| v493_5(void) = Call : func:r493_4 +# 493| v493_5(void) = Call[VoidFunc] : func:r493_4 # 493| mu493_6(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 2 @@ -2878,7 +2878,7 @@ ir.cpp: # 493| Block 3 # 493| r493_7(glval) = FunctionAddress[VoidFunc] : -# 493| v493_8(void) = Call : func:r493_7 +# 493| v493_8(void) = Call[VoidFunc] : func:r493_7 # 493| mu493_9(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 2 @@ -3185,7 +3185,7 @@ ir.cpp: # 552| r552_2(glval<..(*)(..)>) = VariableAddress[pfn] : # 552| r552_3(..(*)(..)) = Load : &:r552_2, ~m? # 552| r552_4(int) = Constant[5] : -# 552| r552_5(int) = Call : func:r552_3, 0:r552_4 +# 552| r552_5(int) = Call[?] : func:r552_3, 0:r552_4 # 552| mu552_6(unknown) = ^CallSideEffect : ~m? # 552| mu552_7(int) = Store : &:r552_1, r552_5 # 551| r551_6(glval) = VariableAddress[#return] : @@ -3306,7 +3306,7 @@ ir.cpp: # 585| r585_4(int) = Constant[1] : # 585| r585_5(glval) = StringConstant["string"] : # 585| r585_6(char *) = Convert : r585_5 -# 585| v585_7(void) = Call : func:r585_1, 0:r585_3, 1:r585_4, 2:r585_6 +# 585| v585_7(void) = Call[VarArgFunction] : func:r585_1, 0:r585_3, 1:r585_4, 2:r585_6 # 585| mu585_8(unknown) = ^CallSideEffect : ~m? # 585| v585_9(void) = ^BufferReadSideEffect[0] : &:r585_3, ~m? # 585| v585_10(void) = ^BufferReadSideEffect[2] : &:r585_6, ~m? @@ -3353,7 +3353,7 @@ ir.cpp: # 616| r616_1(glval) = VariableAddress[s1] : # 616| mu616_2(String) = Uninitialized[s1] : &:r616_1 # 616| r616_3(glval) = FunctionAddress[String] : -# 616| v616_4(void) = Call : func:r616_3, this:r616_1 +# 616| v616_4(void) = Call[String] : func:r616_3, this:r616_1 # 616| mu616_5(unknown) = ^CallSideEffect : ~m? # 616| mu616_6(String) = ^IndirectMayWriteSideEffect[-1] : &:r616_1 # 617| r617_1(glval) = VariableAddress[s2] : @@ -3361,14 +3361,14 @@ ir.cpp: # 617| r617_3(glval) = FunctionAddress[String] : # 617| r617_4(glval) = StringConstant["hello"] : # 617| r617_5(char *) = Convert : r617_4 -# 617| v617_6(void) = Call : func:r617_3, this:r617_1, 0:r617_5 +# 617| v617_6(void) = Call[String] : func:r617_3, this:r617_1, 0:r617_5 # 617| mu617_7(unknown) = ^CallSideEffect : ~m? # 617| mu617_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r617_1 # 617| v617_9(void) = ^BufferReadSideEffect[0] : &:r617_5, ~m? # 617| mu617_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r617_5 # 618| r618_1(glval) = VariableAddress[s3] : # 618| r618_2(glval) = FunctionAddress[ReturnObject] : -# 618| r618_3(String) = Call : func:r618_2 +# 618| r618_3(String) = Call[ReturnObject] : func:r618_2 # 618| mu618_4(unknown) = ^CallSideEffect : ~m? # 618| mu618_5(String) = Store : &:r618_1, r618_3 # 619| r619_1(glval) = VariableAddress[s4] : @@ -3376,7 +3376,7 @@ ir.cpp: # 619| r619_3(glval) = FunctionAddress[String] : # 619| r619_4(glval) = StringConstant["test"] : # 619| r619_5(char *) = Convert : r619_4 -# 619| v619_6(void) = Call : func:r619_3, this:r619_1, 0:r619_5 +# 619| v619_6(void) = Call[String] : func:r619_3, this:r619_1, 0:r619_5 # 619| mu619_7(unknown) = ^CallSideEffect : ~m? # 619| mu619_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r619_1 # 619| v619_9(void) = ^BufferReadSideEffect[0] : &:r619_5, ~m? @@ -3406,7 +3406,7 @@ ir.cpp: # 623| r623_3(glval) = CopyValue : r623_2 # 623| r623_4(glval) = Convert : r623_3 # 623| r623_5(glval) = FunctionAddress[c_str] : -# 623| r623_6(char *) = Call : func:r623_5, this:r623_4 +# 623| r623_6(char *) = Call[c_str] : func:r623_5, this:r623_4 # 623| mu623_7(unknown) = ^CallSideEffect : ~m? # 623| v623_8(void) = ^BufferReadSideEffect[-1] : &:r623_4, ~m? # 623| mu623_9(String) = ^IndirectMayWriteSideEffect[-1] : &:r623_4 @@ -3414,14 +3414,14 @@ ir.cpp: # 624| r624_2(String *) = Load : &:r624_1, ~m? # 624| r624_3(String *) = Convert : r624_2 # 624| r624_4(glval) = FunctionAddress[c_str] : -# 624| r624_5(char *) = Call : func:r624_4, this:r624_3 +# 624| r624_5(char *) = Call[c_str] : func:r624_4, this:r624_3 # 624| mu624_6(unknown) = ^CallSideEffect : ~m? # 624| v624_7(void) = ^BufferReadSideEffect[-1] : &:r624_3, ~m? # 624| mu624_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r624_3 # 625| r625_1(glval) = VariableAddress[s] : # 625| r625_2(glval) = Convert : r625_1 # 625| r625_3(glval) = FunctionAddress[c_str] : -# 625| r625_4(char *) = Call : func:r625_3, this:r625_2 +# 625| r625_4(char *) = Call[c_str] : func:r625_3, this:r625_2 # 625| mu625_5(unknown) = ^CallSideEffect : ~m? # 625| v625_6(void) = ^BufferReadSideEffect[-1] : &:r625_2, ~m? # 625| mu625_7(String) = ^IndirectMayWriteSideEffect[-1] : &:r625_2 @@ -3444,11 +3444,11 @@ ir.cpp: #-----| v0_1(void) = NoOp : # 628| r628_8(glval) = FieldAddress[m_f] : mu628_5 # 628| r628_9(glval) = FunctionAddress[~String] : -# 628| v628_10(void) = Call : func:r628_9, this:r628_8 +# 628| v628_10(void) = Call[~String] : func:r628_9, this:r628_8 # 628| mu628_11(unknown) = ^CallSideEffect : ~m? # 628| r628_12(glval) = FieldAddress[m_b] : mu628_5 # 628| r628_13(glval) = FunctionAddress[~String] : -# 628| v628_14(void) = Call : func:r628_13, this:r628_12 +# 628| v628_14(void) = Call[~String] : func:r628_13, this:r628_12 # 628| mu628_15(unknown) = ^CallSideEffect : ~m? # 628| v628_16(void) = ReturnIndirection[#this] : &:r628_6, ~m? # 628| v628_17(void) = ReturnVoid : @@ -3578,7 +3578,7 @@ ir.cpp: # 653| r653_2(C *) = Load : &:r653_1, ~m? # 653| r653_3(glval) = FunctionAddress[InstanceMemberFunction] : # 653| r653_4(int) = Constant[0] : -# 653| r653_5(int) = Call : func:r653_3, this:r653_2, 0:r653_4 +# 653| r653_5(int) = Call[InstanceMemberFunction] : func:r653_3, this:r653_2, 0:r653_4 # 653| mu653_6(unknown) = ^CallSideEffect : ~m? # 653| v653_7(void) = ^BufferReadSideEffect[-1] : &:r653_2, ~m? # 653| mu653_8(C) = ^IndirectMayWriteSideEffect[-1] : &:r653_2 @@ -3587,7 +3587,7 @@ ir.cpp: # 654| r654_3(glval) = CopyValue : r654_2 # 654| r654_4(glval) = FunctionAddress[InstanceMemberFunction] : # 654| r654_5(int) = Constant[1] : -# 654| r654_6(int) = Call : func:r654_4, this:r654_3, 0:r654_5 +# 654| r654_6(int) = Call[InstanceMemberFunction] : func:r654_4, this:r654_3, 0:r654_5 # 654| mu654_7(unknown) = ^CallSideEffect : ~m? # 654| v654_8(void) = ^BufferReadSideEffect[-1] : &:r654_3, ~m? # 654| mu654_9(C) = ^IndirectMayWriteSideEffect[-1] : &:r654_3 @@ -3595,7 +3595,7 @@ ir.cpp: # 655| r655_2(C *) = Load : &:r655_1, ~m? # 655| r655_3(glval) = FunctionAddress[InstanceMemberFunction] : # 655| r655_4(int) = Constant[2] : -# 655| r655_5(int) = Call : func:r655_3, this:r655_2, 0:r655_4 +# 655| r655_5(int) = Call[InstanceMemberFunction] : func:r655_3, this:r655_2, 0:r655_4 # 655| mu655_6(unknown) = ^CallSideEffect : ~m? # 655| v655_7(void) = ^BufferReadSideEffect[-1] : &:r655_2, ~m? # 655| mu655_8(C) = ^IndirectMayWriteSideEffect[-1] : &:r655_2 @@ -3619,7 +3619,7 @@ ir.cpp: # 659| mu659_3(int) = Store : &:r659_1, r659_2 # 663| r663_1(glval) = FieldAddress[m_b] : mu658_5 # 663| r663_2(glval) = FunctionAddress[String] : -# 663| v663_3(void) = Call : func:r663_2, this:r663_1 +# 663| v663_3(void) = Call[String] : func:r663_2, this:r663_1 # 663| mu663_4(unknown) = ^CallSideEffect : ~m? # 663| mu663_5(String) = ^IndirectMayWriteSideEffect[-1] : &:r663_1 # 660| r660_1(glval) = FieldAddress[m_c] : mu658_5 @@ -3632,7 +3632,7 @@ ir.cpp: # 662| r662_2(glval) = FunctionAddress[String] : # 662| r662_3(glval) = StringConstant["test"] : # 662| r662_4(char *) = Convert : r662_3 -# 662| v662_5(void) = Call : func:r662_2, this:r662_1, 0:r662_4 +# 662| v662_5(void) = Call[String] : func:r662_2, this:r662_1, 0:r662_4 # 662| mu662_6(unknown) = ^CallSideEffect : ~m? # 662| mu662_7(String) = ^IndirectMayWriteSideEffect[-1] : &:r662_1 # 662| v662_8(void) = ^BufferReadSideEffect[0] : &:r662_4, ~m? @@ -3696,7 +3696,7 @@ ir.cpp: # 687| mu687_6(int &) = Store : &:r687_1, r687_5 # 688| r688_1(glval) = VariableAddress[r3] : # 688| r688_2(glval) = FunctionAddress[ReturnReference] : -# 688| r688_3(String &) = Call : func:r688_2 +# 688| r688_3(String &) = Call[ReturnReference] : func:r688_2 # 688| mu688_4(unknown) = ^CallSideEffect : ~m? # 688| r688_5(glval) = CopyValue : r688_3 # 688| r688_6(glval) = Convert : r688_5 @@ -3750,7 +3750,7 @@ ir.cpp: # 700| r700_2(..(&)(..)) = Load : &:r700_1, ~m? # 700| r700_3(..(*)(..)) = CopyValue : r700_2 # 700| r700_4(int) = Constant[5] : -# 700| r700_5(int) = Call : func:r700_3, 0:r700_4 +# 700| r700_5(int) = Call[?] : func:r700_3, 0:r700_4 # 700| mu700_6(unknown) = ^CallSideEffect : ~m? # 701| v701_1(void) = NoOp : # 697| v697_4(void) = ReturnVoid : @@ -3814,7 +3814,7 @@ ir.cpp: # 709| r709_4(int) = Load : &:r709_3, ~m? # 709| r709_5(glval) = VariableAddress[y] : # 709| r709_6(int) = Load : &:r709_5, ~m? -# 709| r709_7(int) = Call : func:r709_2, 0:r709_4, 1:r709_6 +# 709| r709_7(int) = Call[min] : func:r709_2, 0:r709_4, 1:r709_6 # 709| mu709_8(unknown) = ^CallSideEffect : ~m? # 709| mu709_9(int) = Store : &:r709_1, r709_7 # 708| r708_8(glval) = VariableAddress[#return] : @@ -3851,7 +3851,7 @@ ir.cpp: # 721| r721_2(glval) = FunctionAddress[Func] : # 721| r721_3(void *) = Constant[0] : # 721| r721_4(char) = Constant[111] : -# 721| r721_5(long) = Call : func:r721_2, 0:r721_3, 1:r721_4 +# 721| r721_5(long) = Call[Func] : func:r721_2, 0:r721_3, 1:r721_4 # 721| mu721_6(unknown) = ^CallSideEffect : ~m? # 721| v721_7(void) = ^BufferReadSideEffect[0] : &:r721_3, ~m? # 721| mu721_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r721_3 @@ -3926,7 +3926,7 @@ ir.cpp: # 731| r731_13(glval) = FunctionAddress[String] : # 731| r731_14(glval) = StringConstant["String object"] : # 731| r731_15(char *) = Convert : r731_14 -# 731| v731_16(void) = Call : func:r731_13, this:r731_11, 0:r731_15 +# 731| v731_16(void) = Call[String] : func:r731_13, this:r731_11, 0:r731_15 # 731| mu731_17(unknown) = ^CallSideEffect : ~m? # 731| mu731_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r731_11 # 731| v731_19(void) = ^BufferReadSideEffect[0] : &:r731_15, ~m? @@ -3955,7 +3955,7 @@ ir.cpp: # 736| r736_3(glval) = FunctionAddress[String] : # 736| r736_4(glval) = VariableAddress[s] : # 736| r736_5(char *) = Load : &:r736_4, ~m? -# 736| v736_6(void) = Call : func:r736_3, this:r736_1, 0:r736_5 +# 736| v736_6(void) = Call[String] : func:r736_3, this:r736_1, 0:r736_5 # 736| mu736_7(unknown) = ^CallSideEffect : ~m? # 736| mu736_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r736_1 # 736| v736_9(void) = ^BufferReadSideEffect[0] : &:r736_5, ~m? @@ -4009,7 +4009,7 @@ ir.cpp: #-----| r0_5(glval) = CopyValue : r745_14 # 745| r745_15(glval) = FieldAddress[base_s] : r0_5 #-----| r0_6(String &) = CopyValue : r745_15 -# 745| r745_16(String &) = Call : func:r745_12, this:r745_11, 0:r0_6 +# 745| r745_16(String &) = Call[operator=] : func:r745_12, this:r745_11, 0:r0_6 # 745| mu745_17(unknown) = ^CallSideEffect : ~m? # 745| v745_18(void) = ^BufferReadSideEffect[-1] : &:r745_11, ~m? #-----| v0_7(void) = ^BufferReadSideEffect[0] : &:r0_6, ~m? @@ -4044,7 +4044,7 @@ ir.cpp: #-----| mu0_4(unknown) = InitializeIndirection[p#0] : &:r0_3 # 745| r745_8(glval) = FieldAddress[base_s] : mu745_5 # 745| r745_9(glval) = FunctionAddress[String] : -# 745| v745_10(void) = Call : func:r745_9, this:r745_8 +# 745| v745_10(void) = Call[String] : func:r745_9, this:r745_8 # 745| mu745_11(unknown) = ^CallSideEffect : ~m? # 745| mu745_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r745_8 # 745| v745_13(void) = NoOp : @@ -4065,7 +4065,7 @@ ir.cpp: # 748| mu748_7(Base) = InitializeIndirection[#this] : &:r748_6 # 748| r748_8(glval) = FieldAddress[base_s] : mu748_5 # 748| r748_9(glval) = FunctionAddress[String] : -# 748| v748_10(void) = Call : func:r748_9, this:r748_8 +# 748| v748_10(void) = Call[String] : func:r748_9, this:r748_8 # 748| mu748_11(unknown) = ^CallSideEffect : ~m? # 748| mu748_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r748_8 # 749| v749_1(void) = NoOp : @@ -4086,7 +4086,7 @@ ir.cpp: # 751| v751_1(void) = NoOp : # 751| r751_2(glval) = FieldAddress[base_s] : mu750_5 # 751| r751_3(glval) = FunctionAddress[~String] : -# 751| v751_4(void) = Call : func:r751_3, this:r751_2 +# 751| v751_4(void) = Call[~String] : func:r751_3, this:r751_2 # 751| mu751_5(unknown) = ^CallSideEffect : ~m? # 750| v750_8(void) = ReturnIndirection[#this] : &:r750_6, ~m? # 750| v750_9(void) = ReturnVoid : @@ -4117,7 +4117,7 @@ ir.cpp: #-----| r0_7(Base *) = ConvertToNonVirtualBase[Middle : Base] : r754_13 # 754| r754_14(glval) = CopyValue : r0_7 #-----| r0_8(Base &) = CopyValue : r754_14 -# 754| r754_15(Base &) = Call : func:r754_10, this:r0_5, 0:r0_8 +# 754| r754_15(Base &) = Call[operator=] : func:r754_10, this:r0_5, 0:r0_8 # 754| mu754_16(unknown) = ^CallSideEffect : ~m? #-----| v0_9(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m? #-----| v0_10(void) = ^BufferReadSideEffect[0] : &:r0_8, ~m? @@ -4134,7 +4134,7 @@ ir.cpp: #-----| r0_14(glval) = CopyValue : r754_23 # 754| r754_24(glval) = FieldAddress[middle_s] : r0_14 #-----| r0_15(String &) = CopyValue : r754_24 -# 754| r754_25(String &) = Call : func:r754_21, this:r754_20, 0:r0_15 +# 754| r754_25(String &) = Call[operator=] : func:r754_21, this:r754_20, 0:r0_15 # 754| mu754_26(unknown) = ^CallSideEffect : ~m? # 754| v754_27(void) = ^BufferReadSideEffect[-1] : &:r754_20, ~m? #-----| v0_16(void) = ^BufferReadSideEffect[0] : &:r0_15, ~m? @@ -4165,12 +4165,12 @@ ir.cpp: # 757| mu757_7(Middle) = InitializeIndirection[#this] : &:r757_6 # 757| r757_8(glval) = ConvertToNonVirtualBase[Middle : Base] : mu757_5 # 757| r757_9(glval) = FunctionAddress[Base] : -# 757| v757_10(void) = Call : func:r757_9, this:r757_8 +# 757| v757_10(void) = Call[Base] : func:r757_9, this:r757_8 # 757| mu757_11(unknown) = ^CallSideEffect : ~m? # 757| mu757_12(Base) = ^IndirectMayWriteSideEffect[-1] : &:r757_8 # 757| r757_13(glval) = FieldAddress[middle_s] : mu757_5 # 757| r757_14(glval) = FunctionAddress[String] : -# 757| v757_15(void) = Call : func:r757_14, this:r757_13 +# 757| v757_15(void) = Call[String] : func:r757_14, this:r757_13 # 757| mu757_16(unknown) = ^CallSideEffect : ~m? # 757| mu757_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r757_13 # 758| v758_1(void) = NoOp : @@ -4191,11 +4191,11 @@ ir.cpp: # 760| v760_1(void) = NoOp : # 760| r760_2(glval) = FieldAddress[middle_s] : mu759_5 # 760| r760_3(glval) = FunctionAddress[~String] : -# 760| v760_4(void) = Call : func:r760_3, this:r760_2 +# 760| v760_4(void) = Call[~String] : func:r760_3, this:r760_2 # 760| mu760_5(unknown) = ^CallSideEffect : ~m? # 760| r760_6(glval) = ConvertToNonVirtualBase[Middle : Base] : mu759_5 # 760| r760_7(glval) = FunctionAddress[~Base] : -# 760| v760_8(void) = Call : func:r760_7, this:r760_6 +# 760| v760_8(void) = Call[~Base] : func:r760_7, this:r760_6 # 760| mu760_9(unknown) = ^CallSideEffect : ~m? # 759| v759_8(void) = ReturnIndirection[#this] : &:r759_6, ~m? # 759| v759_9(void) = ReturnVoid : @@ -4226,7 +4226,7 @@ ir.cpp: #-----| r0_7(Middle *) = ConvertToNonVirtualBase[Derived : Middle] : r763_13 # 763| r763_14(glval) = CopyValue : r0_7 #-----| r0_8(Middle &) = CopyValue : r763_14 -# 763| r763_15(Middle &) = Call : func:r763_10, this:r0_5, 0:r0_8 +# 763| r763_15(Middle &) = Call[operator=] : func:r763_10, this:r0_5, 0:r0_8 # 763| mu763_16(unknown) = ^CallSideEffect : ~m? #-----| v0_9(void) = ^BufferReadSideEffect[-1] : &:r0_5, ~m? #-----| v0_10(void) = ^BufferReadSideEffect[0] : &:r0_8, ~m? @@ -4243,7 +4243,7 @@ ir.cpp: #-----| r0_14(glval) = CopyValue : r763_23 # 763| r763_24(glval) = FieldAddress[derived_s] : r0_14 #-----| r0_15(String &) = CopyValue : r763_24 -# 763| r763_25(String &) = Call : func:r763_21, this:r763_20, 0:r0_15 +# 763| r763_25(String &) = Call[operator=] : func:r763_21, this:r763_20, 0:r0_15 # 763| mu763_26(unknown) = ^CallSideEffect : ~m? # 763| v763_27(void) = ^BufferReadSideEffect[-1] : &:r763_20, ~m? #-----| v0_16(void) = ^BufferReadSideEffect[0] : &:r0_15, ~m? @@ -4274,12 +4274,12 @@ ir.cpp: # 766| mu766_7(Derived) = InitializeIndirection[#this] : &:r766_6 # 766| r766_8(glval) = ConvertToNonVirtualBase[Derived : Middle] : mu766_5 # 766| r766_9(glval) = FunctionAddress[Middle] : -# 766| v766_10(void) = Call : func:r766_9, this:r766_8 +# 766| v766_10(void) = Call[Middle] : func:r766_9, this:r766_8 # 766| mu766_11(unknown) = ^CallSideEffect : ~m? # 766| mu766_12(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r766_8 # 766| r766_13(glval) = FieldAddress[derived_s] : mu766_5 # 766| r766_14(glval) = FunctionAddress[String] : -# 766| v766_15(void) = Call : func:r766_14, this:r766_13 +# 766| v766_15(void) = Call[String] : func:r766_14, this:r766_13 # 766| mu766_16(unknown) = ^CallSideEffect : ~m? # 766| mu766_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r766_13 # 767| v767_1(void) = NoOp : @@ -4300,11 +4300,11 @@ ir.cpp: # 769| v769_1(void) = NoOp : # 769| r769_2(glval) = FieldAddress[derived_s] : mu768_5 # 769| r769_3(glval) = FunctionAddress[~String] : -# 769| v769_4(void) = Call : func:r769_3, this:r769_2 +# 769| v769_4(void) = Call[~String] : func:r769_3, this:r769_2 # 769| mu769_5(unknown) = ^CallSideEffect : ~m? # 769| r769_6(glval) = ConvertToNonVirtualBase[Derived : Middle] : mu768_5 # 769| r769_7(glval) = FunctionAddress[~Middle] : -# 769| v769_8(void) = Call : func:r769_7, this:r769_6 +# 769| v769_8(void) = Call[~Middle] : func:r769_7, this:r769_6 # 769| mu769_9(unknown) = ^CallSideEffect : ~m? # 768| v768_8(void) = ReturnIndirection[#this] : &:r768_6, ~m? # 768| v768_9(void) = ReturnVoid : @@ -4322,12 +4322,12 @@ ir.cpp: # 775| mu775_7(MiddleVB1) = InitializeIndirection[#this] : &:r775_6 # 775| r775_8(glval) = ConvertToNonVirtualBase[MiddleVB1 : Base] : mu775_5 # 775| r775_9(glval) = FunctionAddress[Base] : -# 775| v775_10(void) = Call : func:r775_9, this:r775_8 +# 775| v775_10(void) = Call[Base] : func:r775_9, this:r775_8 # 775| mu775_11(unknown) = ^CallSideEffect : ~m? # 775| mu775_12(Base) = ^IndirectMayWriteSideEffect[-1] : &:r775_8 # 775| r775_13(glval) = FieldAddress[middlevb1_s] : mu775_5 # 775| r775_14(glval) = FunctionAddress[String] : -# 775| v775_15(void) = Call : func:r775_14, this:r775_13 +# 775| v775_15(void) = Call[String] : func:r775_14, this:r775_13 # 775| mu775_16(unknown) = ^CallSideEffect : ~m? # 775| mu775_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r775_13 # 776| v776_1(void) = NoOp : @@ -4348,11 +4348,11 @@ ir.cpp: # 778| v778_1(void) = NoOp : # 778| r778_2(glval) = FieldAddress[middlevb1_s] : mu777_5 # 778| r778_3(glval) = FunctionAddress[~String] : -# 778| v778_4(void) = Call : func:r778_3, this:r778_2 +# 778| v778_4(void) = Call[~String] : func:r778_3, this:r778_2 # 778| mu778_5(unknown) = ^CallSideEffect : ~m? # 778| r778_6(glval) = ConvertToNonVirtualBase[MiddleVB1 : Base] : mu777_5 # 778| r778_7(glval) = FunctionAddress[~Base] : -# 778| v778_8(void) = Call : func:r778_7, this:r778_6 +# 778| v778_8(void) = Call[~Base] : func:r778_7, this:r778_6 # 778| mu778_9(unknown) = ^CallSideEffect : ~m? # 777| v777_8(void) = ReturnIndirection[#this] : &:r777_6, ~m? # 777| v777_9(void) = ReturnVoid : @@ -4370,12 +4370,12 @@ ir.cpp: # 784| mu784_7(MiddleVB2) = InitializeIndirection[#this] : &:r784_6 # 784| r784_8(glval) = ConvertToNonVirtualBase[MiddleVB2 : Base] : mu784_5 # 784| r784_9(glval) = FunctionAddress[Base] : -# 784| v784_10(void) = Call : func:r784_9, this:r784_8 +# 784| v784_10(void) = Call[Base] : func:r784_9, this:r784_8 # 784| mu784_11(unknown) = ^CallSideEffect : ~m? # 784| mu784_12(Base) = ^IndirectMayWriteSideEffect[-1] : &:r784_8 # 784| r784_13(glval) = FieldAddress[middlevb2_s] : mu784_5 # 784| r784_14(glval) = FunctionAddress[String] : -# 784| v784_15(void) = Call : func:r784_14, this:r784_13 +# 784| v784_15(void) = Call[String] : func:r784_14, this:r784_13 # 784| mu784_16(unknown) = ^CallSideEffect : ~m? # 784| mu784_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r784_13 # 785| v785_1(void) = NoOp : @@ -4396,11 +4396,11 @@ ir.cpp: # 787| v787_1(void) = NoOp : # 787| r787_2(glval) = FieldAddress[middlevb2_s] : mu786_5 # 787| r787_3(glval) = FunctionAddress[~String] : -# 787| v787_4(void) = Call : func:r787_3, this:r787_2 +# 787| v787_4(void) = Call[~String] : func:r787_3, this:r787_2 # 787| mu787_5(unknown) = ^CallSideEffect : ~m? # 787| r787_6(glval) = ConvertToNonVirtualBase[MiddleVB2 : Base] : mu786_5 # 787| r787_7(glval) = FunctionAddress[~Base] : -# 787| v787_8(void) = Call : func:r787_7, this:r787_6 +# 787| v787_8(void) = Call[~Base] : func:r787_7, this:r787_6 # 787| mu787_9(unknown) = ^CallSideEffect : ~m? # 786| v786_8(void) = ReturnIndirection[#this] : &:r786_6, ~m? # 786| v786_9(void) = ReturnVoid : @@ -4418,22 +4418,22 @@ ir.cpp: # 793| mu793_7(DerivedVB) = InitializeIndirection[#this] : &:r793_6 # 793| r793_8(glval) = ConvertToNonVirtualBase[DerivedVB : Base] : mu793_5 # 793| r793_9(glval) = FunctionAddress[Base] : -# 793| v793_10(void) = Call : func:r793_9, this:r793_8 +# 793| v793_10(void) = Call[Base] : func:r793_9, this:r793_8 # 793| mu793_11(unknown) = ^CallSideEffect : ~m? # 793| mu793_12(Base) = ^IndirectMayWriteSideEffect[-1] : &:r793_8 # 793| r793_13(glval) = ConvertToNonVirtualBase[DerivedVB : MiddleVB1] : mu793_5 # 793| r793_14(glval) = FunctionAddress[MiddleVB1] : -# 793| v793_15(void) = Call : func:r793_14, this:r793_13 +# 793| v793_15(void) = Call[MiddleVB1] : func:r793_14, this:r793_13 # 793| mu793_16(unknown) = ^CallSideEffect : ~m? # 793| mu793_17(MiddleVB1) = ^IndirectMayWriteSideEffect[-1] : &:r793_13 # 793| r793_18(glval) = ConvertToNonVirtualBase[DerivedVB : MiddleVB2] : mu793_5 # 793| r793_19(glval) = FunctionAddress[MiddleVB2] : -# 793| v793_20(void) = Call : func:r793_19, this:r793_18 +# 793| v793_20(void) = Call[MiddleVB2] : func:r793_19, this:r793_18 # 793| mu793_21(unknown) = ^CallSideEffect : ~m? # 793| mu793_22(MiddleVB2) = ^IndirectMayWriteSideEffect[-1] : &:r793_18 # 793| r793_23(glval) = FieldAddress[derivedvb_s] : mu793_5 # 793| r793_24(glval) = FunctionAddress[String] : -# 793| v793_25(void) = Call : func:r793_24, this:r793_23 +# 793| v793_25(void) = Call[String] : func:r793_24, this:r793_23 # 793| mu793_26(unknown) = ^CallSideEffect : ~m? # 793| mu793_27(String) = ^IndirectMayWriteSideEffect[-1] : &:r793_23 # 794| v794_1(void) = NoOp : @@ -4454,19 +4454,19 @@ ir.cpp: # 796| v796_1(void) = NoOp : # 796| r796_2(glval) = FieldAddress[derivedvb_s] : mu795_5 # 796| r796_3(glval) = FunctionAddress[~String] : -# 796| v796_4(void) = Call : func:r796_3, this:r796_2 +# 796| v796_4(void) = Call[~String] : func:r796_3, this:r796_2 # 796| mu796_5(unknown) = ^CallSideEffect : ~m? # 796| r796_6(glval) = ConvertToNonVirtualBase[DerivedVB : MiddleVB2] : mu795_5 # 796| r796_7(glval) = FunctionAddress[~MiddleVB2] : -# 796| v796_8(void) = Call : func:r796_7, this:r796_6 +# 796| v796_8(void) = Call[~MiddleVB2] : func:r796_7, this:r796_6 # 796| mu796_9(unknown) = ^CallSideEffect : ~m? # 796| r796_10(glval) = ConvertToNonVirtualBase[DerivedVB : MiddleVB1] : mu795_5 # 796| r796_11(glval) = FunctionAddress[~MiddleVB1] : -# 796| v796_12(void) = Call : func:r796_11, this:r796_10 +# 796| v796_12(void) = Call[~MiddleVB1] : func:r796_11, this:r796_10 # 796| mu796_13(unknown) = ^CallSideEffect : ~m? # 796| r796_14(glval) = ConvertToNonVirtualBase[DerivedVB : Base] : mu795_5 # 796| r796_15(glval) = FunctionAddress[~Base] : -# 796| v796_16(void) = Call : func:r796_15, this:r796_14 +# 796| v796_16(void) = Call[~Base] : func:r796_15, this:r796_14 # 796| mu796_17(unknown) = ^CallSideEffect : ~m? # 795| v795_8(void) = ReturnIndirection[#this] : &:r795_6, ~m? # 795| v795_9(void) = ReturnVoid : @@ -4481,19 +4481,19 @@ ir.cpp: # 800| r800_1(glval) = VariableAddress[b] : # 800| mu800_2(Base) = Uninitialized[b] : &:r800_1 # 800| r800_3(glval) = FunctionAddress[Base] : -# 800| v800_4(void) = Call : func:r800_3, this:r800_1 +# 800| v800_4(void) = Call[Base] : func:r800_3, this:r800_1 # 800| mu800_5(unknown) = ^CallSideEffect : ~m? # 800| mu800_6(Base) = ^IndirectMayWriteSideEffect[-1] : &:r800_1 # 801| r801_1(glval) = VariableAddress[m] : # 801| mu801_2(Middle) = Uninitialized[m] : &:r801_1 # 801| r801_3(glval) = FunctionAddress[Middle] : -# 801| v801_4(void) = Call : func:r801_3, this:r801_1 +# 801| v801_4(void) = Call[Middle] : func:r801_3, this:r801_1 # 801| mu801_5(unknown) = ^CallSideEffect : ~m? # 801| mu801_6(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r801_1 # 802| r802_1(glval) = VariableAddress[d] : # 802| mu802_2(Derived) = Uninitialized[d] : &:r802_1 # 802| r802_3(glval) = FunctionAddress[Derived] : -# 802| v802_4(void) = Call : func:r802_3, this:r802_1 +# 802| v802_4(void) = Call[Derived] : func:r802_3, this:r802_1 # 802| mu802_5(unknown) = ^CallSideEffect : ~m? # 802| mu802_6(Derived) = ^IndirectMayWriteSideEffect[-1] : &:r802_1 # 804| r804_1(glval) = VariableAddress[pb] : @@ -4513,7 +4513,7 @@ ir.cpp: # 808| r808_3(glval) = VariableAddress[m] : # 808| r808_4(glval) = ConvertToNonVirtualBase[Middle : Base] : r808_3 # 808| r808_5(Base &) = CopyValue : r808_4 -# 808| r808_6(Base &) = Call : func:r808_2, this:r808_1, 0:r808_5 +# 808| r808_6(Base &) = Call[operator=] : func:r808_2, this:r808_1, 0:r808_5 # 808| mu808_7(unknown) = ^CallSideEffect : ~m? # 808| v808_8(void) = ^BufferReadSideEffect[-1] : &:r808_1, ~m? # 808| v808_9(void) = ^BufferReadSideEffect[0] : &:r808_5, ~m? @@ -4526,14 +4526,14 @@ ir.cpp: # 809| r809_4(glval) = VariableAddress[m] : # 809| r809_5(glval) = ConvertToNonVirtualBase[Middle : Base] : r809_4 # 809| r809_6(Base &) = CopyValue : r809_5 -# 809| v809_7(void) = Call : func:r809_3, 0:r809_6 +# 809| v809_7(void) = Call[Base] : func:r809_3, 0:r809_6 # 809| mu809_8(unknown) = ^CallSideEffect : ~m? # 809| mu809_9(Base) = ^IndirectMayWriteSideEffect[-1] : # 809| v809_10(void) = ^BufferReadSideEffect[0] : &:r809_6, ~m? # 809| mu809_11(unknown) = ^BufferMayWriteSideEffect[0] : &:r809_6 # 809| r809_12(glval) = Convert : v809_7 # 809| r809_13(Base &) = CopyValue : r809_12 -# 809| r809_14(Base &) = Call : func:r809_2, this:r809_1, 0:r809_13 +# 809| r809_14(Base &) = Call[operator=] : func:r809_2, this:r809_1, 0:r809_13 # 809| mu809_15(unknown) = ^CallSideEffect : ~m? # 809| v809_16(void) = ^BufferReadSideEffect[-1] : &:r809_1, ~m? # 809| v809_17(void) = ^BufferReadSideEffect[0] : &:r809_13, ~m? @@ -4546,14 +4546,14 @@ ir.cpp: # 810| r810_4(glval) = VariableAddress[m] : # 810| r810_5(glval) = ConvertToNonVirtualBase[Middle : Base] : r810_4 # 810| r810_6(Base &) = CopyValue : r810_5 -# 810| v810_7(void) = Call : func:r810_3, 0:r810_6 +# 810| v810_7(void) = Call[Base] : func:r810_3, 0:r810_6 # 810| mu810_8(unknown) = ^CallSideEffect : ~m? # 810| mu810_9(Base) = ^IndirectMayWriteSideEffect[-1] : # 810| v810_10(void) = ^BufferReadSideEffect[0] : &:r810_6, ~m? # 810| mu810_11(unknown) = ^BufferMayWriteSideEffect[0] : &:r810_6 # 810| r810_12(glval) = Convert : v810_7 # 810| r810_13(Base &) = CopyValue : r810_12 -# 810| r810_14(Base &) = Call : func:r810_2, this:r810_1, 0:r810_13 +# 810| r810_14(Base &) = Call[operator=] : func:r810_2, this:r810_1, 0:r810_13 # 810| mu810_15(unknown) = ^CallSideEffect : ~m? # 810| v810_16(void) = ^BufferReadSideEffect[-1] : &:r810_1, ~m? # 810| v810_17(void) = ^BufferReadSideEffect[0] : &:r810_13, ~m? @@ -4586,7 +4586,7 @@ ir.cpp: # 816| r816_4(glval) = ConvertToDerived[Middle : Base] : r816_3 # 816| r816_5(glval) = Convert : r816_4 # 816| r816_6(Middle &) = CopyValue : r816_5 -# 816| r816_7(Middle &) = Call : func:r816_2, this:r816_1, 0:r816_6 +# 816| r816_7(Middle &) = Call[operator=] : func:r816_2, this:r816_1, 0:r816_6 # 816| mu816_8(unknown) = ^CallSideEffect : ~m? # 816| v816_9(void) = ^BufferReadSideEffect[-1] : &:r816_1, ~m? # 816| v816_10(void) = ^BufferReadSideEffect[0] : &:r816_6, ~m? @@ -4599,7 +4599,7 @@ ir.cpp: # 817| r817_4(glval) = ConvertToDerived[Middle : Base] : r817_3 # 817| r817_5(glval) = Convert : r817_4 # 817| r817_6(Middle &) = CopyValue : r817_5 -# 817| r817_7(Middle &) = Call : func:r817_2, this:r817_1, 0:r817_6 +# 817| r817_7(Middle &) = Call[operator=] : func:r817_2, this:r817_1, 0:r817_6 # 817| mu817_8(unknown) = ^CallSideEffect : ~m? # 817| v817_9(void) = ^BufferReadSideEffect[-1] : &:r817_1, ~m? # 817| v817_10(void) = ^BufferReadSideEffect[0] : &:r817_6, ~m? @@ -4627,7 +4627,7 @@ ir.cpp: # 822| r822_4(glval) = ConvertToNonVirtualBase[Derived : Middle] : r822_3 # 822| r822_5(glval) = ConvertToNonVirtualBase[Middle : Base] : r822_4 # 822| r822_6(Base &) = CopyValue : r822_5 -# 822| r822_7(Base &) = Call : func:r822_2, this:r822_1, 0:r822_6 +# 822| r822_7(Base &) = Call[operator=] : func:r822_2, this:r822_1, 0:r822_6 # 822| mu822_8(unknown) = ^CallSideEffect : ~m? # 822| v822_9(void) = ^BufferReadSideEffect[-1] : &:r822_1, ~m? # 822| v822_10(void) = ^BufferReadSideEffect[0] : &:r822_6, ~m? @@ -4641,14 +4641,14 @@ ir.cpp: # 823| r823_5(glval) = ConvertToNonVirtualBase[Derived : Middle] : r823_4 # 823| r823_6(glval) = ConvertToNonVirtualBase[Middle : Base] : r823_5 # 823| r823_7(Base &) = CopyValue : r823_6 -# 823| v823_8(void) = Call : func:r823_3, 0:r823_7 +# 823| v823_8(void) = Call[Base] : func:r823_3, 0:r823_7 # 823| mu823_9(unknown) = ^CallSideEffect : ~m? # 823| mu823_10(Base) = ^IndirectMayWriteSideEffect[-1] : # 823| v823_11(void) = ^BufferReadSideEffect[0] : &:r823_7, ~m? # 823| mu823_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r823_7 # 823| r823_13(glval) = Convert : v823_8 # 823| r823_14(Base &) = CopyValue : r823_13 -# 823| r823_15(Base &) = Call : func:r823_2, this:r823_1, 0:r823_14 +# 823| r823_15(Base &) = Call[operator=] : func:r823_2, this:r823_1, 0:r823_14 # 823| mu823_16(unknown) = ^CallSideEffect : ~m? # 823| v823_17(void) = ^BufferReadSideEffect[-1] : &:r823_1, ~m? # 823| v823_18(void) = ^BufferReadSideEffect[0] : &:r823_14, ~m? @@ -4662,14 +4662,14 @@ ir.cpp: # 824| r824_5(glval) = ConvertToNonVirtualBase[Derived : Middle] : r824_4 # 824| r824_6(glval) = ConvertToNonVirtualBase[Middle : Base] : r824_5 # 824| r824_7(Base &) = CopyValue : r824_6 -# 824| v824_8(void) = Call : func:r824_3, 0:r824_7 +# 824| v824_8(void) = Call[Base] : func:r824_3, 0:r824_7 # 824| mu824_9(unknown) = ^CallSideEffect : ~m? # 824| mu824_10(Base) = ^IndirectMayWriteSideEffect[-1] : # 824| v824_11(void) = ^BufferReadSideEffect[0] : &:r824_7, ~m? # 824| mu824_12(unknown) = ^BufferMayWriteSideEffect[0] : &:r824_7 # 824| r824_13(glval) = Convert : v824_8 # 824| r824_14(Base &) = CopyValue : r824_13 -# 824| r824_15(Base &) = Call : func:r824_2, this:r824_1, 0:r824_14 +# 824| r824_15(Base &) = Call[operator=] : func:r824_2, this:r824_1, 0:r824_14 # 824| mu824_16(unknown) = ^CallSideEffect : ~m? # 824| v824_17(void) = ^BufferReadSideEffect[-1] : &:r824_1, ~m? # 824| v824_18(void) = ^BufferReadSideEffect[0] : &:r824_14, ~m? @@ -4706,7 +4706,7 @@ ir.cpp: # 830| r830_5(glval) = ConvertToDerived[Derived : Middle] : r830_4 # 830| r830_6(glval) = Convert : r830_5 # 830| r830_7(Derived &) = CopyValue : r830_6 -# 830| r830_8(Derived &) = Call : func:r830_2, this:r830_1, 0:r830_7 +# 830| r830_8(Derived &) = Call[operator=] : func:r830_2, this:r830_1, 0:r830_7 # 830| mu830_9(unknown) = ^CallSideEffect : ~m? # 830| v830_10(void) = ^BufferReadSideEffect[-1] : &:r830_1, ~m? # 830| v830_11(void) = ^BufferReadSideEffect[0] : &:r830_7, ~m? @@ -4720,7 +4720,7 @@ ir.cpp: # 831| r831_5(glval) = ConvertToDerived[Derived : Middle] : r831_4 # 831| r831_6(glval) = Convert : r831_5 # 831| r831_7(Derived &) = CopyValue : r831_6 -# 831| r831_8(Derived &) = Call : func:r831_2, this:r831_1, 0:r831_7 +# 831| r831_8(Derived &) = Call[operator=] : func:r831_2, this:r831_1, 0:r831_7 # 831| mu831_9(unknown) = ^CallSideEffect : ~m? # 831| v831_10(void) = ^BufferReadSideEffect[-1] : &:r831_1, ~m? # 831| v831_11(void) = ^BufferReadSideEffect[0] : &:r831_7, ~m? @@ -4791,7 +4791,7 @@ ir.cpp: # 846| mu846_7(PolymorphicDerived) = InitializeIndirection[#this] : &:r846_6 # 846| r846_8(glval) = ConvertToNonVirtualBase[PolymorphicDerived : PolymorphicBase] : mu846_5 # 846| r846_9(glval) = FunctionAddress[PolymorphicBase] : -# 846| v846_10(void) = Call : func:r846_9, this:r846_8 +# 846| v846_10(void) = Call[PolymorphicBase] : func:r846_9, this:r846_8 # 846| mu846_11(unknown) = ^CallSideEffect : ~m? # 846| mu846_12(PolymorphicBase) = ^IndirectMayWriteSideEffect[-1] : &:r846_8 # 846| v846_13(void) = NoOp : @@ -4812,7 +4812,7 @@ ir.cpp: #-----| v0_1(void) = NoOp : # 846| r846_8(glval) = ConvertToNonVirtualBase[PolymorphicDerived : PolymorphicBase] : mu846_5 # 846| r846_9(glval) = FunctionAddress[~PolymorphicBase] : -# 846| v846_10(void) = Call : func:r846_9, this:r846_8 +# 846| v846_10(void) = Call[~PolymorphicBase] : func:r846_9, this:r846_8 # 846| mu846_11(unknown) = ^CallSideEffect : ~m? # 846| v846_12(void) = ReturnIndirection[#this] : &:r846_6, ~m? # 846| v846_13(void) = ReturnVoid : @@ -4827,13 +4827,13 @@ ir.cpp: # 850| r850_1(glval) = VariableAddress[b] : # 850| mu850_2(PolymorphicBase) = Uninitialized[b] : &:r850_1 #-----| r0_1(glval) = FunctionAddress[PolymorphicBase] : -#-----| v0_2(void) = Call : func:r0_1, this:r850_1 +#-----| v0_2(void) = Call[PolymorphicBase] : func:r0_1, this:r850_1 #-----| mu0_3(unknown) = ^CallSideEffect : ~m? #-----| mu0_4(PolymorphicBase) = ^IndirectMayWriteSideEffect[-1] : &:r850_1 # 851| r851_1(glval) = VariableAddress[d] : # 851| mu851_2(PolymorphicDerived) = Uninitialized[d] : &:r851_1 #-----| r0_5(glval) = FunctionAddress[PolymorphicDerived] : -#-----| v0_6(void) = Call : func:r0_5, this:r851_1 +#-----| v0_6(void) = Call[PolymorphicDerived] : func:r0_5, this:r851_1 #-----| mu0_7(unknown) = ^CallSideEffect : ~m? #-----| mu0_8(PolymorphicDerived) = ^IndirectMayWriteSideEffect[-1] : &:r851_1 # 853| r853_1(glval) = VariableAddress[pb] : @@ -4891,7 +4891,7 @@ ir.cpp: # 868| r868_1(glval) = FunctionAddress[String] : # 868| r868_2(glval) = StringConstant[""] : # 868| r868_3(char *) = Convert : r868_2 -# 868| v868_4(void) = Call : func:r868_1, this:mu867_5, 0:r868_3 +# 868| v868_4(void) = Call[String] : func:r868_1, this:mu867_5, 0:r868_3 # 868| mu868_5(unknown) = ^CallSideEffect : ~m? # 868| mu868_6(String) = ^IndirectMayWriteSideEffect[-1] : &:mu867_5 # 868| v868_7(void) = ^BufferReadSideEffect[0] : &:r868_3, ~m? @@ -5082,7 +5082,7 @@ ir.cpp: # 905| r905_3(int) = Load : &:r905_2, ~m? # 905| r905_4(glval<__va_list_tag[1]>) = VariableAddress[args2] : # 905| r905_5(__va_list_tag *) = Convert : r905_4 -# 905| v905_6(void) = Call : func:r905_1, 0:r905_3, 1:r905_5 +# 905| v905_6(void) = Call[VAListUsage] : func:r905_1, 0:r905_3, 1:r905_5 # 905| mu905_7(unknown) = ^CallSideEffect : ~m? # 905| v905_8(void) = ^BufferReadSideEffect[1] : &:r905_5, ~m? # 905| mu905_9(unknown) = ^BufferMayWriteSideEffect[1] : &:r905_5 @@ -5154,20 +5154,20 @@ ir.cpp: # 949| mu949_3(unknown) = InitializeNonLocal : # 950| r950_1(glval) = FunctionAddress[operator new] : # 950| r950_2(unsigned long) = Constant[4] : -# 950| r950_3(void *) = Call : func:r950_1, 0:r950_2 +# 950| r950_3(void *) = Call[operator new] : func:r950_1, 0:r950_2 # 950| mu950_4(unknown) = ^CallSideEffect : ~m? # 950| mu950_5(unknown) = ^InitializeDynamicAllocation : &:r950_3 # 950| r950_6(int *) = Convert : r950_3 # 951| r951_1(glval) = FunctionAddress[operator new] : # 951| r951_2(unsigned long) = Constant[4] : # 951| r951_3(float) = Constant[1.0] : -# 951| r951_4(void *) = Call : func:r951_1, 0:r951_2, 1:r951_3 +# 951| r951_4(void *) = Call[operator new] : func:r951_1, 0:r951_2, 1:r951_3 # 951| mu951_5(unknown) = ^CallSideEffect : ~m? # 951| mu951_6(unknown) = ^InitializeDynamicAllocation : &:r951_4 # 951| r951_7(int *) = Convert : r951_4 # 952| r952_1(glval) = FunctionAddress[operator new] : # 952| r952_2(unsigned long) = Constant[4] : -# 952| r952_3(void *) = Call : func:r952_1, 0:r952_2 +# 952| r952_3(void *) = Call[operator new] : func:r952_1, 0:r952_2 # 952| mu952_4(unknown) = ^CallSideEffect : ~m? # 952| mu952_5(unknown) = ^InitializeDynamicAllocation : &:r952_3 # 952| r952_6(int *) = Convert : r952_3 @@ -5175,25 +5175,25 @@ ir.cpp: # 952| mu952_8(int) = Store : &:r952_6, r952_7 # 953| r953_1(glval) = FunctionAddress[operator new] : # 953| r953_2(unsigned long) = Constant[8] : -# 953| r953_3(void *) = Call : func:r953_1, 0:r953_2 +# 953| r953_3(void *) = Call[operator new] : func:r953_1, 0:r953_2 # 953| mu953_4(unknown) = ^CallSideEffect : ~m? # 953| mu953_5(unknown) = ^InitializeDynamicAllocation : &:r953_3 # 953| r953_6(String *) = Convert : r953_3 # 953| r953_7(glval) = FunctionAddress[String] : -# 953| v953_8(void) = Call : func:r953_7, this:r953_6 +# 953| v953_8(void) = Call[String] : func:r953_7, this:r953_6 # 953| mu953_9(unknown) = ^CallSideEffect : ~m? # 953| mu953_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r953_6 # 954| r954_1(glval) = FunctionAddress[operator new] : # 954| r954_2(unsigned long) = Constant[8] : # 954| r954_3(float) = Constant[1.0] : -# 954| r954_4(void *) = Call : func:r954_1, 0:r954_2, 1:r954_3 +# 954| r954_4(void *) = Call[operator new] : func:r954_1, 0:r954_2, 1:r954_3 # 954| mu954_5(unknown) = ^CallSideEffect : ~m? # 954| mu954_6(unknown) = ^InitializeDynamicAllocation : &:r954_4 # 954| r954_7(String *) = Convert : r954_4 # 954| r954_8(glval) = FunctionAddress[String] : # 954| r954_9(glval) = StringConstant["hello"] : # 954| r954_10(char *) = Convert : r954_9 -# 954| v954_11(void) = Call : func:r954_8, this:r954_7, 0:r954_10 +# 954| v954_11(void) = Call[String] : func:r954_8, this:r954_7, 0:r954_10 # 954| mu954_12(unknown) = ^CallSideEffect : ~m? # 954| mu954_13(String) = ^IndirectMayWriteSideEffect[-1] : &:r954_7 # 954| v954_14(void) = ^BufferReadSideEffect[0] : &:r954_10, ~m? @@ -5201,7 +5201,7 @@ ir.cpp: # 955| r955_1(glval) = FunctionAddress[operator new] : # 955| r955_2(unsigned long) = Constant[256] : # 955| r955_3(align_val_t) = Constant[128] : -# 955| r955_4(void *) = Call : func:r955_1, 0:r955_2, 1:r955_3 +# 955| r955_4(void *) = Call[operator new] : func:r955_1, 0:r955_2, 1:r955_3 # 955| mu955_5(unknown) = ^CallSideEffect : ~m? # 955| mu955_6(unknown) = ^InitializeDynamicAllocation : &:r955_4 # 955| r955_7(Overaligned *) = Convert : r955_4 @@ -5209,7 +5209,7 @@ ir.cpp: # 956| r956_2(unsigned long) = Constant[256] : # 956| r956_3(align_val_t) = Constant[128] : # 956| r956_4(float) = Constant[1.0] : -# 956| r956_5(void *) = Call : func:r956_1, 0:r956_2, 1:r956_3, 2:r956_4 +# 956| r956_5(void *) = Call[operator new] : func:r956_1, 0:r956_2, 1:r956_3, 2:r956_4 # 956| mu956_6(unknown) = ^CallSideEffect : ~m? # 956| mu956_7(unknown) = ^InitializeDynamicAllocation : &:r956_5 # 956| r956_8(Overaligned *) = Convert : r956_5 @@ -5229,7 +5229,7 @@ ir.cpp: # 959| mu959_5(int) = InitializeParameter[n] : &:r959_4 # 960| r960_1(glval) = FunctionAddress[operator new[]] : # 960| r960_2(unsigned long) = Constant[40] : -# 960| r960_3(void *) = Call : func:r960_1, 0:r960_2 +# 960| r960_3(void *) = Call[operator new[]] : func:r960_1, 0:r960_2 # 960| mu960_4(unknown) = ^CallSideEffect : ~m? # 960| mu960_5(unknown) = ^InitializeDynamicAllocation : &:r960_3 # 960| r960_6(int *) = Convert : r960_3 @@ -5239,7 +5239,7 @@ ir.cpp: # 961| r961_4(unsigned long) = Convert : r961_3 # 961| r961_5(unsigned long) = Constant[4] : # 961| r961_6(unsigned long) = Mul : r961_4, r961_5 -# 961| r961_7(void *) = Call : func:r961_1, 0:r961_6 +# 961| r961_7(void *) = Call[operator new[]] : func:r961_1, 0:r961_6 # 961| mu961_8(unknown) = ^CallSideEffect : ~m? # 961| mu961_9(unknown) = ^InitializeDynamicAllocation : &:r961_7 # 961| r961_10(int *) = Convert : r961_7 @@ -5250,7 +5250,7 @@ ir.cpp: # 962| r962_5(unsigned long) = Constant[4] : # 962| r962_6(unsigned long) = Mul : r962_4, r962_5 # 962| r962_7(float) = Constant[1.0] : -# 962| r962_8(void *) = Call : func:r962_1, 0:r962_6, 1:r962_7 +# 962| r962_8(void *) = Call[operator new[]] : func:r962_1, 0:r962_6, 1:r962_7 # 962| mu962_9(unknown) = ^CallSideEffect : ~m? # 962| mu962_10(unknown) = ^InitializeDynamicAllocation : &:r962_8 # 962| r962_11(int *) = Convert : r962_8 @@ -5260,7 +5260,7 @@ ir.cpp: # 963| r963_4(unsigned long) = Convert : r963_3 # 963| r963_5(unsigned long) = Constant[8] : # 963| r963_6(unsigned long) = Mul : r963_4, r963_5 -# 963| r963_7(void *) = Call : func:r963_1, 0:r963_6 +# 963| r963_7(void *) = Call[operator new[]] : func:r963_1, 0:r963_6 # 963| mu963_8(unknown) = ^CallSideEffect : ~m? # 963| mu963_9(unknown) = ^InitializeDynamicAllocation : &:r963_7 # 963| r963_10(String *) = Convert : r963_7 @@ -5271,7 +5271,7 @@ ir.cpp: # 964| r964_5(unsigned long) = Constant[256] : # 964| r964_6(unsigned long) = Mul : r964_4, r964_5 # 964| r964_7(align_val_t) = Constant[128] : -# 964| r964_8(void *) = Call : func:r964_1, 0:r964_6, 1:r964_7 +# 964| r964_8(void *) = Call[operator new[]] : func:r964_1, 0:r964_6, 1:r964_7 # 964| mu964_9(unknown) = ^CallSideEffect : ~m? # 964| mu964_10(unknown) = ^InitializeDynamicAllocation : &:r964_8 # 964| r964_11(Overaligned *) = Convert : r964_8 @@ -5279,7 +5279,7 @@ ir.cpp: # 965| r965_2(unsigned long) = Constant[2560] : # 965| r965_3(align_val_t) = Constant[128] : # 965| r965_4(float) = Constant[1.0] : -# 965| r965_5(void *) = Call : func:r965_1, 0:r965_2, 1:r965_3, 2:r965_4 +# 965| r965_5(void *) = Call[operator new[]] : func:r965_1, 0:r965_2, 1:r965_3, 2:r965_4 # 965| mu965_6(unknown) = ^CallSideEffect : ~m? # 965| mu965_7(unknown) = ^InitializeDynamicAllocation : &:r965_5 # 965| r965_8(Overaligned *) = Convert : r965_5 @@ -5289,7 +5289,7 @@ ir.cpp: # 966| r966_4(unsigned long) = Convert : r966_3 # 966| r966_5(unsigned long) = Constant[1] : # 966| r966_6(unsigned long) = Mul : r966_4, r966_5 -# 966| r966_7(void *) = Call : func:r966_1, 0:r966_6 +# 966| r966_7(void *) = Call[operator new[]] : func:r966_1, 0:r966_6 # 966| mu966_8(unknown) = ^CallSideEffect : ~m? # 966| mu966_9(unknown) = ^InitializeDynamicAllocation : &:r966_7 # 966| r966_10(DefaultCtorWithDefaultParam *) = Convert : r966_7 @@ -5299,7 +5299,7 @@ ir.cpp: # 967| r967_4(unsigned long) = Convert : r967_3 # 967| r967_5(unsigned long) = Constant[4] : # 967| r967_6(unsigned long) = Mul : r967_4, r967_5 -# 967| r967_7(void *) = Call : func:r967_1, 0:r967_6 +# 967| r967_7(void *) = Call[operator new[]] : func:r967_1, 0:r967_6 # 967| mu967_8(unknown) = ^CallSideEffect : ~m? # 967| mu967_9(unknown) = ^InitializeDynamicAllocation : &:r967_7 # 967| r967_10(int *) = Convert : r967_7 @@ -5522,7 +5522,7 @@ ir.cpp: # 997| r997_7(glval<..(*)(..)>) = VariableAddress[fn] : # 997| r997_8(..(*)(..)) = Load : &:r997_7, ~m? # 997| r997_9(float) = Constant[1.0] : -# 997| r997_10(int) = Call : func:r997_8, 0:r997_9 +# 997| r997_10(int) = Call[?] : func:r997_8, 0:r997_9 # 997| mu997_11(unknown) = ^CallSideEffect : ~m? # 997| r997_12(int) = Add : r997_6, r997_10 # 997| mu997_13(int) = Store : &:r997_1, r997_12 @@ -5704,7 +5704,7 @@ ir.cpp: # 1044| r1044_2(glval) = Convert : r1044_1 # 1044| r1044_3(glval) = FunctionAddress[operator()] : # 1044| r1044_4(float) = Constant[1.0] : -# 1044| r1044_5(char) = Call : func:r1044_3, this:r1044_2, 0:r1044_4 +# 1044| r1044_5(char) = Call[operator()] : func:r1044_3, this:r1044_2, 0:r1044_4 # 1044| mu1044_6(unknown) = ^CallSideEffect : ~m? # 1044| v1044_7(void) = ^BufferReadSideEffect[-1] : &:r1044_2, ~m? # 1044| mu1044_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1044_2 @@ -5713,7 +5713,7 @@ ir.cpp: # 1045| mu1045_3(decltype([...](...){...})) = Uninitialized[#temp1045:20] : &:r1045_2 # 1045| r1045_4(glval) = FieldAddress[s] : r1045_2 # 1045| r1045_5(glval) = FunctionAddress[String] : -# 1045| v1045_6(void) = Call : func:r1045_5, this:r1045_4 +# 1045| v1045_6(void) = Call[String] : func:r1045_5, this:r1045_4 # 1045| mu1045_7(unknown) = ^CallSideEffect : ~m? # 1045| mu1045_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r1045_4 # 1045| r1045_9(glval) = FieldAddress[x] : r1045_2 @@ -5726,7 +5726,7 @@ ir.cpp: # 1046| r1046_2(glval) = Convert : r1046_1 # 1046| r1046_3(glval) = FunctionAddress[operator()] : # 1046| r1046_4(float) = Constant[2.0] : -# 1046| r1046_5(char) = Call : func:r1046_3, this:r1046_2, 0:r1046_4 +# 1046| r1046_5(char) = Call[operator()] : func:r1046_3, this:r1046_2, 0:r1046_4 # 1046| mu1046_6(unknown) = ^CallSideEffect : ~m? # 1046| v1046_7(void) = ^BufferReadSideEffect[-1] : &:r1046_2, ~m? # 1046| mu1046_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1046_2 @@ -5745,7 +5745,7 @@ ir.cpp: # 1048| r1048_2(glval) = Convert : r1048_1 # 1048| r1048_3(glval) = FunctionAddress[operator()] : # 1048| r1048_4(float) = Constant[3.0] : -# 1048| r1048_5(char) = Call : func:r1048_3, this:r1048_2, 0:r1048_4 +# 1048| r1048_5(char) = Call[operator()] : func:r1048_3, this:r1048_2, 0:r1048_4 # 1048| mu1048_6(unknown) = ^CallSideEffect : ~m? # 1048| v1048_7(void) = ^BufferReadSideEffect[-1] : &:r1048_2, ~m? # 1048| mu1048_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1048_2 @@ -5754,7 +5754,7 @@ ir.cpp: # 1049| mu1049_3(decltype([...](...){...})) = Uninitialized[#temp1049:29] : &:r1049_2 # 1049| r1049_4(glval) = FieldAddress[s] : r1049_2 # 1049| r1049_5(glval) = FunctionAddress[String] : -# 1049| v1049_6(void) = Call : func:r1049_5, this:r1049_4 +# 1049| v1049_6(void) = Call[String] : func:r1049_5, this:r1049_4 # 1049| mu1049_7(unknown) = ^CallSideEffect : ~m? # 1049| mu1049_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r1049_4 # 1049| r1049_9(decltype([...](...){...})) = Load : &:r1049_2, ~m? @@ -5763,7 +5763,7 @@ ir.cpp: # 1050| r1050_2(glval) = Convert : r1050_1 # 1050| r1050_3(glval) = FunctionAddress[operator()] : # 1050| r1050_4(float) = Constant[4.0] : -# 1050| r1050_5(char) = Call : func:r1050_3, this:r1050_2, 0:r1050_4 +# 1050| r1050_5(char) = Call[operator()] : func:r1050_3, this:r1050_2, 0:r1050_4 # 1050| mu1050_6(unknown) = ^CallSideEffect : ~m? # 1050| v1050_7(void) = ^BufferReadSideEffect[-1] : &:r1050_2, ~m? # 1050| mu1050_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1050_2 @@ -5786,7 +5786,7 @@ ir.cpp: # 1052| r1052_2(glval) = Convert : r1052_1 # 1052| r1052_3(glval) = FunctionAddress[operator()] : # 1052| r1052_4(float) = Constant[5.0] : -# 1052| r1052_5(char) = Call : func:r1052_3, this:r1052_2, 0:r1052_4 +# 1052| r1052_5(char) = Call[operator()] : func:r1052_3, this:r1052_2, 0:r1052_4 # 1052| mu1052_6(unknown) = ^CallSideEffect : ~m? # 1052| v1052_7(void) = ^BufferReadSideEffect[-1] : &:r1052_2, ~m? # 1052| mu1052_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1052_2 @@ -5825,7 +5825,7 @@ ir.cpp: # 1055| r1055_2(glval) = Convert : r1055_1 # 1055| r1055_3(glval) = FunctionAddress[operator()] : # 1055| r1055_4(float) = Constant[6.0] : -# 1055| r1055_5(char) = Call : func:r1055_3, this:r1055_2, 0:r1055_4 +# 1055| r1055_5(char) = Call[operator()] : func:r1055_3, this:r1055_2, 0:r1055_4 # 1055| mu1055_6(unknown) = ^CallSideEffect : ~m? # 1055| v1055_7(void) = ^BufferReadSideEffect[-1] : &:r1055_2, ~m? # 1055| mu1055_8(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r1055_2 @@ -5891,7 +5891,7 @@ ir.cpp: # 1043| r1043_14(String &) = Load : &:r1043_13, ~m? # 1043| r1043_15(glval) = CopyValue : r1043_14 # 1043| r1043_16(glval) = FunctionAddress[c_str] : -# 1043| r1043_17(char *) = Call : func:r1043_16, this:r1043_15 +# 1043| r1043_17(char *) = Call[c_str] : func:r1043_16, this:r1043_15 # 1043| mu1043_18(unknown) = ^CallSideEffect : ~m? # 1043| v1043_19(void) = ^BufferReadSideEffect[-1] : &:r1043_15, ~m? # 1043| mu1043_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1043_15 @@ -5921,7 +5921,7 @@ ir.cpp: #-----| v0_1(void) = NoOp : # 1045| r1045_8(glval) = FieldAddress[s] : mu1045_5 # 1045| r1045_9(glval) = FunctionAddress[~String] : -# 1045| v1045_10(void) = Call : func:r1045_9, this:r1045_8 +# 1045| v1045_10(void) = Call[~String] : func:r1045_9, this:r1045_8 # 1045| mu1045_11(unknown) = ^CallSideEffect : ~m? # 1045| v1045_12(void) = ReturnIndirection[#this] : &:r1045_6, ~m? # 1045| v1045_13(void) = ReturnVoid : @@ -5944,7 +5944,7 @@ ir.cpp: # 1045| r1045_12(lambda [] type at line 1045, col. 21 *) = Load : &:r1045_11, ~m? # 1045| r1045_13(glval) = FieldAddress[s] : r1045_12 # 1045| r1045_14(glval) = FunctionAddress[c_str] : -# 1045| r1045_15(char *) = Call : func:r1045_14, this:r1045_13 +# 1045| r1045_15(char *) = Call[c_str] : func:r1045_14, this:r1045_13 # 1045| mu1045_16(unknown) = ^CallSideEffect : ~m? # 1045| v1045_17(void) = ^BufferReadSideEffect[-1] : &:r1045_13, ~m? # 1045| mu1045_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r1045_13 @@ -5979,7 +5979,7 @@ ir.cpp: # 1047| r1047_14(String &) = Load : &:r1047_13, ~m? # 1047| r1047_15(glval) = CopyValue : r1047_14 # 1047| r1047_16(glval) = FunctionAddress[c_str] : -# 1047| r1047_17(char *) = Call : func:r1047_16, this:r1047_15 +# 1047| r1047_17(char *) = Call[c_str] : func:r1047_16, this:r1047_15 # 1047| mu1047_18(unknown) = ^CallSideEffect : ~m? # 1047| v1047_19(void) = ^BufferReadSideEffect[-1] : &:r1047_15, ~m? # 1047| mu1047_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1047_15 @@ -6005,7 +6005,7 @@ ir.cpp: #-----| v0_1(void) = NoOp : # 1049| r1049_8(glval) = FieldAddress[s] : mu1049_5 # 1049| r1049_9(glval) = FunctionAddress[~String] : -# 1049| v1049_10(void) = Call : func:r1049_9, this:r1049_8 +# 1049| v1049_10(void) = Call[~String] : func:r1049_9, this:r1049_8 # 1049| mu1049_11(unknown) = ^CallSideEffect : ~m? # 1049| v1049_12(void) = ReturnIndirection[#this] : &:r1049_6, ~m? # 1049| v1049_13(void) = ReturnVoid : @@ -6028,7 +6028,7 @@ ir.cpp: # 1049| r1049_12(lambda [] type at line 1049, col. 30 *) = Load : &:r1049_11, ~m? # 1049| r1049_13(glval) = FieldAddress[s] : r1049_12 # 1049| r1049_14(glval) = FunctionAddress[c_str] : -# 1049| r1049_15(char *) = Call : func:r1049_14, this:r1049_13 +# 1049| r1049_15(char *) = Call[c_str] : func:r1049_14, this:r1049_13 # 1049| mu1049_16(unknown) = ^CallSideEffect : ~m? # 1049| v1049_17(void) = ^BufferReadSideEffect[-1] : &:r1049_13, ~m? # 1049| mu1049_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r1049_13 @@ -6060,7 +6060,7 @@ ir.cpp: # 1051| r1051_14(String &) = Load : &:r1051_13, ~m? # 1051| r1051_15(glval) = CopyValue : r1051_14 # 1051| r1051_16(glval) = FunctionAddress[c_str] : -# 1051| r1051_17(char *) = Call : func:r1051_16, this:r1051_15 +# 1051| r1051_17(char *) = Call[c_str] : func:r1051_16, this:r1051_15 # 1051| mu1051_18(unknown) = ^CallSideEffect : ~m? # 1051| v1051_19(void) = ^BufferReadSideEffect[-1] : &:r1051_15, ~m? # 1051| mu1051_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1051_15 @@ -6095,7 +6095,7 @@ ir.cpp: # 1054| r1054_14(String &) = Load : &:r1054_13, ~m? # 1054| r1054_15(glval) = CopyValue : r1054_14 # 1054| r1054_16(glval) = FunctionAddress[c_str] : -# 1054| r1054_17(char *) = Call : func:r1054_16, this:r1054_15 +# 1054| r1054_17(char *) = Call[c_str] : func:r1054_16, this:r1054_15 # 1054| mu1054_18(unknown) = ^CallSideEffect : ~m? # 1054| v1054_19(void) = ^BufferReadSideEffect[-1] : &:r1054_15, ~m? # 1054| mu1054_20(String) = ^IndirectMayWriteSideEffect[-1] : &:r1054_15 @@ -6143,7 +6143,7 @@ ir.cpp: # 1078| r1078_9(vector &) = Load : &:r1078_8, ~m? #-----| r0_1(glval>) = CopyValue : r1078_9 # 1078| r1078_10(glval) = FunctionAddress[begin] : -# 1078| r1078_11(iterator) = Call : func:r1078_10, this:r0_1 +# 1078| r1078_11(iterator) = Call[begin] : func:r1078_10, this:r0_1 # 1078| mu1078_12(unknown) = ^CallSideEffect : ~m? #-----| v0_2(void) = ^BufferReadSideEffect[-1] : &:r0_1, ~m? #-----| mu0_3(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_1 @@ -6153,7 +6153,7 @@ ir.cpp: # 1078| r1078_16(vector &) = Load : &:r1078_15, ~m? #-----| r0_4(glval>) = CopyValue : r1078_16 # 1078| r1078_17(glval) = FunctionAddress[end] : -# 1078| r1078_18(iterator) = Call : func:r1078_17, this:r0_4 +# 1078| r1078_18(iterator) = Call[end] : func:r1078_17, this:r0_4 # 1078| mu1078_19(unknown) = ^CallSideEffect : ~m? #-----| v0_5(void) = ^BufferReadSideEffect[-1] : &:r0_4, ~m? #-----| mu0_6(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_4 @@ -6166,7 +6166,7 @@ ir.cpp: # 1084| r1084_2(glval) = FunctionAddress[operator!=] : # 1084| r1084_3(glval) = VariableAddress[(__end)] : # 1084| r1084_4(iterator) = Load : &:r1084_3, ~m? -# 1084| r1084_5(bool) = Call : func:r1084_2, this:r0_7, 0:r1084_4 +# 1084| r1084_5(bool) = Call[operator!=] : func:r1084_2, this:r0_7, 0:r1084_4 # 1084| mu1084_6(unknown) = ^CallSideEffect : ~m? #-----| v0_8(void) = ^BufferReadSideEffect[-1] : &:r0_7, ~m? #-----| mu0_9(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_7 @@ -6177,7 +6177,7 @@ ir.cpp: # 1084| Block 2 # 1084| r1084_8(glval) = VariableAddress[(__begin)] : # 1084| r1084_9(glval) = FunctionAddress[operator++] : -# 1084| r1084_10(iterator &) = Call : func:r1084_9, this:r1084_8 +# 1084| r1084_10(iterator &) = Call[operator++] : func:r1084_9, this:r1084_8 # 1084| mu1084_11(unknown) = ^CallSideEffect : ~m? # 1084| v1084_12(void) = ^BufferReadSideEffect[-1] : &:r1084_8, ~m? # 1084| mu1084_13(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r1084_8 @@ -6189,7 +6189,7 @@ ir.cpp: # 1084| r1084_16(glval) = VariableAddress[(__begin)] : #-----| r0_10(glval) = Convert : r1084_16 # 1084| r1084_17(glval) = FunctionAddress[operator*] : -# 1084| r1084_18(int &) = Call : func:r1084_17, this:r0_10 +# 1084| r1084_18(int &) = Call[operator*] : func:r1084_17, this:r0_10 # 1084| mu1084_19(unknown) = ^CallSideEffect : ~m? #-----| v0_11(void) = ^BufferReadSideEffect[-1] : &:r0_10, ~m? #-----| mu0_12(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_10 @@ -6224,7 +6224,7 @@ ir.cpp: # 1078| r1078_22(glval) = FunctionAddress[operator!=] : # 1078| r1078_23(glval) = VariableAddress[(__end)] : # 1078| r1078_24(iterator) = Load : &:r1078_23, ~m? -# 1078| r1078_25(bool) = Call : func:r1078_22, this:r0_13, 0:r1078_24 +# 1078| r1078_25(bool) = Call[operator!=] : func:r1078_22, this:r0_13, 0:r1078_24 # 1078| mu1078_26(unknown) = ^CallSideEffect : ~m? #-----| v0_14(void) = ^BufferReadSideEffect[-1] : &:r0_13, ~m? #-----| mu0_15(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_13 @@ -6237,7 +6237,7 @@ ir.cpp: # 1078| r1078_29(glval) = VariableAddress[(__begin)] : #-----| r0_16(glval) = Convert : r1078_29 # 1078| r1078_30(glval) = FunctionAddress[operator*] : -# 1078| r1078_31(int &) = Call : func:r1078_30, this:r0_16 +# 1078| r1078_31(int &) = Call[operator*] : func:r1078_30, this:r0_16 # 1078| mu1078_32(unknown) = ^CallSideEffect : ~m? #-----| v0_17(void) = ^BufferReadSideEffect[-1] : &:r0_16, ~m? #-----| mu0_18(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r0_16 @@ -6259,7 +6259,7 @@ ir.cpp: # 1078| v1078_35(void) = NoOp : # 1078| r1078_36(glval) = VariableAddress[(__begin)] : # 1078| r1078_37(glval) = FunctionAddress[operator++] : -# 1078| r1078_38(iterator &) = Call : func:r1078_37, this:r1078_36 +# 1078| r1078_38(iterator &) = Call[operator++] : func:r1078_37, this:r1078_36 # 1078| mu1078_39(unknown) = ^CallSideEffect : ~m? # 1078| v1078_40(void) = ^BufferReadSideEffect[-1] : &:r1078_36, ~m? # 1078| mu1078_41(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r1078_36 @@ -6278,7 +6278,7 @@ ir.cpp: # 1084| r1084_32(vector &) = Load : &:r1084_31, ~m? #-----| r0_19(glval>) = CopyValue : r1084_32 # 1084| r1084_33(glval) = FunctionAddress[begin] : -# 1084| r1084_34(iterator) = Call : func:r1084_33, this:r0_19 +# 1084| r1084_34(iterator) = Call[begin] : func:r1084_33, this:r0_19 # 1084| mu1084_35(unknown) = ^CallSideEffect : ~m? #-----| v0_20(void) = ^BufferReadSideEffect[-1] : &:r0_19, ~m? #-----| mu0_21(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_19 @@ -6288,7 +6288,7 @@ ir.cpp: # 1084| r1084_39(vector &) = Load : &:r1084_38, ~m? #-----| r0_22(glval>) = CopyValue : r1084_39 # 1084| r1084_40(glval) = FunctionAddress[end] : -# 1084| r1084_41(iterator) = Call : func:r1084_40, this:r0_22 +# 1084| r1084_41(iterator) = Call[end] : func:r1084_40, this:r0_22 # 1084| mu1084_42(unknown) = ^CallSideEffect : ~m? #-----| v0_23(void) = ^BufferReadSideEffect[-1] : &:r0_22, ~m? #-----| mu0_24(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_22 @@ -6460,7 +6460,7 @@ ir.cpp: # 1149| r1149_13(glval) = FunctionAddress[String] : # 1149| r1149_14(glval) = StringConstant["String object"] : # 1149| r1149_15(char *) = Convert : r1149_14 -# 1149| v1149_16(void) = Call : func:r1149_13, this:r1149_11, 0:r1149_15 +# 1149| v1149_16(void) = Call[String] : func:r1149_13, this:r1149_11, 0:r1149_15 # 1149| mu1149_17(unknown) = ^CallSideEffect : ~m? # 1149| mu1149_18(String) = ^IndirectMayWriteSideEffect[-1] : &:r1149_11 # 1149| v1149_19(void) = ^BufferReadSideEffect[0] : &:r1149_15, ~m? @@ -6489,7 +6489,7 @@ ir.cpp: # 1154| r1154_3(glval) = FunctionAddress[String] : # 1154| r1154_4(glval) = VariableAddress[s] : # 1154| r1154_5(char *) = Load : &:r1154_4, ~m? -# 1154| v1154_6(void) = Call : func:r1154_3, this:r1154_1, 0:r1154_5 +# 1154| v1154_6(void) = Call[String] : func:r1154_3, this:r1154_1, 0:r1154_5 # 1154| mu1154_7(unknown) = ^CallSideEffect : ~m? # 1154| mu1154_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r1154_1 # 1154| v1154_9(void) = ^BufferReadSideEffect[0] : &:r1154_5, ~m? @@ -6594,7 +6594,7 @@ ir.cpp: # 1174| r1174_6(int *) = CopyValue : r1174_5 # 1174| r1174_7(void *) = Convert : r1174_6 # 1174| r1174_8(int) = Constant[4] : -# 1174| r1174_9(void *) = Call : func:r1174_1, 0:r1174_4, 1:r1174_7, 2:r1174_8 +# 1174| r1174_9(void *) = Call[memcpy] : func:r1174_1, 0:r1174_4, 1:r1174_7, 2:r1174_8 # 1174| v1174_10(void) = ^SizedBufferReadSideEffect[1] : &:r1174_7, r1174_8, ~m? # 1174| mu1174_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r1174_4, r1174_8 # 1175| r1175_1(glval) = VariableAddress[#return] : @@ -6616,7 +6616,7 @@ ir.cpp: # 1179| r1179_3(glval) = FunctionAddress[String] : # 1179| r1179_4(glval) = StringConstant["foo"] : # 1179| r1179_5(char *) = Convert : r1179_4 -# 1179| r1179_6(String) = Call : func:r1179_3, this:r1179_1, 0:r1179_5 +# 1179| r1179_6(String) = Call[String] : func:r1179_3, this:r1179_1, 0:r1179_5 # 1179| mu1179_7(unknown) = ^CallSideEffect : ~m? # 1179| mu1179_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r1179_1 # 1179| v1179_9(void) = ^BufferReadSideEffect[0] : &:r1179_5, ~m? @@ -6862,7 +6862,7 @@ ir.cpp: # 1242| r1242_5(glval) = FunctionAddress[String] : # 1242| r1242_6(glval) = StringConstant["static"] : # 1242| r1242_7(char *) = Convert : r1242_6 -# 1242| v1242_8(void) = Call : func:r1242_5, this:r1242_4, 0:r1242_7 +# 1242| v1242_8(void) = Call[String] : func:r1242_5, this:r1242_4, 0:r1242_7 # 1242| mu1242_9(unknown) = ^CallSideEffect : ~m? # 1242| mu1242_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r1242_4 # 1242| v1242_11(void) = ^BufferReadSideEffect[0] : &:r1242_7, ~m? @@ -6883,7 +6883,7 @@ ir.cpp: # 1243| r1243_5(glval) = FunctionAddress[String] : # 1243| r1243_6(glval) = VariableAddress[dynamic] : # 1243| r1243_7(char *) = Load : &:r1243_6, ~m? -# 1243| v1243_8(void) = Call : func:r1243_5, this:r1243_4, 0:r1243_7 +# 1243| v1243_8(void) = Call[String] : func:r1243_5, this:r1243_4, 0:r1243_7 # 1243| mu1243_9(unknown) = ^CallSideEffect : ~m? # 1243| mu1243_10(String) = ^IndirectMayWriteSideEffect[-1] : &:r1243_4 # 1243| v1243_11(void) = ^BufferReadSideEffect[0] : &:r1243_7, ~m? @@ -6902,7 +6902,7 @@ ir.cpp: # 1241| Block 6 # 1241| r1241_4(glval) = VariableAddress[a] : #-----| r0_1(glval) = FunctionAddress[String] : -#-----| v0_2(void) = Call : func:r0_1, this:r1241_4 +#-----| v0_2(void) = Call[String] : func:r0_1, this:r1241_4 #-----| mu0_3(unknown) = ^CallSideEffect : ~m? #-----| mu0_4(String) = ^IndirectMayWriteSideEffect[-1] : &:r1241_4 # 1241| r1241_5(bool) = Constant[1] : @@ -6938,7 +6938,7 @@ ir.cpp: # 1254| r1254_4(glval) = VariableAddress[s1] : # 1254| r1254_5(char *) = Load : &:r1254_4, ~m? # 1254| r1254_6(char *) = Convert : r1254_5 -# 1254| r1254_7(char *) = Call : func:r1254_1, 0:r1254_3, 1:r1254_6 +# 1254| r1254_7(char *) = Call[strcpy] : func:r1254_1, 0:r1254_3, 1:r1254_6 # 1254| v1254_8(void) = ^BufferReadSideEffect[1] : &:r1254_6, ~m? # 1254| mu1254_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r1254_3 # 1255| r1255_1(glval) = FunctionAddress[strcat] : @@ -6947,7 +6947,7 @@ ir.cpp: # 1255| r1255_4(glval) = VariableAddress[s2] : # 1255| r1255_5(char *) = Load : &:r1255_4, ~m? # 1255| r1255_6(char *) = Convert : r1255_5 -# 1255| r1255_7(char *) = Call : func:r1255_1, 0:r1255_3, 1:r1255_6 +# 1255| r1255_7(char *) = Call[strcat] : func:r1255_1, 0:r1255_3, 1:r1255_6 # 1255| v1255_8(void) = ^BufferReadSideEffect[0] : &:r1255_3, ~m? # 1255| v1255_9(void) = ^BufferReadSideEffect[1] : &:r1255_6, ~m? # 1255| mu1255_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1255_3 @@ -6995,17 +6995,17 @@ ir.cpp: # 1271| r1271_1(glval) = VariableAddress[c] : # 1271| mu1271_2(C) = Uninitialized[c] : &:r1271_1 # 1271| r1271_3(glval) = FunctionAddress[C] : -# 1271| v1271_4(void) = Call : func:r1271_3, this:r1271_1 +# 1271| v1271_4(void) = Call[C] : func:r1271_3, this:r1271_1 # 1271| mu1271_5(unknown) = ^CallSideEffect : ~m? # 1271| mu1271_6(C) = ^IndirectMayWriteSideEffect[-1] : &:r1271_1 # 1272| r1272_1(glval) = VariableAddress[c] : # 1272| r1272_2(glval) = FunctionAddress[StaticMemberFunction] : # 1272| r1272_3(int) = Constant[10] : -# 1272| r1272_4(int) = Call : func:r1272_2, 0:r1272_3 +# 1272| r1272_4(int) = Call[StaticMemberFunction] : func:r1272_2, 0:r1272_3 # 1272| mu1272_5(unknown) = ^CallSideEffect : ~m? # 1273| r1273_1(glval) = FunctionAddress[StaticMemberFunction] : # 1273| r1273_2(int) = Constant[10] : -# 1273| r1273_3(int) = Call : func:r1273_1, 0:r1273_2 +# 1273| r1273_3(int) = Call[StaticMemberFunction] : func:r1273_1, 0:r1273_2 # 1273| mu1273_4(unknown) = ^CallSideEffect : ~m? # 1275| r1275_1(glval) = VariableAddress[a] : # 1275| mu1275_2(A) = Uninitialized[a] : &:r1275_1 @@ -7015,7 +7015,7 @@ ir.cpp: # 1276| r1276_4(A *) = CopyValue : r1276_3 # 1276| r1276_5(glval) = VariableAddress[int_arg] : # 1276| r1276_6(int) = Load : &:r1276_5, ~m? -# 1276| v1276_7(void) = Call : func:r1276_2, 0:r1276_4, 1:r1276_6 +# 1276| v1276_7(void) = Call[static_member] : func:r1276_2, 0:r1276_4, 1:r1276_6 # 1276| mu1276_8(unknown) = ^CallSideEffect : ~m? # 1276| v1276_9(void) = ^BufferReadSideEffect[0] : &:r1276_4, ~m? # 1276| mu1276_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1276_4 @@ -7024,7 +7024,7 @@ ir.cpp: # 1277| r1277_3(A *) = CopyValue : r1277_2 # 1277| r1277_4(glval) = VariableAddress[int_arg] : # 1277| r1277_5(int) = Load : &:r1277_4, ~m? -# 1277| v1277_6(void) = Call : func:r1277_1, 0:r1277_3, 1:r1277_5 +# 1277| v1277_6(void) = Call[static_member] : func:r1277_1, 0:r1277_3, 1:r1277_5 # 1277| mu1277_7(unknown) = ^CallSideEffect : ~m? # 1277| v1277_8(void) = ^BufferReadSideEffect[0] : &:r1277_3, ~m? # 1277| mu1277_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r1277_3 @@ -7037,7 +7037,7 @@ ir.cpp: # 1279| r1279_7(int) = Load : &:r1279_6, ~m? # 1279| r1279_8(int) = Constant[2] : # 1279| r1279_9(int) = Add : r1279_7, r1279_8 -# 1279| v1279_10(void) = Call : func:r1279_3, 0:r1279_5, 1:r1279_9 +# 1279| v1279_10(void) = Call[static_member] : func:r1279_3, 0:r1279_5, 1:r1279_9 # 1279| mu1279_11(unknown) = ^CallSideEffect : ~m? # 1279| v1279_12(void) = ^BufferReadSideEffect[0] : &:r1279_5, ~m? # 1279| mu1279_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r1279_5 @@ -7048,7 +7048,7 @@ ir.cpp: # 1280| r1280_5(glval) = VariableAddress[a] : # 1280| r1280_6(A *) = CopyValue : r1280_5 # 1280| r1280_7(int) = Constant[99] : -# 1280| v1280_8(void) = Call : func:r1280_4, 0:r1280_6, 1:r1280_7 +# 1280| v1280_8(void) = Call[static_member] : func:r1280_4, 0:r1280_6, 1:r1280_7 # 1280| mu1280_9(unknown) = ^CallSideEffect : ~m? # 1280| v1280_10(void) = ^BufferReadSideEffect[0] : &:r1280_6, ~m? # 1280| mu1280_11(unknown) = ^BufferMayWriteSideEffect[0] : &:r1280_6 @@ -7058,22 +7058,22 @@ ir.cpp: # 1281| r1281_4(glval) = VariableAddress[a_arg] : # 1281| r1281_5(A *) = Load : &:r1281_4, ~m? # 1281| r1281_6(int) = Constant[-1] : -# 1281| v1281_7(void) = Call : func:r1281_3, 0:r1281_5, 1:r1281_6 +# 1281| v1281_7(void) = Call[static_member] : func:r1281_3, 0:r1281_5, 1:r1281_6 # 1281| mu1281_8(unknown) = ^CallSideEffect : ~m? # 1281| v1281_9(void) = ^BufferReadSideEffect[0] : &:r1281_5, ~m? # 1281| mu1281_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1281_5 # 1283| r1283_1(glval) = VariableAddress[a] : # 1283| r1283_2(glval) = FunctionAddress[static_member_without_def] : -# 1283| v1283_3(void) = Call : func:r1283_2 +# 1283| v1283_3(void) = Call[static_member_without_def] : func:r1283_2 # 1283| mu1283_4(unknown) = ^CallSideEffect : ~m? # 1284| r1284_1(glval) = FunctionAddress[static_member_without_def] : -# 1284| v1284_2(void) = Call : func:r1284_1 +# 1284| v1284_2(void) = Call[static_member_without_def] : func:r1284_1 # 1284| mu1284_3(unknown) = ^CallSideEffect : ~m? # 1286| r1286_1(glval) = FunctionAddress[getAnInstanceOfA] : -# 1286| r1286_2(A *) = Call : func:r1286_1 +# 1286| r1286_2(A *) = Call[getAnInstanceOfA] : func:r1286_1 # 1286| mu1286_3(unknown) = ^CallSideEffect : ~m? # 1286| r1286_4(glval) = FunctionAddress[static_member_without_def] : -# 1286| v1286_5(void) = Call : func:r1286_4 +# 1286| v1286_5(void) = Call[static_member_without_def] : func:r1286_4 # 1286| mu1286_6(unknown) = ^CallSideEffect : ~m? # 1287| v1287_1(void) = NoOp : # 1270| v1270_10(void) = ReturnIndirection[a_arg] : &:r1270_8, ~m? @@ -7123,7 +7123,7 @@ ir.cpp: # 1296| r1296_3(int) = Load : &:r1296_2, ~m? # 1296| r1296_4(glval) = VariableAddress[y] : # 1296| r1296_5(int) = Load : &:r1296_4, ~m? -# 1296| v1296_6(void) = Call : func:r1296_1, 0:r1296_3, 1:r1296_5 +# 1296| v1296_6(void) = Call[IntegerOps] : func:r1296_1, 0:r1296_3, 1:r1296_5 # 1296| mu1296_7(unknown) = ^CallSideEffect : ~m? # 1296| v1296_8(void) = NoOp : # 1295| v1295_8(void) = ReturnVoid : @@ -7372,7 +7372,7 @@ ir.cpp: # 1314| mu1314_7(int) = InitializeParameter[y] : &:r1314_6 # 1315| r1315_1(glval) = VariableAddress[#return] : # 1315| r1315_2(glval) = FunctionAddress[predicateA] : -# 1315| r1315_3(bool) = Call : func:r1315_2 +# 1315| r1315_3(bool) = Call[predicateA] : func:r1315_2 # 1315| mu1315_4(unknown) = ^CallSideEffect : ~m? # 1315| v1315_5(void) = ConditionalBranch : r1315_3 #-----| False -> Block 3 @@ -7380,7 +7380,7 @@ ir.cpp: # 1315| Block 1 # 1315| r1315_6(glval) = FunctionAddress[predicateB] : -# 1315| r1315_7(bool) = Call : func:r1315_6 +# 1315| r1315_7(bool) = Call[predicateB] : func:r1315_6 # 1315| mu1315_8(unknown) = ^CallSideEffect : ~m? # 1315| v1315_9(void) = ConditionalBranch : r1315_7 #-----| False -> Block 3 @@ -7423,7 +7423,7 @@ ir.cpp: # 1322| r1322_3(glval) = VariableAddress[p] : # 1322| r1322_4(int *) = Load : &:r1322_3, ~m? # 1322| r1322_5(void *) = Convert : r1322_4 -# 1322| r1322_6(void *) = Call : func:r1322_1, 0:r1322_2, 1:r1322_5 +# 1322| r1322_6(void *) = Call[operator new] : func:r1322_1, 0:r1322_2, 1:r1322_5 # 1322| mu1322_7(unknown) = ^CallSideEffect : ~m? # 1322| mu1322_8(unknown) = ^InitializeDynamicAllocation : &:r1322_6 # 1322| r1322_9(int *) = Convert : r1322_6 @@ -7462,12 +7462,12 @@ perf-regression.cpp: # 10| r10_1(glval) = VariableAddress[big] : # 10| r10_2(glval) = FunctionAddress[operator new] : # 10| r10_3(unsigned long) = Constant[1073741824] : -# 10| r10_4(void *) = Call : func:r10_2, 0:r10_3 +# 10| r10_4(void *) = Call[operator new] : func:r10_2, 0:r10_3 # 10| mu10_5(unknown) = ^CallSideEffect : ~m? # 10| mu10_6(unknown) = ^InitializeDynamicAllocation : &:r10_4 # 10| r10_7(Big *) = Convert : r10_4 # 10| r10_8(glval) = FunctionAddress[Big] : -# 10| v10_9(void) = Call : func:r10_8, this:r10_7 +# 10| v10_9(void) = Call[Big] : func:r10_8, this:r10_7 # 10| mu10_10(unknown) = ^CallSideEffect : ~m? # 10| mu10_11(Big) = ^IndirectMayWriteSideEffect[-1] : &:r10_7 # 10| mu10_12(Big *) = Store : &:r10_1, r10_7 @@ -7507,7 +7507,7 @@ struct_init.cpp: # 25| r25_1(glval) = FunctionAddress[let_info_escape] : # 25| r25_2(glval) = VariableAddress[static_infos] : # 25| r25_3(Info *) = Convert : r25_2 -# 25| v25_4(void) = Call : func:r25_1, 0:r25_3 +# 25| v25_4(void) = Call[let_info_escape] : func:r25_1, 0:r25_3 # 25| mu25_5(unknown) = ^CallSideEffect : ~m? # 25| v25_6(void) = ^BufferReadSideEffect[0] : &:r25_3, ~m? # 25| mu25_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r25_3 @@ -7545,7 +7545,7 @@ struct_init.cpp: # 33| r33_1(glval) = FunctionAddress[let_info_escape] : # 33| r33_2(glval) = VariableAddress[local_infos] : # 33| r33_3(Info *) = Convert : r33_2 -# 33| v33_4(void) = Call : func:r33_1, 0:r33_3 +# 33| v33_4(void) = Call[let_info_escape] : func:r33_1, 0:r33_3 # 33| mu33_5(unknown) = ^CallSideEffect : ~m? # 33| v33_6(void) = ^BufferReadSideEffect[0] : &:r33_3, ~m? # 33| mu33_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r33_3 @@ -7573,7 +7573,7 @@ struct_init.cpp: # 41| r41_1(glval) = FunctionAddress[let_info_escape] : # 41| r41_2(glval) = VariableAddress[static_infos] : # 41| r41_3(Info *) = Convert : r41_2 -# 41| v41_4(void) = Call : func:r41_1, 0:r41_3 +# 41| v41_4(void) = Call[let_info_escape] : func:r41_1, 0:r41_3 # 41| mu41_5(unknown) = ^CallSideEffect : ~m? # 41| v41_6(void) = ^BufferReadSideEffect[0] : &:r41_3, ~m? # 41| mu41_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r41_3 diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index 43b8116d85f..ae579bbbbdc 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -346,7 +346,7 @@ ssa.cpp: # 97| r97_2(glval) = VariableAddress[a] : # 97| r97_3(Point *) = CopyValue : r97_2 # 97| r97_4(void *) = Convert : r97_3 -# 97| v97_5(void) = Call : func:r97_1, 0:r97_4 +# 97| v97_5(void) = Call[Escape] : func:r97_1, 0:r97_4 # 97| m97_6(unknown) = ^CallSideEffect : ~m95_7 # 97| m97_7(unknown) = Chi : total:m95_7, partial:m97_6 # 97| v97_8(void) = ^BufferReadSideEffect[0] : &:r97_4, ~m97_7 @@ -403,7 +403,7 @@ ssa.cpp: # 108| r108_2(glval) = VariableAddress[a] : # 108| r108_3(Point *) = CopyValue : r108_2 # 108| r108_4(void *) = Convert : r108_3 -# 108| v108_5(void) = Call : func:r108_1, 0:r108_4 +# 108| v108_5(void) = Call[Escape] : func:r108_1, 0:r108_4 # 108| m108_6(unknown) = ^CallSideEffect : ~m105_7 # 108| m108_7(unknown) = Chi : total:m105_7, partial:m108_6 # 108| v108_8(void) = ^BufferReadSideEffect[0] : &:r108_4, ~m108_7 @@ -476,7 +476,7 @@ ssa.cpp: # 119| r119_2(glval) = VariableAddress[a] : # 119| r119_3(Point *) = CopyValue : r119_2 # 119| r119_4(void *) = Convert : r119_3 -# 119| v119_5(void) = Call : func:r119_1, 0:r119_4 +# 119| v119_5(void) = Call[Escape] : func:r119_1, 0:r119_4 # 119| m119_6(unknown) = ^CallSideEffect : ~m117_13 # 119| m119_7(unknown) = Chi : total:m117_13, partial:m119_6 # 119| v119_8(void) = ^BufferReadSideEffect[0] : &:r119_4, ~m119_7 @@ -848,7 +848,7 @@ ssa.cpp: # 199| r199_6(glval) = VariableAddress[str2] : # 199| r199_7(char *) = Load : &:r199_6, m198_10 # 199| r199_8(char *) = Convert : r199_7 -# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8 +# 199| r199_9(int) = Call[strcmp] : func:r199_2, 0:r199_5, 1:r199_8 # 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_8 # 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_12 # 199| m199_12(int) = Store : &:r199_1, r199_9 @@ -856,7 +856,7 @@ ssa.cpp: # 200| r200_2(glval) = VariableAddress[str1] : # 200| r200_3(char *) = Load : &:r200_2, m198_6 # 200| r200_4(char *) = Convert : r200_3 -# 200| r200_5(int) = Call : func:r200_1, 0:r200_4 +# 200| r200_5(int) = Call[strlen] : func:r200_1, 0:r200_4 # 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_8 # 200| r200_7(glval) = VariableAddress[ret] : # 200| r200_8(int) = Load : &:r200_7, m199_12 @@ -865,7 +865,7 @@ ssa.cpp: # 201| r201_1(glval) = FunctionAddress[abs] : # 201| r201_2(glval) = VariableAddress[x] : # 201| r201_3(int) = Load : &:r201_2, m198_14 -# 201| r201_4(int) = Call : func:r201_1, 0:r201_3 +# 201| r201_4(int) = Call[abs] : func:r201_1, 0:r201_3 # 201| r201_5(glval) = VariableAddress[ret] : # 201| r201_6(int) = Load : &:r201_5, m200_10 # 201| r201_7(int) = Add : r201_6, r201_4 @@ -901,7 +901,7 @@ ssa.cpp: # 209| r209_6(int *) = CopyValue : r209_5 # 209| r209_7(void *) = Convert : r209_6 # 209| r209_8(int) = Constant[4] : -# 209| r209_9(void *) = Call : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 +# 209| r209_9(void *) = Call[memcpy] : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 # 209| v209_10(void) = ^SizedBufferReadSideEffect[1] : &:r209_7, r209_8, ~m207_6 # 209| m209_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r209_4, r209_8 # 209| m209_12(unknown) = Chi : total:m208_3, partial:m209_11 @@ -988,7 +988,7 @@ ssa.cpp: # 226| m226_3(unknown) = InitializeNonLocal : # 226| m226_4(unknown) = Chi : total:m226_2, partial:m226_3 # 227| r227_1(glval) = FunctionAddress[ExternalFunc] : -# 227| v227_2(void) = Call : func:r227_1 +# 227| v227_2(void) = Call[ExternalFunc] : func:r227_1 # 227| m227_3(unknown) = ^CallSideEffect : ~m226_4 # 227| m227_4(unknown) = Chi : total:m226_4, partial:m227_3 # 229| r229_1(glval) = VariableAddress[s] : @@ -1051,14 +1051,14 @@ ssa.cpp: # 240| m240_2(Constructible) = Uninitialized[c] : &:r240_1 # 240| r240_3(glval) = FunctionAddress[Constructible] : # 240| r240_4(int) = Constant[1] : -# 240| v240_5(void) = Call : func:r240_3, this:r240_1, 0:r240_4 +# 240| v240_5(void) = Call[Constructible] : func:r240_3, this:r240_1, 0:r240_4 # 240| m240_6(unknown) = ^CallSideEffect : ~m239_4 # 240| m240_7(unknown) = Chi : total:m239_4, partial:m240_6 # 240| m240_8(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r240_1 # 240| m240_9(Constructible) = Chi : total:m240_2, partial:m240_8 # 241| r241_1(glval) = VariableAddress[c] : # 241| r241_2(glval) = FunctionAddress[g] : -# 241| v241_3(void) = Call : func:r241_2, this:r241_1 +# 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| m241_4(unknown) = ^CallSideEffect : ~m240_7 # 241| m241_5(unknown) = Chi : total:m240_7, partial:m241_4 # 241| v241_6(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m240_9 @@ -1066,7 +1066,7 @@ ssa.cpp: # 241| m241_8(Constructible) = Chi : total:m240_9, partial:m241_7 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : -# 242| v242_3(void) = Call : func:r242_2, this:r242_1 +# 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| m242_4(unknown) = ^CallSideEffect : ~m241_5 # 242| m242_5(unknown) = Chi : total:m241_5, partial:m242_4 # 242| v242_6(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m241_8 @@ -1076,14 +1076,14 @@ ssa.cpp: # 243| m243_2(Constructible) = Uninitialized[c2] : &:r243_1 # 243| r243_3(glval) = FunctionAddress[Constructible] : # 243| r243_4(int) = Constant[2] : -# 243| v243_5(void) = Call : func:r243_3, this:r243_1, 0:r243_4 +# 243| v243_5(void) = Call[Constructible] : func:r243_3, this:r243_1, 0:r243_4 # 243| m243_6(unknown) = ^CallSideEffect : ~m242_5 # 243| m243_7(unknown) = Chi : total:m242_5, partial:m243_6 # 243| m243_8(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r243_1 # 243| m243_9(Constructible) = Chi : total:m243_2, partial:m243_8 # 244| r244_1(glval) = VariableAddress[c2] : # 244| r244_2(glval) = FunctionAddress[g] : -# 244| v244_3(void) = Call : func:r244_2, this:r244_1 +# 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| m244_4(unknown) = ^CallSideEffect : ~m243_7 # 244| m244_5(unknown) = Chi : total:m243_7, partial:m244_4 # 244| v244_6(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m243_9 @@ -1114,7 +1114,7 @@ ssa.cpp: # 248| r248_5(unsigned long) = Convert : r248_4 # 248| r248_6(unsigned long) = Constant[1] : # 248| r248_7(unsigned long) = Mul : r248_5, r248_6 -# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7 +# 248| r248_8(void *) = Call[operator new[]] : func:r248_2, 0:r248_7 # 248| m248_9(unknown) = ^CallSideEffect : ~m247_9 # 248| m248_10(unknown) = Chi : total:m247_9, partial:m248_9 # 248| m248_11(unknown) = ^InitializeDynamicAllocation : &:r248_8 @@ -1136,7 +1136,7 @@ ssa.cpp: # 250| r250_7(void *) = Convert : r250_6 # 250| r250_8(glval) = VariableAddress[size] : # 250| r250_9(int) = Load : &:r250_8, m247_11 -# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 +# 250| r250_10(void *) = Call[memcpy] : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 # 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6 # 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9 # 250| m250_13(unknown) = Chi : total:m249_6, partial:m250_12 @@ -1166,14 +1166,14 @@ ssa.cpp: # 256| Block 1 # 256| r256_1(glval) = FunctionAddress[ExternalFunc] : -# 256| v256_2(void) = Call : func:r256_1 +# 256| v256_2(void) = Call[ExternalFunc] : func:r256_1 # 256| m256_3(unknown) = ^CallSideEffect : ~m254_4 # 256| m256_4(unknown) = Chi : total:m254_4, partial:m256_3 #-----| Goto -> Block 3 # 259| Block 2 # 259| r259_1(glval) = FunctionAddress[ExternalFunc] : -# 259| v259_2(void) = Call : func:r259_1 +# 259| v259_2(void) = Call[ExternalFunc] : func:r259_1 # 259| m259_3(unknown) = ^CallSideEffect : ~m254_4 # 259| m259_4(unknown) = Chi : total:m254_4, partial:m259_3 #-----| Goto -> Block 3 @@ -1213,7 +1213,7 @@ ssa.cpp: # 269| r269_2(glval) = FunctionAddress[malloc] : # 269| r269_3(glval) = VariableAddress[size] : # 269| r269_4(int) = Load : &:r269_3, m268_11 -# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4 +# 269| r269_5(void *) = Call[malloc] : func:r269_2, 0:r269_4 # 269| m269_6(unknown) = ^CallSideEffect : ~m268_9 # 269| m269_7(unknown) = Chi : total:m268_9, partial:m269_6 # 269| m269_8(unknown) = ^InitializeDynamicAllocation : &:r269_5 @@ -1226,7 +1226,7 @@ ssa.cpp: # 270| r270_5(void *) = Load : &:r270_4, m268_6 # 270| r270_6(glval) = VariableAddress[size] : # 270| r270_7(int) = Load : &:r270_6, m268_11 -# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 +# 270| r270_8(void *) = Call[memcpy] : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 # 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m269_7 # 270| m270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7 # 270| m270_11(unknown) = Chi : total:m269_9, partial:m270_10 @@ -1362,7 +1362,7 @@ ssa.cpp: # 292| r292_1(glval) = VariableAddress[p] : # 292| r292_2(glval) = FunctionAddress[operator new] : # 292| r292_3(unsigned long) = Constant[8] : -# 292| r292_4(void *) = Call : func:r292_2, 0:r292_3 +# 292| r292_4(void *) = Call[operator new] : func:r292_2, 0:r292_3 # 292| m292_5(unknown) = ^CallSideEffect : ~m291_4 # 292| m292_6(unknown) = Chi : total:m291_4, partial:m292_5 # 292| m292_7(unknown) = ^InitializeDynamicAllocation : &:r292_4 @@ -1371,7 +1371,7 @@ ssa.cpp: # 293| r293_1(glval) = VariableAddress[q] : # 293| r293_2(glval) = FunctionAddress[operator new] : # 293| r293_3(unsigned long) = Constant[8] : -# 293| r293_4(void *) = Call : func:r293_2, 0:r293_3 +# 293| r293_4(void *) = Call[operator new] : func:r293_2, 0:r293_3 # 293| m293_5(unknown) = ^CallSideEffect : ~m292_6 # 293| m293_6(unknown) = Chi : total:m292_6, partial:m293_5 # 293| m293_7(unknown) = ^InitializeDynamicAllocation : &:r293_4 @@ -1380,7 +1380,7 @@ ssa.cpp: # 294| r294_1(glval) = VariableAddress[j] : # 294| r294_2(glval) = FunctionAddress[operator new] : # 294| r294_3(unsigned long) = Constant[4] : -# 294| r294_4(void *) = Call : func:r294_2, 0:r294_3 +# 294| r294_4(void *) = Call[operator new] : func:r294_2, 0:r294_3 # 294| m294_5(unknown) = ^CallSideEffect : ~m293_6 # 294| m294_6(unknown) = Chi : total:m293_6, partial:m294_5 # 294| m294_7(unknown) = ^InitializeDynamicAllocation : &:r294_4 @@ -1388,7 +1388,7 @@ ssa.cpp: # 294| r294_9(glval) = FunctionAddress[A] : # 294| r294_10(glval) = FunctionAddress[operator new] : # 294| r294_11(unsigned long) = Constant[4] : -# 294| r294_12(void *) = Call : func:r294_10, 0:r294_11 +# 294| r294_12(void *) = Call[operator new] : func:r294_10, 0:r294_11 # 294| m294_13(unknown) = ^CallSideEffect : ~m294_6 # 294| m294_14(unknown) = Chi : total:m294_6, partial:m294_13 # 294| m294_15(unknown) = ^InitializeDynamicAllocation : &:r294_12 @@ -1396,12 +1396,12 @@ ssa.cpp: # 294| r294_17(glval) = FunctionAddress[A] : # 294| r294_18(glval) = VariableAddress[x] : # 294| r294_19(int) = Load : &:r294_18, m291_6 -# 294| v294_20(void) = Call : func:r294_17, this:r294_16, 0:r294_19 +# 294| v294_20(void) = Call[A] : func:r294_17, this:r294_16, 0:r294_19 # 294| m294_21(unknown) = ^CallSideEffect : ~m294_14 # 294| m294_22(unknown) = Chi : total:m294_14, partial:m294_21 # 294| m294_23(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_16 # 294| m294_24(unknown) = Chi : total:m294_15, partial:m294_23 -# 294| v294_25(void) = Call : func:r294_9, this:r294_8, 0:r294_16 +# 294| v294_25(void) = Call[A] : func:r294_9, this:r294_8, 0:r294_16 # 294| m294_26(unknown) = ^CallSideEffect : ~m294_22 # 294| m294_27(unknown) = Chi : total:m294_22, partial:m294_26 # 294| m294_28(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_8 @@ -1415,13 +1415,13 @@ ssa.cpp: # 295| r295_1(glval) = VariableAddress[a] : # 295| r295_2(glval) = FunctionAddress[operator new] : # 295| r295_3(unsigned long) = Constant[4] : -# 295| r295_4(void *) = Call : func:r295_2, 0:r295_3 +# 295| r295_4(void *) = Call[operator new] : func:r295_2, 0:r295_3 # 295| m295_5(unknown) = ^CallSideEffect : ~m294_27 # 295| m295_6(unknown) = Chi : total:m294_27, partial:m295_5 # 295| m295_7(unknown) = ^InitializeDynamicAllocation : &:r295_4 # 295| r295_8(A *) = Convert : r295_4 # 295| r295_9(glval) = FunctionAddress[A] : -# 295| v295_10(void) = Call : func:r295_9, this:r295_8 +# 295| v295_10(void) = Call[A] : func:r295_9, this:r295_8 # 295| m295_11(unknown) = ^CallSideEffect : ~m295_6 # 295| m295_12(unknown) = Chi : total:m295_6, partial:m295_11 # 295| m295_13(A) = ^IndirectMayWriteSideEffect[-1] : &:r295_8 @@ -1454,7 +1454,7 @@ ssa.cpp: # 302| r302_3(int) = Load : &:r302_2, m301_6 # 302| r302_4(glval) = VariableAddress[argv] : # 302| r302_5(char **) = Load : &:r302_4, m301_8 -# 302| v302_6(void) = Call : func:r302_1, 0:r302_3, 1:r302_5 +# 302| v302_6(void) = Call[unknownFunction] : func:r302_1, 0:r302_3, 1:r302_5 # 302| m302_7(unknown) = ^CallSideEffect : ~m301_11 # 302| m302_8(unknown) = Chi : total:m301_11, partial:m302_7 # 302| v302_9(void) = ^BufferReadSideEffect[1] : &:r302_5, ~m302_8 @@ -1465,7 +1465,7 @@ ssa.cpp: # 303| r303_3(int) = Load : &:r303_2, m301_6 # 303| r303_4(glval) = VariableAddress[argv] : # 303| r303_5(char **) = Load : &:r303_4, m301_8 -# 303| v303_6(void) = Call : func:r303_1, 0:r303_3, 1:r303_5 +# 303| v303_6(void) = Call[unknownFunction] : func:r303_1, 0:r303_3, 1:r303_5 # 303| m303_7(unknown) = ^CallSideEffect : ~m302_11 # 303| m303_8(unknown) = Chi : total:m302_11, partial:m303_7 # 303| v303_9(void) = ^BufferReadSideEffect[1] : &:r303_5, ~m303_8 diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected index ef07fde174d..0e486a79289 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir_unsound.expected @@ -345,7 +345,7 @@ ssa.cpp: # 97| r97_2(glval) = VariableAddress[a] : # 97| r97_3(Point *) = CopyValue : r97_2 # 97| r97_4(void *) = Convert : r97_3 -# 97| v97_5(void) = Call : func:r97_1, 0:r97_4 +# 97| v97_5(void) = Call[Escape] : func:r97_1, 0:r97_4 # 97| m97_6(unknown) = ^CallSideEffect : ~m95_4 # 97| m97_7(unknown) = Chi : total:m95_4, partial:m97_6 # 97| v97_8(void) = ^BufferReadSideEffect[0] : &:r97_4, ~m95_6 @@ -401,7 +401,7 @@ ssa.cpp: # 108| r108_2(glval) = VariableAddress[a] : # 108| r108_3(Point *) = CopyValue : r108_2 # 108| r108_4(void *) = Convert : r108_3 -# 108| v108_5(void) = Call : func:r108_1, 0:r108_4 +# 108| v108_5(void) = Call[Escape] : func:r108_1, 0:r108_4 # 108| m108_6(unknown) = ^CallSideEffect : ~m105_4 # 108| m108_7(unknown) = Chi : total:m105_4, partial:m108_6 # 108| v108_8(void) = ^BufferReadSideEffect[0] : &:r108_4, ~m105_6 @@ -473,7 +473,7 @@ ssa.cpp: # 119| r119_2(glval) = VariableAddress[a] : # 119| r119_3(Point *) = CopyValue : r119_2 # 119| r119_4(void *) = Convert : r119_3 -# 119| v119_5(void) = Call : func:r119_1, 0:r119_4 +# 119| v119_5(void) = Call[Escape] : func:r119_1, 0:r119_4 # 119| m119_6(unknown) = ^CallSideEffect : ~m116_4 # 119| m119_7(unknown) = Chi : total:m116_4, partial:m119_6 # 119| v119_8(void) = ^BufferReadSideEffect[0] : &:r119_4, ~m117_12 @@ -843,7 +843,7 @@ ssa.cpp: # 199| r199_6(glval) = VariableAddress[str2] : # 199| r199_7(char *) = Load : &:r199_6, m198_10 # 199| r199_8(char *) = Convert : r199_7 -# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8 +# 199| r199_9(int) = Call[strcmp] : func:r199_2, 0:r199_5, 1:r199_8 # 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_8 # 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_12 # 199| m199_12(int) = Store : &:r199_1, r199_9 @@ -851,7 +851,7 @@ ssa.cpp: # 200| r200_2(glval) = VariableAddress[str1] : # 200| r200_3(char *) = Load : &:r200_2, m198_6 # 200| r200_4(char *) = Convert : r200_3 -# 200| r200_5(int) = Call : func:r200_1, 0:r200_4 +# 200| r200_5(int) = Call[strlen] : func:r200_1, 0:r200_4 # 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_8 # 200| r200_7(glval) = VariableAddress[ret] : # 200| r200_8(int) = Load : &:r200_7, m199_12 @@ -860,7 +860,7 @@ ssa.cpp: # 201| r201_1(glval) = FunctionAddress[abs] : # 201| r201_2(glval) = VariableAddress[x] : # 201| r201_3(int) = Load : &:r201_2, m198_14 -# 201| r201_4(int) = Call : func:r201_1, 0:r201_3 +# 201| r201_4(int) = Call[abs] : func:r201_1, 0:r201_3 # 201| r201_5(glval) = VariableAddress[ret] : # 201| r201_6(int) = Load : &:r201_5, m200_10 # 201| r201_7(int) = Add : r201_6, r201_4 @@ -894,7 +894,7 @@ ssa.cpp: # 209| r209_6(int *) = CopyValue : r209_5 # 209| r209_7(void *) = Convert : r209_6 # 209| r209_8(int) = Constant[4] : -# 209| r209_9(void *) = Call : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 +# 209| r209_9(void *) = Call[memcpy] : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 # 209| v209_10(void) = ^SizedBufferReadSideEffect[1] : &:r209_7, r209_8, ~m207_6 # 209| m209_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r209_4, r209_8 # 209| m209_12(int) = Chi : total:m208_2, partial:m209_11 @@ -981,7 +981,7 @@ ssa.cpp: # 226| m226_3(unknown) = InitializeNonLocal : # 226| m226_4(unknown) = Chi : total:m226_2, partial:m226_3 # 227| r227_1(glval) = FunctionAddress[ExternalFunc] : -# 227| v227_2(void) = Call : func:r227_1 +# 227| v227_2(void) = Call[ExternalFunc] : func:r227_1 # 227| m227_3(unknown) = ^CallSideEffect : ~m226_4 # 227| m227_4(unknown) = Chi : total:m226_4, partial:m227_3 # 229| r229_1(glval) = VariableAddress[s] : @@ -1044,14 +1044,14 @@ ssa.cpp: # 240| m240_2(Constructible) = Uninitialized[c] : &:r240_1 # 240| r240_3(glval) = FunctionAddress[Constructible] : # 240| r240_4(int) = Constant[1] : -# 240| v240_5(void) = Call : func:r240_3, this:r240_1, 0:r240_4 +# 240| v240_5(void) = Call[Constructible] : func:r240_3, this:r240_1, 0:r240_4 # 240| m240_6(unknown) = ^CallSideEffect : ~m239_4 # 240| m240_7(unknown) = Chi : total:m239_4, partial:m240_6 # 240| m240_8(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r240_1 # 240| m240_9(Constructible) = Chi : total:m240_2, partial:m240_8 # 241| r241_1(glval) = VariableAddress[c] : # 241| r241_2(glval) = FunctionAddress[g] : -# 241| v241_3(void) = Call : func:r241_2, this:r241_1 +# 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| m241_4(unknown) = ^CallSideEffect : ~m240_7 # 241| m241_5(unknown) = Chi : total:m240_7, partial:m241_4 # 241| v241_6(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m240_9 @@ -1059,7 +1059,7 @@ ssa.cpp: # 241| m241_8(Constructible) = Chi : total:m240_9, partial:m241_7 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : -# 242| v242_3(void) = Call : func:r242_2, this:r242_1 +# 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| m242_4(unknown) = ^CallSideEffect : ~m241_5 # 242| m242_5(unknown) = Chi : total:m241_5, partial:m242_4 # 242| v242_6(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m241_8 @@ -1069,14 +1069,14 @@ ssa.cpp: # 243| m243_2(Constructible) = Uninitialized[c2] : &:r243_1 # 243| r243_3(glval) = FunctionAddress[Constructible] : # 243| r243_4(int) = Constant[2] : -# 243| v243_5(void) = Call : func:r243_3, this:r243_1, 0:r243_4 +# 243| v243_5(void) = Call[Constructible] : func:r243_3, this:r243_1, 0:r243_4 # 243| m243_6(unknown) = ^CallSideEffect : ~m242_5 # 243| m243_7(unknown) = Chi : total:m242_5, partial:m243_6 # 243| m243_8(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r243_1 # 243| m243_9(Constructible) = Chi : total:m243_2, partial:m243_8 # 244| r244_1(glval) = VariableAddress[c2] : # 244| r244_2(glval) = FunctionAddress[g] : -# 244| v244_3(void) = Call : func:r244_2, this:r244_1 +# 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| m244_4(unknown) = ^CallSideEffect : ~m243_7 # 244| m244_5(unknown) = Chi : total:m243_7, partial:m244_4 # 244| v244_6(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m243_9 @@ -1106,7 +1106,7 @@ ssa.cpp: # 248| r248_5(unsigned long) = Convert : r248_4 # 248| r248_6(unsigned long) = Constant[1] : # 248| r248_7(unsigned long) = Mul : r248_5, r248_6 -# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7 +# 248| r248_8(void *) = Call[operator new[]] : func:r248_2, 0:r248_7 # 248| m248_9(unknown) = ^CallSideEffect : ~m247_4 # 248| m248_10(unknown) = Chi : total:m247_4, partial:m248_9 # 248| m248_11(unknown) = ^InitializeDynamicAllocation : &:r248_8 @@ -1127,7 +1127,7 @@ ssa.cpp: # 250| r250_7(void *) = Convert : r250_6 # 250| r250_8(glval) = VariableAddress[size] : # 250| r250_9(int) = Load : &:r250_8, m247_10 -# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 +# 250| r250_10(void *) = Call[memcpy] : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 # 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6 # 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9 # 250| m250_13(unknown) = Chi : total:m248_11, partial:m250_12 @@ -1157,14 +1157,14 @@ ssa.cpp: # 256| Block 1 # 256| r256_1(glval) = FunctionAddress[ExternalFunc] : -# 256| v256_2(void) = Call : func:r256_1 +# 256| v256_2(void) = Call[ExternalFunc] : func:r256_1 # 256| m256_3(unknown) = ^CallSideEffect : ~m254_4 # 256| m256_4(unknown) = Chi : total:m254_4, partial:m256_3 #-----| Goto -> Block 3 # 259| Block 2 # 259| r259_1(glval) = FunctionAddress[ExternalFunc] : -# 259| v259_2(void) = Call : func:r259_1 +# 259| v259_2(void) = Call[ExternalFunc] : func:r259_1 # 259| m259_3(unknown) = ^CallSideEffect : ~m254_4 # 259| m259_4(unknown) = Chi : total:m254_4, partial:m259_3 #-----| Goto -> Block 3 @@ -1203,7 +1203,7 @@ ssa.cpp: # 269| r269_2(glval) = FunctionAddress[malloc] : # 269| r269_3(glval) = VariableAddress[size] : # 269| r269_4(int) = Load : &:r269_3, m268_10 -# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4 +# 269| r269_5(void *) = Call[malloc] : func:r269_2, 0:r269_4 # 269| m269_6(unknown) = ^CallSideEffect : ~m268_4 # 269| m269_7(unknown) = Chi : total:m268_4, partial:m269_6 # 269| m269_8(unknown) = ^InitializeDynamicAllocation : &:r269_5 @@ -1215,7 +1215,7 @@ ssa.cpp: # 270| r270_5(void *) = Load : &:r270_4, m268_6 # 270| r270_6(glval) = VariableAddress[size] : # 270| r270_7(int) = Load : &:r270_6, m268_10 -# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 +# 270| r270_8(void *) = Call[memcpy] : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 # 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m268_8 # 270| m270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7 # 270| m270_11(unknown) = Chi : total:m269_8, partial:m270_10 @@ -1350,7 +1350,7 @@ ssa.cpp: # 292| r292_1(glval) = VariableAddress[p] : # 292| r292_2(glval) = FunctionAddress[operator new] : # 292| r292_3(unsigned long) = Constant[8] : -# 292| r292_4(void *) = Call : func:r292_2, 0:r292_3 +# 292| r292_4(void *) = Call[operator new] : func:r292_2, 0:r292_3 # 292| m292_5(unknown) = ^CallSideEffect : ~m291_4 # 292| m292_6(unknown) = Chi : total:m291_4, partial:m292_5 # 292| m292_7(unknown) = ^InitializeDynamicAllocation : &:r292_4 @@ -1359,7 +1359,7 @@ ssa.cpp: # 293| r293_1(glval) = VariableAddress[q] : # 293| r293_2(glval) = FunctionAddress[operator new] : # 293| r293_3(unsigned long) = Constant[8] : -# 293| r293_4(void *) = Call : func:r293_2, 0:r293_3 +# 293| r293_4(void *) = Call[operator new] : func:r293_2, 0:r293_3 # 293| m293_5(unknown) = ^CallSideEffect : ~m292_6 # 293| m293_6(unknown) = Chi : total:m292_6, partial:m293_5 # 293| m293_7(unknown) = ^InitializeDynamicAllocation : &:r293_4 @@ -1368,7 +1368,7 @@ ssa.cpp: # 294| r294_1(glval) = VariableAddress[j] : # 294| r294_2(glval) = FunctionAddress[operator new] : # 294| r294_3(unsigned long) = Constant[4] : -# 294| r294_4(void *) = Call : func:r294_2, 0:r294_3 +# 294| r294_4(void *) = Call[operator new] : func:r294_2, 0:r294_3 # 294| m294_5(unknown) = ^CallSideEffect : ~m293_6 # 294| m294_6(unknown) = Chi : total:m293_6, partial:m294_5 # 294| m294_7(unknown) = ^InitializeDynamicAllocation : &:r294_4 @@ -1376,7 +1376,7 @@ ssa.cpp: # 294| r294_9(glval) = FunctionAddress[A] : # 294| r294_10(glval) = FunctionAddress[operator new] : # 294| r294_11(unsigned long) = Constant[4] : -# 294| r294_12(void *) = Call : func:r294_10, 0:r294_11 +# 294| r294_12(void *) = Call[operator new] : func:r294_10, 0:r294_11 # 294| m294_13(unknown) = ^CallSideEffect : ~m294_6 # 294| m294_14(unknown) = Chi : total:m294_6, partial:m294_13 # 294| m294_15(unknown) = ^InitializeDynamicAllocation : &:r294_12 @@ -1384,12 +1384,12 @@ ssa.cpp: # 294| r294_17(glval) = FunctionAddress[A] : # 294| r294_18(glval) = VariableAddress[x] : # 294| r294_19(int) = Load : &:r294_18, m291_6 -# 294| v294_20(void) = Call : func:r294_17, this:r294_16, 0:r294_19 +# 294| v294_20(void) = Call[A] : func:r294_17, this:r294_16, 0:r294_19 # 294| m294_21(unknown) = ^CallSideEffect : ~m294_14 # 294| m294_22(unknown) = Chi : total:m294_14, partial:m294_21 # 294| m294_23(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_16 # 294| m294_24(unknown) = Chi : total:m294_15, partial:m294_23 -# 294| v294_25(void) = Call : func:r294_9, this:r294_8, 0:r294_16 +# 294| v294_25(void) = Call[A] : func:r294_9, this:r294_8, 0:r294_16 # 294| m294_26(unknown) = ^CallSideEffect : ~m294_22 # 294| m294_27(unknown) = Chi : total:m294_22, partial:m294_26 # 294| m294_28(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_8 @@ -1403,13 +1403,13 @@ ssa.cpp: # 295| r295_1(glval) = VariableAddress[a] : # 295| r295_2(glval) = FunctionAddress[operator new] : # 295| r295_3(unsigned long) = Constant[4] : -# 295| r295_4(void *) = Call : func:r295_2, 0:r295_3 +# 295| r295_4(void *) = Call[operator new] : func:r295_2, 0:r295_3 # 295| m295_5(unknown) = ^CallSideEffect : ~m294_27 # 295| m295_6(unknown) = Chi : total:m294_27, partial:m295_5 # 295| m295_7(unknown) = ^InitializeDynamicAllocation : &:r295_4 # 295| r295_8(A *) = Convert : r295_4 # 295| r295_9(glval) = FunctionAddress[A] : -# 295| v295_10(void) = Call : func:r295_9, this:r295_8 +# 295| v295_10(void) = Call[A] : func:r295_9, this:r295_8 # 295| m295_11(unknown) = ^CallSideEffect : ~m295_6 # 295| m295_12(unknown) = Chi : total:m295_6, partial:m295_11 # 295| m295_13(A) = ^IndirectMayWriteSideEffect[-1] : &:r295_8 @@ -1441,7 +1441,7 @@ ssa.cpp: # 302| r302_3(int) = Load : &:r302_2, m301_6 # 302| r302_4(glval) = VariableAddress[argv] : # 302| r302_5(char **) = Load : &:r302_4, m301_8 -# 302| v302_6(void) = Call : func:r302_1, 0:r302_3, 1:r302_5 +# 302| v302_6(void) = Call[unknownFunction] : func:r302_1, 0:r302_3, 1:r302_5 # 302| m302_7(unknown) = ^CallSideEffect : ~m301_4 # 302| m302_8(unknown) = Chi : total:m301_4, partial:m302_7 # 302| v302_9(void) = ^BufferReadSideEffect[1] : &:r302_5, ~m301_10 @@ -1452,7 +1452,7 @@ ssa.cpp: # 303| r303_3(int) = Load : &:r303_2, m301_6 # 303| r303_4(glval) = VariableAddress[argv] : # 303| r303_5(char **) = Load : &:r303_4, m301_8 -# 303| v303_6(void) = Call : func:r303_1, 0:r303_3, 1:r303_5 +# 303| v303_6(void) = Call[unknownFunction] : func:r303_1, 0:r303_3, 1:r303_5 # 303| m303_7(unknown) = ^CallSideEffect : ~m302_8 # 303| m303_8(unknown) = Chi : total:m302_8, partial:m303_7 # 303| v303_9(void) = ^BufferReadSideEffect[1] : &:r303_5, ~m302_11 diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index 1d155eaf30d..9157b211d5b 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -334,7 +334,7 @@ ssa.cpp: # 97| r97_2(glval) = VariableAddress[a] : # 97| r97_3(Point *) = CopyValue : r97_2 # 97| r97_4(void *) = Convert : r97_3 -# 97| v97_5(void) = Call : func:r97_1, 0:r97_4 +# 97| v97_5(void) = Call[Escape] : func:r97_1, 0:r97_4 # 97| mu97_6(unknown) = ^CallSideEffect : ~m? # 97| v97_7(void) = ^BufferReadSideEffect[0] : &:r97_4, ~m? # 97| mu97_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r97_4 @@ -386,7 +386,7 @@ ssa.cpp: # 108| r108_2(glval) = VariableAddress[a] : # 108| r108_3(Point *) = CopyValue : r108_2 # 108| r108_4(void *) = Convert : r108_3 -# 108| v108_5(void) = Call : func:r108_1, 0:r108_4 +# 108| v108_5(void) = Call[Escape] : func:r108_1, 0:r108_4 # 108| mu108_6(unknown) = ^CallSideEffect : ~m? # 108| v108_7(void) = ^BufferReadSideEffect[0] : &:r108_4, ~m? # 108| mu108_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r108_4 @@ -450,7 +450,7 @@ ssa.cpp: # 119| r119_2(glval) = VariableAddress[a] : # 119| r119_3(Point *) = CopyValue : r119_2 # 119| r119_4(void *) = Convert : r119_3 -# 119| v119_5(void) = Call : func:r119_1, 0:r119_4 +# 119| v119_5(void) = Call[Escape] : func:r119_1, 0:r119_4 # 119| mu119_6(unknown) = ^CallSideEffect : ~m? # 119| v119_7(void) = ^BufferReadSideEffect[0] : &:r119_4, ~m? # 119| mu119_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r119_4 @@ -789,7 +789,7 @@ ssa.cpp: # 199| r199_6(glval) = VariableAddress[str2] : # 199| r199_7(char *) = Load : &:r199_6, m198_9 # 199| r199_8(char *) = Convert : r199_7 -# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8 +# 199| r199_9(int) = Call[strcmp] : func:r199_2, 0:r199_5, 1:r199_8 # 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m? # 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m? # 199| m199_12(int) = Store : &:r199_1, r199_9 @@ -797,7 +797,7 @@ ssa.cpp: # 200| r200_2(glval) = VariableAddress[str1] : # 200| r200_3(char *) = Load : &:r200_2, m198_5 # 200| r200_4(char *) = Convert : r200_3 -# 200| r200_5(int) = Call : func:r200_1, 0:r200_4 +# 200| r200_5(int) = Call[strlen] : func:r200_1, 0:r200_4 # 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m? # 200| r200_7(glval) = VariableAddress[ret] : # 200| r200_8(int) = Load : &:r200_7, m199_12 @@ -806,7 +806,7 @@ ssa.cpp: # 201| r201_1(glval) = FunctionAddress[abs] : # 201| r201_2(glval) = VariableAddress[x] : # 201| r201_3(int) = Load : &:r201_2, m198_13 -# 201| r201_4(int) = Call : func:r201_1, 0:r201_3 +# 201| r201_4(int) = Call[abs] : func:r201_1, 0:r201_3 # 201| r201_5(glval) = VariableAddress[ret] : # 201| r201_6(int) = Load : &:r201_5, m200_10 # 201| r201_7(int) = Add : r201_6, r201_4 @@ -839,7 +839,7 @@ ssa.cpp: # 209| r209_6(int *) = CopyValue : r209_5 # 209| r209_7(void *) = Convert : r209_6 # 209| r209_8(int) = Constant[4] : -# 209| r209_9(void *) = Call : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 +# 209| r209_9(void *) = Call[memcpy] : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 # 209| v209_10(void) = ^SizedBufferReadSideEffect[1] : &:r209_7, r209_8, ~m? # 209| mu209_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r209_4, r209_8 # 210| r210_1(glval) = VariableAddress[#return] : @@ -917,7 +917,7 @@ ssa.cpp: # 226| mu226_2(unknown) = AliasedDefinition : # 226| mu226_3(unknown) = InitializeNonLocal : # 227| r227_1(glval) = FunctionAddress[ExternalFunc] : -# 227| v227_2(void) = Call : func:r227_1 +# 227| v227_2(void) = Call[ExternalFunc] : func:r227_1 # 227| mu227_3(unknown) = ^CallSideEffect : ~m? # 229| r229_1(glval) = VariableAddress[s] : # 229| r229_2(glval) = StringConstant["Literal"] : @@ -976,18 +976,18 @@ ssa.cpp: # 240| mu240_2(Constructible) = Uninitialized[c] : &:r240_1 # 240| r240_3(glval) = FunctionAddress[Constructible] : # 240| r240_4(int) = Constant[1] : -# 240| v240_5(void) = Call : func:r240_3, this:r240_1, 0:r240_4 +# 240| v240_5(void) = Call[Constructible] : func:r240_3, this:r240_1, 0:r240_4 # 240| mu240_6(unknown) = ^CallSideEffect : ~m? # 240| mu240_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r240_1 # 241| r241_1(glval) = VariableAddress[c] : # 241| r241_2(glval) = FunctionAddress[g] : -# 241| v241_3(void) = Call : func:r241_2, this:r241_1 +# 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| mu241_4(unknown) = ^CallSideEffect : ~m? # 241| v241_5(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m? # 241| mu241_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : -# 242| v242_3(void) = Call : func:r242_2, this:r242_1 +# 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| mu242_4(unknown) = ^CallSideEffect : ~m? # 242| v242_5(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m? # 242| mu242_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 @@ -995,12 +995,12 @@ ssa.cpp: # 243| mu243_2(Constructible) = Uninitialized[c2] : &:r243_1 # 243| r243_3(glval) = FunctionAddress[Constructible] : # 243| r243_4(int) = Constant[2] : -# 243| v243_5(void) = Call : func:r243_3, this:r243_1, 0:r243_4 +# 243| v243_5(void) = Call[Constructible] : func:r243_3, this:r243_1, 0:r243_4 # 243| mu243_6(unknown) = ^CallSideEffect : ~m? # 243| mu243_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r243_1 # 244| r244_1(glval) = VariableAddress[c2] : # 244| r244_2(glval) = FunctionAddress[g] : -# 244| v244_3(void) = Call : func:r244_2, this:r244_1 +# 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| mu244_4(unknown) = ^CallSideEffect : ~m? # 244| v244_5(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m? # 244| mu244_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 @@ -1027,7 +1027,7 @@ ssa.cpp: # 248| r248_5(unsigned long) = Convert : r248_4 # 248| r248_6(unsigned long) = Constant[1] : # 248| r248_7(unsigned long) = Mul : r248_5, r248_6 -# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7 +# 248| r248_8(void *) = Call[operator new[]] : func:r248_2, 0:r248_7 # 248| mu248_9(unknown) = ^CallSideEffect : ~m? # 248| mu248_10(unknown) = ^InitializeDynamicAllocation : &:r248_8 # 248| r248_11(char *) = Convert : r248_8 @@ -1046,7 +1046,7 @@ ssa.cpp: # 250| r250_7(void *) = Convert : r250_6 # 250| r250_8(glval) = VariableAddress[size] : # 250| r250_9(int) = Load : &:r250_8, m247_9 -# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 +# 250| r250_10(void *) = Call[memcpy] : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 # 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m? # 250| mu250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9 # 251| r251_1(glval) = VariableAddress[#return] : @@ -1074,13 +1074,13 @@ ssa.cpp: # 256| Block 1 # 256| r256_1(glval) = FunctionAddress[ExternalFunc] : -# 256| v256_2(void) = Call : func:r256_1 +# 256| v256_2(void) = Call[ExternalFunc] : func:r256_1 # 256| mu256_3(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 3 # 259| Block 2 # 259| r259_1(glval) = FunctionAddress[ExternalFunc] : -# 259| v259_2(void) = Call : func:r259_1 +# 259| v259_2(void) = Call[ExternalFunc] : func:r259_1 # 259| mu259_3(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 3 @@ -1116,7 +1116,7 @@ ssa.cpp: # 269| r269_2(glval) = FunctionAddress[malloc] : # 269| r269_3(glval) = VariableAddress[size] : # 269| r269_4(int) = Load : &:r269_3, m268_9 -# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4 +# 269| r269_5(void *) = Call[malloc] : func:r269_2, 0:r269_4 # 269| mu269_6(unknown) = ^CallSideEffect : ~m? # 269| mu269_7(unknown) = ^InitializeDynamicAllocation : &:r269_5 # 269| m269_8(void *) = Store : &:r269_1, r269_5 @@ -1127,7 +1127,7 @@ ssa.cpp: # 270| r270_5(void *) = Load : &:r270_4, m268_5 # 270| r270_6(glval) = VariableAddress[size] : # 270| r270_7(int) = Load : &:r270_6, m268_9 -# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 +# 270| r270_8(void *) = Call[memcpy] : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 # 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m? # 270| mu270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7 # 271| r271_1(glval) = VariableAddress[#return] : @@ -1250,7 +1250,7 @@ ssa.cpp: # 292| r292_1(glval) = VariableAddress[p] : # 292| r292_2(glval) = FunctionAddress[operator new] : # 292| r292_3(unsigned long) = Constant[8] : -# 292| r292_4(void *) = Call : func:r292_2, 0:r292_3 +# 292| r292_4(void *) = Call[operator new] : func:r292_2, 0:r292_3 # 292| mu292_5(unknown) = ^CallSideEffect : ~m? # 292| mu292_6(unknown) = ^InitializeDynamicAllocation : &:r292_4 # 292| r292_7(Point *) = Convert : r292_4 @@ -1258,7 +1258,7 @@ ssa.cpp: # 293| r293_1(glval) = VariableAddress[q] : # 293| r293_2(glval) = FunctionAddress[operator new] : # 293| r293_3(unsigned long) = Constant[8] : -# 293| r293_4(void *) = Call : func:r293_2, 0:r293_3 +# 293| r293_4(void *) = Call[operator new] : func:r293_2, 0:r293_3 # 293| mu293_5(unknown) = ^CallSideEffect : ~m? # 293| mu293_6(unknown) = ^InitializeDynamicAllocation : &:r293_4 # 293| r293_7(Point *) = Convert : r293_4 @@ -1266,24 +1266,24 @@ ssa.cpp: # 294| r294_1(glval) = VariableAddress[j] : # 294| r294_2(glval) = FunctionAddress[operator new] : # 294| r294_3(unsigned long) = Constant[4] : -# 294| r294_4(void *) = Call : func:r294_2, 0:r294_3 +# 294| r294_4(void *) = Call[operator new] : func:r294_2, 0:r294_3 # 294| mu294_5(unknown) = ^CallSideEffect : ~m? # 294| mu294_6(unknown) = ^InitializeDynamicAllocation : &:r294_4 # 294| r294_7(A *) = Convert : r294_4 # 294| r294_8(glval) = FunctionAddress[A] : # 294| r294_9(glval) = FunctionAddress[operator new] : # 294| r294_10(unsigned long) = Constant[4] : -# 294| r294_11(void *) = Call : func:r294_9, 0:r294_10 +# 294| r294_11(void *) = Call[operator new] : func:r294_9, 0:r294_10 # 294| mu294_12(unknown) = ^CallSideEffect : ~m? # 294| mu294_13(unknown) = ^InitializeDynamicAllocation : &:r294_11 # 294| r294_14(A *) = Convert : r294_11 # 294| r294_15(glval) = FunctionAddress[A] : # 294| r294_16(glval) = VariableAddress[x] : # 294| r294_17(int) = Load : &:r294_16, m291_5 -# 294| v294_18(void) = Call : func:r294_15, this:r294_14, 0:r294_17 +# 294| v294_18(void) = Call[A] : func:r294_15, this:r294_14, 0:r294_17 # 294| mu294_19(unknown) = ^CallSideEffect : ~m? # 294| mu294_20(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_14 -# 294| v294_21(void) = Call : func:r294_8, this:r294_7, 0:r294_14 +# 294| v294_21(void) = Call[A] : func:r294_8, this:r294_7, 0:r294_14 # 294| mu294_22(unknown) = ^CallSideEffect : ~m? # 294| mu294_23(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_7 # 294| v294_24(void) = ^BufferReadSideEffect[0] : &:r294_14, ~m? @@ -1294,12 +1294,12 @@ ssa.cpp: # 295| r295_1(glval) = VariableAddress[a] : # 295| r295_2(glval) = FunctionAddress[operator new] : # 295| r295_3(unsigned long) = Constant[4] : -# 295| r295_4(void *) = Call : func:r295_2, 0:r295_3 +# 295| r295_4(void *) = Call[operator new] : func:r295_2, 0:r295_3 # 295| mu295_5(unknown) = ^CallSideEffect : ~m? # 295| mu295_6(unknown) = ^InitializeDynamicAllocation : &:r295_4 # 295| r295_7(A *) = Convert : r295_4 # 295| r295_8(glval) = FunctionAddress[A] : -# 295| v295_9(void) = Call : func:r295_8, this:r295_7 +# 295| v295_9(void) = Call[A] : func:r295_8, this:r295_7 # 295| mu295_10(unknown) = ^CallSideEffect : ~m? # 295| mu295_11(A) = ^IndirectMayWriteSideEffect[-1] : &:r295_7 # 295| m295_12(A *) = Store : &:r295_1, r295_7 @@ -1328,7 +1328,7 @@ ssa.cpp: # 302| r302_3(int) = Load : &:r302_2, m301_5 # 302| r302_4(glval) = VariableAddress[argv] : # 302| r302_5(char **) = Load : &:r302_4, m301_7 -# 302| v302_6(void) = Call : func:r302_1, 0:r302_3, 1:r302_5 +# 302| v302_6(void) = Call[unknownFunction] : func:r302_1, 0:r302_3, 1:r302_5 # 302| mu302_7(unknown) = ^CallSideEffect : ~m? # 302| v302_8(void) = ^BufferReadSideEffect[1] : &:r302_5, ~m? # 302| mu302_9(unknown) = ^BufferMayWriteSideEffect[1] : &:r302_5 @@ -1337,7 +1337,7 @@ ssa.cpp: # 303| r303_3(int) = Load : &:r303_2, m301_5 # 303| r303_4(glval) = VariableAddress[argv] : # 303| r303_5(char **) = Load : &:r303_4, m301_7 -# 303| v303_6(void) = Call : func:r303_1, 0:r303_3, 1:r303_5 +# 303| v303_6(void) = Call[unknownFunction] : func:r303_1, 0:r303_3, 1:r303_5 # 303| mu303_7(unknown) = ^CallSideEffect : ~m? # 303| v303_8(void) = ^BufferReadSideEffect[1] : &:r303_5, ~m? # 303| mu303_9(unknown) = ^BufferMayWriteSideEffect[1] : &:r303_5 diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected index 1d155eaf30d..9157b211d5b 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir_unsound.expected @@ -334,7 +334,7 @@ ssa.cpp: # 97| r97_2(glval) = VariableAddress[a] : # 97| r97_3(Point *) = CopyValue : r97_2 # 97| r97_4(void *) = Convert : r97_3 -# 97| v97_5(void) = Call : func:r97_1, 0:r97_4 +# 97| v97_5(void) = Call[Escape] : func:r97_1, 0:r97_4 # 97| mu97_6(unknown) = ^CallSideEffect : ~m? # 97| v97_7(void) = ^BufferReadSideEffect[0] : &:r97_4, ~m? # 97| mu97_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r97_4 @@ -386,7 +386,7 @@ ssa.cpp: # 108| r108_2(glval) = VariableAddress[a] : # 108| r108_3(Point *) = CopyValue : r108_2 # 108| r108_4(void *) = Convert : r108_3 -# 108| v108_5(void) = Call : func:r108_1, 0:r108_4 +# 108| v108_5(void) = Call[Escape] : func:r108_1, 0:r108_4 # 108| mu108_6(unknown) = ^CallSideEffect : ~m? # 108| v108_7(void) = ^BufferReadSideEffect[0] : &:r108_4, ~m? # 108| mu108_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r108_4 @@ -450,7 +450,7 @@ ssa.cpp: # 119| r119_2(glval) = VariableAddress[a] : # 119| r119_3(Point *) = CopyValue : r119_2 # 119| r119_4(void *) = Convert : r119_3 -# 119| v119_5(void) = Call : func:r119_1, 0:r119_4 +# 119| v119_5(void) = Call[Escape] : func:r119_1, 0:r119_4 # 119| mu119_6(unknown) = ^CallSideEffect : ~m? # 119| v119_7(void) = ^BufferReadSideEffect[0] : &:r119_4, ~m? # 119| mu119_8(unknown) = ^BufferMayWriteSideEffect[0] : &:r119_4 @@ -789,7 +789,7 @@ ssa.cpp: # 199| r199_6(glval) = VariableAddress[str2] : # 199| r199_7(char *) = Load : &:r199_6, m198_9 # 199| r199_8(char *) = Convert : r199_7 -# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8 +# 199| r199_9(int) = Call[strcmp] : func:r199_2, 0:r199_5, 1:r199_8 # 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m? # 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m? # 199| m199_12(int) = Store : &:r199_1, r199_9 @@ -797,7 +797,7 @@ ssa.cpp: # 200| r200_2(glval) = VariableAddress[str1] : # 200| r200_3(char *) = Load : &:r200_2, m198_5 # 200| r200_4(char *) = Convert : r200_3 -# 200| r200_5(int) = Call : func:r200_1, 0:r200_4 +# 200| r200_5(int) = Call[strlen] : func:r200_1, 0:r200_4 # 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m? # 200| r200_7(glval) = VariableAddress[ret] : # 200| r200_8(int) = Load : &:r200_7, m199_12 @@ -806,7 +806,7 @@ ssa.cpp: # 201| r201_1(glval) = FunctionAddress[abs] : # 201| r201_2(glval) = VariableAddress[x] : # 201| r201_3(int) = Load : &:r201_2, m198_13 -# 201| r201_4(int) = Call : func:r201_1, 0:r201_3 +# 201| r201_4(int) = Call[abs] : func:r201_1, 0:r201_3 # 201| r201_5(glval) = VariableAddress[ret] : # 201| r201_6(int) = Load : &:r201_5, m200_10 # 201| r201_7(int) = Add : r201_6, r201_4 @@ -839,7 +839,7 @@ ssa.cpp: # 209| r209_6(int *) = CopyValue : r209_5 # 209| r209_7(void *) = Convert : r209_6 # 209| r209_8(int) = Constant[4] : -# 209| r209_9(void *) = Call : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 +# 209| r209_9(void *) = Call[memcpy] : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8 # 209| v209_10(void) = ^SizedBufferReadSideEffect[1] : &:r209_7, r209_8, ~m? # 209| mu209_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r209_4, r209_8 # 210| r210_1(glval) = VariableAddress[#return] : @@ -917,7 +917,7 @@ ssa.cpp: # 226| mu226_2(unknown) = AliasedDefinition : # 226| mu226_3(unknown) = InitializeNonLocal : # 227| r227_1(glval) = FunctionAddress[ExternalFunc] : -# 227| v227_2(void) = Call : func:r227_1 +# 227| v227_2(void) = Call[ExternalFunc] : func:r227_1 # 227| mu227_3(unknown) = ^CallSideEffect : ~m? # 229| r229_1(glval) = VariableAddress[s] : # 229| r229_2(glval) = StringConstant["Literal"] : @@ -976,18 +976,18 @@ ssa.cpp: # 240| mu240_2(Constructible) = Uninitialized[c] : &:r240_1 # 240| r240_3(glval) = FunctionAddress[Constructible] : # 240| r240_4(int) = Constant[1] : -# 240| v240_5(void) = Call : func:r240_3, this:r240_1, 0:r240_4 +# 240| v240_5(void) = Call[Constructible] : func:r240_3, this:r240_1, 0:r240_4 # 240| mu240_6(unknown) = ^CallSideEffect : ~m? # 240| mu240_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r240_1 # 241| r241_1(glval) = VariableAddress[c] : # 241| r241_2(glval) = FunctionAddress[g] : -# 241| v241_3(void) = Call : func:r241_2, this:r241_1 +# 241| v241_3(void) = Call[g] : func:r241_2, this:r241_1 # 241| mu241_4(unknown) = ^CallSideEffect : ~m? # 241| v241_5(void) = ^BufferReadSideEffect[-1] : &:r241_1, ~m? # 241| mu241_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r241_1 # 242| r242_1(glval) = VariableAddress[c] : # 242| r242_2(glval) = FunctionAddress[g] : -# 242| v242_3(void) = Call : func:r242_2, this:r242_1 +# 242| v242_3(void) = Call[g] : func:r242_2, this:r242_1 # 242| mu242_4(unknown) = ^CallSideEffect : ~m? # 242| v242_5(void) = ^BufferReadSideEffect[-1] : &:r242_1, ~m? # 242| mu242_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r242_1 @@ -995,12 +995,12 @@ ssa.cpp: # 243| mu243_2(Constructible) = Uninitialized[c2] : &:r243_1 # 243| r243_3(glval) = FunctionAddress[Constructible] : # 243| r243_4(int) = Constant[2] : -# 243| v243_5(void) = Call : func:r243_3, this:r243_1, 0:r243_4 +# 243| v243_5(void) = Call[Constructible] : func:r243_3, this:r243_1, 0:r243_4 # 243| mu243_6(unknown) = ^CallSideEffect : ~m? # 243| mu243_7(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r243_1 # 244| r244_1(glval) = VariableAddress[c2] : # 244| r244_2(glval) = FunctionAddress[g] : -# 244| v244_3(void) = Call : func:r244_2, this:r244_1 +# 244| v244_3(void) = Call[g] : func:r244_2, this:r244_1 # 244| mu244_4(unknown) = ^CallSideEffect : ~m? # 244| v244_5(void) = ^BufferReadSideEffect[-1] : &:r244_1, ~m? # 244| mu244_6(Constructible) = ^IndirectMayWriteSideEffect[-1] : &:r244_1 @@ -1027,7 +1027,7 @@ ssa.cpp: # 248| r248_5(unsigned long) = Convert : r248_4 # 248| r248_6(unsigned long) = Constant[1] : # 248| r248_7(unsigned long) = Mul : r248_5, r248_6 -# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7 +# 248| r248_8(void *) = Call[operator new[]] : func:r248_2, 0:r248_7 # 248| mu248_9(unknown) = ^CallSideEffect : ~m? # 248| mu248_10(unknown) = ^InitializeDynamicAllocation : &:r248_8 # 248| r248_11(char *) = Convert : r248_8 @@ -1046,7 +1046,7 @@ ssa.cpp: # 250| r250_7(void *) = Convert : r250_6 # 250| r250_8(glval) = VariableAddress[size] : # 250| r250_9(int) = Load : &:r250_8, m247_9 -# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 +# 250| r250_10(void *) = Call[memcpy] : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9 # 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m? # 250| mu250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9 # 251| r251_1(glval) = VariableAddress[#return] : @@ -1074,13 +1074,13 @@ ssa.cpp: # 256| Block 1 # 256| r256_1(glval) = FunctionAddress[ExternalFunc] : -# 256| v256_2(void) = Call : func:r256_1 +# 256| v256_2(void) = Call[ExternalFunc] : func:r256_1 # 256| mu256_3(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 3 # 259| Block 2 # 259| r259_1(glval) = FunctionAddress[ExternalFunc] : -# 259| v259_2(void) = Call : func:r259_1 +# 259| v259_2(void) = Call[ExternalFunc] : func:r259_1 # 259| mu259_3(unknown) = ^CallSideEffect : ~m? #-----| Goto -> Block 3 @@ -1116,7 +1116,7 @@ ssa.cpp: # 269| r269_2(glval) = FunctionAddress[malloc] : # 269| r269_3(glval) = VariableAddress[size] : # 269| r269_4(int) = Load : &:r269_3, m268_9 -# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4 +# 269| r269_5(void *) = Call[malloc] : func:r269_2, 0:r269_4 # 269| mu269_6(unknown) = ^CallSideEffect : ~m? # 269| mu269_7(unknown) = ^InitializeDynamicAllocation : &:r269_5 # 269| m269_8(void *) = Store : &:r269_1, r269_5 @@ -1127,7 +1127,7 @@ ssa.cpp: # 270| r270_5(void *) = Load : &:r270_4, m268_5 # 270| r270_6(glval) = VariableAddress[size] : # 270| r270_7(int) = Load : &:r270_6, m268_9 -# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 +# 270| r270_8(void *) = Call[memcpy] : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7 # 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m? # 270| mu270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7 # 271| r271_1(glval) = VariableAddress[#return] : @@ -1250,7 +1250,7 @@ ssa.cpp: # 292| r292_1(glval) = VariableAddress[p] : # 292| r292_2(glval) = FunctionAddress[operator new] : # 292| r292_3(unsigned long) = Constant[8] : -# 292| r292_4(void *) = Call : func:r292_2, 0:r292_3 +# 292| r292_4(void *) = Call[operator new] : func:r292_2, 0:r292_3 # 292| mu292_5(unknown) = ^CallSideEffect : ~m? # 292| mu292_6(unknown) = ^InitializeDynamicAllocation : &:r292_4 # 292| r292_7(Point *) = Convert : r292_4 @@ -1258,7 +1258,7 @@ ssa.cpp: # 293| r293_1(glval) = VariableAddress[q] : # 293| r293_2(glval) = FunctionAddress[operator new] : # 293| r293_3(unsigned long) = Constant[8] : -# 293| r293_4(void *) = Call : func:r293_2, 0:r293_3 +# 293| r293_4(void *) = Call[operator new] : func:r293_2, 0:r293_3 # 293| mu293_5(unknown) = ^CallSideEffect : ~m? # 293| mu293_6(unknown) = ^InitializeDynamicAllocation : &:r293_4 # 293| r293_7(Point *) = Convert : r293_4 @@ -1266,24 +1266,24 @@ ssa.cpp: # 294| r294_1(glval) = VariableAddress[j] : # 294| r294_2(glval) = FunctionAddress[operator new] : # 294| r294_3(unsigned long) = Constant[4] : -# 294| r294_4(void *) = Call : func:r294_2, 0:r294_3 +# 294| r294_4(void *) = Call[operator new] : func:r294_2, 0:r294_3 # 294| mu294_5(unknown) = ^CallSideEffect : ~m? # 294| mu294_6(unknown) = ^InitializeDynamicAllocation : &:r294_4 # 294| r294_7(A *) = Convert : r294_4 # 294| r294_8(glval) = FunctionAddress[A] : # 294| r294_9(glval) = FunctionAddress[operator new] : # 294| r294_10(unsigned long) = Constant[4] : -# 294| r294_11(void *) = Call : func:r294_9, 0:r294_10 +# 294| r294_11(void *) = Call[operator new] : func:r294_9, 0:r294_10 # 294| mu294_12(unknown) = ^CallSideEffect : ~m? # 294| mu294_13(unknown) = ^InitializeDynamicAllocation : &:r294_11 # 294| r294_14(A *) = Convert : r294_11 # 294| r294_15(glval) = FunctionAddress[A] : # 294| r294_16(glval) = VariableAddress[x] : # 294| r294_17(int) = Load : &:r294_16, m291_5 -# 294| v294_18(void) = Call : func:r294_15, this:r294_14, 0:r294_17 +# 294| v294_18(void) = Call[A] : func:r294_15, this:r294_14, 0:r294_17 # 294| mu294_19(unknown) = ^CallSideEffect : ~m? # 294| mu294_20(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_14 -# 294| v294_21(void) = Call : func:r294_8, this:r294_7, 0:r294_14 +# 294| v294_21(void) = Call[A] : func:r294_8, this:r294_7, 0:r294_14 # 294| mu294_22(unknown) = ^CallSideEffect : ~m? # 294| mu294_23(A) = ^IndirectMayWriteSideEffect[-1] : &:r294_7 # 294| v294_24(void) = ^BufferReadSideEffect[0] : &:r294_14, ~m? @@ -1294,12 +1294,12 @@ ssa.cpp: # 295| r295_1(glval) = VariableAddress[a] : # 295| r295_2(glval) = FunctionAddress[operator new] : # 295| r295_3(unsigned long) = Constant[4] : -# 295| r295_4(void *) = Call : func:r295_2, 0:r295_3 +# 295| r295_4(void *) = Call[operator new] : func:r295_2, 0:r295_3 # 295| mu295_5(unknown) = ^CallSideEffect : ~m? # 295| mu295_6(unknown) = ^InitializeDynamicAllocation : &:r295_4 # 295| r295_7(A *) = Convert : r295_4 # 295| r295_8(glval) = FunctionAddress[A] : -# 295| v295_9(void) = Call : func:r295_8, this:r295_7 +# 295| v295_9(void) = Call[A] : func:r295_8, this:r295_7 # 295| mu295_10(unknown) = ^CallSideEffect : ~m? # 295| mu295_11(A) = ^IndirectMayWriteSideEffect[-1] : &:r295_7 # 295| m295_12(A *) = Store : &:r295_1, r295_7 @@ -1328,7 +1328,7 @@ ssa.cpp: # 302| r302_3(int) = Load : &:r302_2, m301_5 # 302| r302_4(glval) = VariableAddress[argv] : # 302| r302_5(char **) = Load : &:r302_4, m301_7 -# 302| v302_6(void) = Call : func:r302_1, 0:r302_3, 1:r302_5 +# 302| v302_6(void) = Call[unknownFunction] : func:r302_1, 0:r302_3, 1:r302_5 # 302| mu302_7(unknown) = ^CallSideEffect : ~m? # 302| v302_8(void) = ^BufferReadSideEffect[1] : &:r302_5, ~m? # 302| mu302_9(unknown) = ^BufferMayWriteSideEffect[1] : &:r302_5 @@ -1337,7 +1337,7 @@ ssa.cpp: # 303| r303_3(int) = Load : &:r303_2, m301_5 # 303| r303_4(glval) = VariableAddress[argv] : # 303| r303_5(char **) = Load : &:r303_4, m301_7 -# 303| v303_6(void) = Call : func:r303_1, 0:r303_3, 1:r303_5 +# 303| v303_6(void) = Call[unknownFunction] : func:r303_1, 0:r303_3, 1:r303_5 # 303| mu303_7(unknown) = ^CallSideEffect : ~m? # 303| v303_8(void) = ^BufferReadSideEffect[1] : &:r303_5, ~m? # 303| mu303_9(unknown) = ^BufferMayWriteSideEffect[1] : &:r303_5 diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected index b5d59659460..0f8f7079b40 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -202,7 +202,7 @@ test.cpp: # 29| valnum = m29_10, r29_8 # 30| r30_1(glval) = FunctionAddress[change_global02] : # 30| valnum = unique -# 30| v30_2(void) = Call : func:r30_1 +# 30| v30_2(void) = Call[change_global02] : func:r30_1 # 30| m30_3(unknown) = ^CallSideEffect : ~m25_4 # 30| valnum = unique # 30| m30_4(unknown) = Chi : total:m25_4, partial:m30_3 @@ -538,7 +538,7 @@ test.cpp: # 77| valnum = r77_1, r79_1, r80_6 # 77| r77_2(glval) = FunctionAddress[getAValue] : # 77| valnum = unique -# 77| r77_3(int) = Call : func:r77_2 +# 77| r77_3(int) = Call[getAValue] : func:r77_2 # 77| valnum = unique # 77| m77_4(unknown) = ^CallSideEffect : ~m75_4 # 77| valnum = unique @@ -585,7 +585,7 @@ test.cpp: # 80| Block 1 # 80| r80_1(glval) = FunctionAddress[getAValue] : # 80| valnum = unique -# 80| r80_2(int) = Call : func:r80_1 +# 80| r80_2(int) = Call[getAValue] : func:r80_1 # 80| valnum = unique # 80| m80_3(unknown) = ^CallSideEffect : ~m77_5 # 80| valnum = unique diff --git a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll index 620b23b942e..135b91c0dec 100644 --- a/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll @@ -1501,6 +1501,12 @@ class SwitchInstruction extends Instruction { class CallInstruction extends Instruction { CallInstruction() { getOpcode() instanceof Opcode::Call } + final override string getImmediateString() { + result = getStaticCallTarget().toString() + or + not exists(getStaticCallTarget()) and result = "?" + } + /** * Gets the operand the specifies the target function of the call. */ diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll index 620b23b942e..135b91c0dec 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll @@ -1501,6 +1501,12 @@ class SwitchInstruction extends Instruction { class CallInstruction extends Instruction { CallInstruction() { getOpcode() instanceof Opcode::Call } + final override string getImmediateString() { + result = getStaticCallTarget().toString() + or + not exists(getStaticCallTarget()) and result = "?" + } + /** * Gets the operand the specifies the target function of the call. */ diff --git a/csharp/ql/test/experimental/ir/ir/raw_ir.expected b/csharp/ql/test/experimental/ir/ir/raw_ir.expected index e426955f9da..e0586a0adcd 100644 --- a/csharp/ql/test/experimental/ir/ir/raw_ir.expected +++ b/csharp/ql/test/experimental/ir/ir/raw_ir.expected @@ -222,7 +222,7 @@ casts.cs: # 13| r13_1(glval) = VariableAddress[Aobj] : # 13| r13_2(Casts_A) = NewObj : # 13| r13_3() = FunctionAddress[Casts_A] : -# 13| v13_4(Void) = Call : func:r13_3, this:r13_2 +# 13| v13_4(Void) = Call[Casts_A] : func:r13_3, this:r13_2 # 13| mu13_5() = ^CallSideEffect : ~m? # 13| mu13_6(Casts_A) = Store : &:r13_1, r13_2 # 14| r14_1(glval) = VariableAddress[bobjCE] : @@ -247,13 +247,13 @@ collections.cs: # 13| r13_1(glval>) = VariableAddress[dict] : # 13| r13_2(Dictionary) = NewObj : # 13| r13_3() = FunctionAddress[Dictionary] : -# 13| v13_4(Void) = Call : func:r13_3, this:r13_2 +# 13| v13_4(Void) = Call[Dictionary] : func:r13_3, this:r13_2 # 13| mu13_5() = ^CallSideEffect : ~m? # 15| r15_1() = FunctionAddress[Add] : # 15| r15_2(Int32) = Constant[0] : # 15| r15_3(MyClass) = NewObj : # 15| r15_4() = FunctionAddress[MyClass] : -# 15| v15_5(Void) = Call : func:r15_4, this:r15_3 +# 15| v15_5(Void) = Call[MyClass] : func:r15_4, this:r15_3 # 15| mu15_6() = ^CallSideEffect : ~m? # 15| r15_7(String) = StringConstant["Hello"] : # 15| r15_8(glval) = FieldAddress[a] : r15_3 @@ -261,13 +261,13 @@ collections.cs: # 15| r15_10(String) = StringConstant["World"] : # 15| r15_11(glval) = FieldAddress[b] : r15_3 # 15| mu15_12(String) = Store : &:r15_11, r15_10 -# 15| v15_13(Void) = Call : func:r15_1, this:r13_2, 0:r15_2, 1:r15_3 +# 15| v15_13(Void) = Call[Add] : func:r15_1, this:r13_2, 0:r15_2, 1:r15_3 # 15| mu15_14() = ^CallSideEffect : ~m? # 16| r16_1() = FunctionAddress[Add] : # 16| r16_2(Int32) = Constant[1] : # 16| r16_3(MyClass) = NewObj : # 16| r16_4() = FunctionAddress[MyClass] : -# 16| v16_5(Void) = Call : func:r16_4, this:r16_3 +# 16| v16_5(Void) = Call[MyClass] : func:r16_4, this:r16_3 # 16| mu16_6() = ^CallSideEffect : ~m? # 16| r16_7(String) = StringConstant["Foo"] : # 16| r16_8(glval) = FieldAddress[a] : r16_3 @@ -275,7 +275,7 @@ collections.cs: # 16| r16_10(String) = StringConstant["Bar"] : # 16| r16_11(glval) = FieldAddress[b] : r16_3 # 16| mu16_12(String) = Store : &:r16_11, r16_10 -# 16| v16_13(Void) = Call : func:r16_1, this:r13_2, 0:r16_2, 1:r16_3 +# 16| v16_13(Void) = Call[Add] : func:r16_1, this:r13_2, 0:r16_2, 1:r16_3 # 16| mu16_14() = ^CallSideEffect : ~m? # 13| mu13_6(Dictionary) = Store : &:r13_1, r13_2 # 11| v11_3(Void) = ReturnVoid : @@ -316,7 +316,7 @@ constructor_init.cs: # 17| r17_3(glval) = InitializeThis : # 17| r17_4(glval) = Convert[DerivedClass : BaseClass] : r17_3 # 17| r17_5() = FunctionAddress[BaseClass] : -# 17| v17_6(Void) = Call : func:r17_5, this:r17_4 +# 17| v17_6(Void) = Call[BaseClass] : func:r17_5, this:r17_4 # 17| mu17_7() = ^CallSideEffect : ~m? # 18| v18_1(Void) = NoOp : # 17| v17_8(Void) = ReturnVoid : @@ -334,7 +334,7 @@ constructor_init.cs: # 21| r21_7() = FunctionAddress[BaseClass] : # 21| r21_8(glval) = VariableAddress[i] : # 21| r21_9(Int32) = Load : &:r21_8, ~m? -# 21| v21_10(Void) = Call : func:r21_7, this:r21_6, 0:r21_9 +# 21| v21_10(Void) = Call[BaseClass] : func:r21_7, this:r21_6, 0:r21_9 # 21| mu21_11() = ^CallSideEffect : ~m? # 22| v22_1(Void) = NoOp : # 21| v21_12(Void) = ReturnVoid : @@ -353,7 +353,7 @@ constructor_init.cs: # 25| r25_8() = FunctionAddress[DerivedClass] : # 25| r25_9(glval) = VariableAddress[i] : # 25| r25_10(Int32) = Load : &:r25_9, ~m? -# 25| v25_11(Void) = Call : func:r25_8, this:r25_3, 0:r25_10 +# 25| v25_11(Void) = Call[DerivedClass] : func:r25_8, this:r25_3, 0:r25_10 # 25| mu25_12() = ^CallSideEffect : ~m? # 26| v26_1(Void) = NoOp : # 25| v25_13(Void) = ReturnVoid : @@ -367,14 +367,14 @@ constructor_init.cs: # 31| r31_1(glval) = VariableAddress[obj1] : # 31| r31_2(DerivedClass) = NewObj : # 31| r31_3() = FunctionAddress[DerivedClass] : -# 31| v31_4(Void) = Call : func:r31_3, this:r31_2 +# 31| v31_4(Void) = Call[DerivedClass] : func:r31_3, this:r31_2 # 31| mu31_5() = ^CallSideEffect : ~m? # 31| mu31_6(DerivedClass) = Store : &:r31_1, r31_2 # 32| r32_1(glval) = VariableAddress[obj2] : # 32| r32_2(DerivedClass) = NewObj : # 32| r32_3() = FunctionAddress[DerivedClass] : # 32| r32_4(Int32) = Constant[1] : -# 32| v32_5(Void) = Call : func:r32_3, this:r32_2, 0:r32_4 +# 32| v32_5(Void) = Call[DerivedClass] : func:r32_3, this:r32_2, 0:r32_4 # 32| mu32_6() = ^CallSideEffect : ~m? # 32| mu32_7(DerivedClass) = Store : &:r32_1, r32_2 # 33| r33_1(glval) = VariableAddress[obj3] : @@ -382,7 +382,7 @@ constructor_init.cs: # 33| r33_3() = FunctionAddress[DerivedClass] : # 33| r33_4(Int32) = Constant[1] : # 33| r33_5(Int32) = Constant[2] : -# 33| v33_6(Void) = Call : func:r33_3, this:r33_2, 0:r33_4, 1:r33_5 +# 33| v33_6(Void) = Call[DerivedClass] : func:r33_3, this:r33_2, 0:r33_4, 1:r33_5 # 33| mu33_7() = ^CallSideEffect : ~m? # 33| mu33_8(DerivedClass) = Store : &:r33_1, r33_2 # 29| v29_3(Void) = ReturnVoid : @@ -453,14 +453,14 @@ delegates.cs: # 12| r12_2(Del) = NewObj : # 12| r12_3() = FunctionAddress[Del] : # 12| r12_4(glval) = FunctionAddress[returns] : -# 12| v12_5(Void) = Call : func:r12_3, this:r12_2, 0:r12_4 +# 12| v12_5(Void) = Call[Del] : func:r12_3, this:r12_2, 0:r12_4 # 12| mu12_6() = ^CallSideEffect : ~m? # 12| mu12_7(Del) = Store : &:r12_1, r12_2 # 13| r13_1(glval) = VariableAddress[del1] : # 13| r13_2(Del) = Load : &:r13_1, ~m? # 13| r13_3() = FunctionAddress[Invoke] : # 13| r13_4(Int32) = Constant[5] : -# 13| v13_5(Void) = Call : func:r13_3, this:r13_2, 0:r13_4 +# 13| v13_5(Void) = Call[Invoke] : func:r13_3, this:r13_2, 0:r13_4 # 13| mu13_6() = ^CallSideEffect : ~m? # 11| v11_3(Void) = ReturnVoid : # 11| v11_4(Void) = AliasedUse : ~m? @@ -475,7 +475,7 @@ events.cs: # 10| r10_1(MyDel) = NewObj : # 10| r10_2() = FunctionAddress[MyDel] : # 10| r10_3(glval) = FunctionAddress[Fun] : -# 10| v10_4(Void) = Call : func:r10_2, this:r10_1, 0:r10_3 +# 10| v10_4(Void) = Call[MyDel] : func:r10_2, this:r10_1, 0:r10_3 # 10| mu10_5() = ^CallSideEffect : ~m? # 10| r10_6(Events) = CopyValue : r8_3 # 10| r10_7(glval) = FieldAddress[Inst] : r10_6 @@ -494,7 +494,7 @@ events.cs: # 15| r15_3(Events) = CopyValue : r13_3 # 15| r15_4(glval) = FieldAddress[Inst] : r15_3 # 15| r15_5(MyDel) = Load : &:r15_4, ~m? -# 15| v15_6(Void) = Call : func:r15_2, this:r15_1, 0:r15_5 +# 15| v15_6(Void) = Call[add_MyEvent] : func:r15_2, this:r15_1, 0:r15_5 # 15| mu15_7() = ^CallSideEffect : ~m? # 13| v13_4(Void) = ReturnVoid : # 13| v13_5(Void) = AliasedUse : ~m? @@ -510,7 +510,7 @@ events.cs: # 20| r20_3(Events) = CopyValue : r18_3 # 20| r20_4(glval) = FieldAddress[Inst] : r20_3 # 20| r20_5(MyDel) = Load : &:r20_4, ~m? -# 20| v20_6(Void) = Call : func:r20_2, this:r20_1, 0:r20_5 +# 20| v20_6(Void) = Call[remove_MyEvent] : func:r20_2, this:r20_1, 0:r20_5 # 20| mu20_7() = ^CallSideEffect : ~m? # 18| v18_4(Void) = ReturnVoid : # 18| v18_5(Void) = AliasedUse : ~m? @@ -541,26 +541,26 @@ events.cs: # 30| r30_1(glval) = VariableAddress[obj] : # 30| r30_2(Events) = NewObj : # 30| r30_3() = FunctionAddress[Events] : -# 30| v30_4(Void) = Call : func:r30_3, this:r30_2 +# 30| v30_4(Void) = Call[Events] : func:r30_3, this:r30_2 # 30| mu30_5() = ^CallSideEffect : ~m? # 30| mu30_6(Events) = Store : &:r30_1, r30_2 # 31| r31_1(glval) = VariableAddress[obj] : # 31| r31_2(Events) = Load : &:r31_1, ~m? # 31| r31_3() = FunctionAddress[AddEvent] : -# 31| v31_4(Void) = Call : func:r31_3, this:r31_2 +# 31| v31_4(Void) = Call[AddEvent] : func:r31_3, this:r31_2 # 31| mu31_5() = ^CallSideEffect : ~m? # 32| r32_1(glval) = VariableAddress[result] : # 32| r32_2(glval) = VariableAddress[obj] : # 32| r32_3(Events) = Load : &:r32_2, ~m? # 32| r32_4() = FunctionAddress[Invoke] : # 32| r32_5(String) = StringConstant["string"] : -# 32| v32_6(Void) = Call : func:r32_4, this:r32_3, 0:r32_5 +# 32| v32_6(Void) = Call[Invoke] : func:r32_4, this:r32_3, 0:r32_5 # 32| mu32_7() = ^CallSideEffect : ~m? # 32| mu32_8(String) = Store : &:r32_1, v32_6 # 33| r33_1(glval) = VariableAddress[obj] : # 33| r33_2(Events) = Load : &:r33_1, ~m? # 33| r33_3() = FunctionAddress[RemoveEvent] : -# 33| v33_4(Void) = Call : func:r33_3, this:r33_2 +# 33| v33_4(Void) = Call[RemoveEvent] : func:r33_3, this:r33_2 # 33| mu33_5() = ^CallSideEffect : ~m? # 28| v28_5(Void) = ReturnVoid : # 28| v28_6(Void) = AliasedUse : ~m? @@ -605,7 +605,7 @@ foreach.cs: # 7| r7_2(glval) = VariableAddress[a_array] : # 7| r7_3(Int32[]) = Load : &:r7_2, ~m? # 7| r7_4() = FunctionAddress[GetEnumerator] : -# 7| r7_5(IEnumerator) = Call : func:r7_4, this:r7_3 +# 7| r7_5(IEnumerator) = Call[GetEnumerator] : func:r7_4, this:r7_3 # 7| mu7_6() = ^CallSideEffect : ~m? # 7| mu7_7(IEnumerator) = Store : &:r7_1, r7_5 #-----| Goto -> Block 1 @@ -614,7 +614,7 @@ foreach.cs: # 7| r7_8(glval) = VariableAddress[#temp7:9] : # 7| r7_9(Boolean) = Load : &:r7_8, ~m? # 7| r7_10() = FunctionAddress[MoveNext] : -# 7| r7_11(Boolean) = Call : func:r7_10, this:r7_9 +# 7| r7_11(Boolean) = Call[MoveNext] : func:r7_10, this:r7_9 # 7| mu7_12() = ^CallSideEffect : ~m? # 7| v7_13(Void) = ConditionalBranch : r7_11 #-----| False -> Block 3 @@ -625,7 +625,7 @@ foreach.cs: # 7| r7_15(glval) = VariableAddress[#temp7:9] : # 7| r7_16(Boolean) = Load : &:r7_15, ~m? # 7| r7_17() = FunctionAddress[get_Current] : -# 7| r7_18(Int32) = Call : func:r7_17, this:r7_16 +# 7| r7_18(Int32) = Call[get_Current] : func:r7_17, this:r7_16 # 7| mu7_19() = ^CallSideEffect : ~m? # 7| mu7_20(Int32) = Store : &:r7_14, r7_18 # 9| r9_1(glval) = VariableAddress[x] : @@ -638,7 +638,7 @@ foreach.cs: # 7| r7_21(glval) = VariableAddress[#temp7:9] : # 7| r7_22(Boolean) = Load : &:r7_21, ~m? # 7| r7_23() = FunctionAddress[Dispose] : -# 7| v7_24(Void) = Call : func:r7_23, this:r7_22 +# 7| v7_24(Void) = Call[Dispose] : func:r7_23, this:r7_22 # 7| mu7_25() = ^CallSideEffect : ~m? # 4| v4_3(Void) = ReturnVoid : # 4| v4_4(Void) = AliasedUse : ~m? @@ -673,7 +673,7 @@ func_with_param_call.cs: # 12| r12_2() = FunctionAddress[f] : # 12| r12_3(Int32) = Constant[2] : # 12| r12_4(Int32) = Constant[3] : -# 12| r12_5(Int32) = Call : func:r12_2, 0:r12_3, 1:r12_4 +# 12| r12_5(Int32) = Call[f] : func:r12_2, 0:r12_3, 1:r12_4 # 12| mu12_6() = ^CallSideEffect : ~m? # 12| mu12_7(Int32) = Store : &:r12_1, r12_5 # 10| r10_3(glval) = VariableAddress[#return] : @@ -732,7 +732,7 @@ indexers.cs: # 21| r21_1(glval) = VariableAddress[inst] : # 21| r21_2(MyClass) = NewObj : # 21| r21_3() = FunctionAddress[MyClass] : -# 21| v21_4(Void) = Call : func:r21_3, this:r21_2 +# 21| v21_4(Void) = Call[MyClass] : func:r21_3, this:r21_2 # 21| mu21_5() = ^CallSideEffect : ~m? # 21| mu21_6(MyClass) = Store : &:r21_1, r21_2 # 22| r22_1(glval) = VariableAddress[inst] : @@ -740,14 +740,14 @@ indexers.cs: # 22| r22_3() = FunctionAddress[set_Item] : # 22| r22_4(Int32) = Constant[0] : # 22| r22_5(String) = StringConstant["str1"] : -# 22| v22_6(Void) = Call : func:r22_3, this:r22_2, 0:r22_4, 1:r22_5 +# 22| v22_6(Void) = Call[set_Item] : func:r22_3, this:r22_2, 0:r22_4, 1:r22_5 # 22| mu22_7() = ^CallSideEffect : ~m? # 23| r23_1(glval) = VariableAddress[inst] : # 23| r23_2(MyClass) = Load : &:r23_1, ~m? # 23| r23_3() = FunctionAddress[set_Item] : # 23| r23_4(Int32) = Constant[1] : # 23| r23_5(String) = StringConstant["str1"] : -# 23| v23_6(Void) = Call : func:r23_3, this:r23_2, 0:r23_4, 1:r23_5 +# 23| v23_6(Void) = Call[set_Item] : func:r23_3, this:r23_2, 0:r23_4, 1:r23_5 # 23| mu23_7() = ^CallSideEffect : ~m? # 24| r24_1(glval) = VariableAddress[inst] : # 24| r24_2(MyClass) = Load : &:r24_1, ~m? @@ -757,9 +757,9 @@ indexers.cs: # 24| r24_6(MyClass) = Load : &:r24_5, ~m? # 24| r24_7() = FunctionAddress[get_Item] : # 24| r24_8(Int32) = Constant[0] : -# 24| r24_9(String) = Call : func:r24_7, this:r24_6, 0:r24_8 +# 24| r24_9(String) = Call[get_Item] : func:r24_7, this:r24_6, 0:r24_8 # 24| mu24_10() = ^CallSideEffect : ~m? -# 24| v24_11(Void) = Call : func:r24_3, this:r24_2, 0:r24_4, 1:r24_9 +# 24| v24_11(Void) = Call[set_Item] : func:r24_3, this:r24_2, 0:r24_4, 1:r24_9 # 24| mu24_12() = ^CallSideEffect : ~m? # 19| v19_3(Void) = ReturnVoid : # 19| v19_4(Void) = AliasedUse : ~m? @@ -799,13 +799,13 @@ inheritance_polymorphism.cs: # 25| r25_1(glval) = VariableAddress[objB] : # 25| r25_2(B) = NewObj : # 25| r25_3() = FunctionAddress[B] : -# 25| v25_4(Void) = Call : func:r25_3, this:r25_2 +# 25| v25_4(Void) = Call[B] : func:r25_3, this:r25_2 # 25| mu25_5() = ^CallSideEffect : ~m? # 25| mu25_6(B) = Store : &:r25_1, r25_2 # 26| r26_1(glval) = VariableAddress[objB] : # 26| r26_2(B) = Load : &:r26_1, ~m? # 26| r26_3() = FunctionAddress[function] : -# 26| r26_4(Int32) = Call : func:r26_3, this:r26_2 +# 26| r26_4(Int32) = Call[function] : func:r26_3, this:r26_2 # 26| mu26_5() = ^CallSideEffect : ~m? # 29| r29_1(glval) = VariableAddress[objA] : # 29| mu29_2(A) = Uninitialized[objA] : &:r29_1 @@ -817,19 +817,19 @@ inheritance_polymorphism.cs: # 31| r31_1(glval) = VariableAddress[objA] : # 31| r31_2(A) = Load : &:r31_1, ~m? # 31| r31_3() = FunctionAddress[function] : -# 31| r31_4(Int32) = Call : func:r31_3, this:r31_2 +# 31| r31_4(Int32) = Call[function] : func:r31_3, this:r31_2 # 31| mu31_5() = ^CallSideEffect : ~m? # 33| r33_1(glval) = VariableAddress[objC] : # 33| r33_2(C) = NewObj : # 33| r33_3() = FunctionAddress[C] : -# 33| v33_4(Void) = Call : func:r33_3, this:r33_2 +# 33| v33_4(Void) = Call[C] : func:r33_3, this:r33_2 # 33| mu33_5() = ^CallSideEffect : ~m? # 33| r33_6(A) = Convert : r33_2 # 33| mu33_7(A) = Store : &:r33_1, r33_2 # 34| r34_1(glval) = VariableAddress[objC] : # 34| r34_2(A) = Load : &:r34_1, ~m? # 34| r34_3() = FunctionAddress[function] : -# 34| r34_4(Int32) = Call : func:r34_3, this:r34_2 +# 34| r34_4(Int32) = Call[function] : func:r34_3, this:r34_2 # 34| mu34_5() = ^CallSideEffect : ~m? # 23| v23_3(Void) = ReturnVoid : # 23| v23_4(Void) = AliasedUse : ~m? @@ -905,7 +905,7 @@ inoutref.cs: # 26| r26_4(glval) = VariableAddress[c1] : # 26| r26_5(MyClass) = Load : &:r26_4, ~m? # 26| r26_6(MyClass) = Load : &:r26_5, ~m? -# 26| v26_7(Void) = Call : func:r26_1, 0:r26_3, 1:r26_6 +# 26| v26_7(Void) = Call[set] : func:r26_1, 0:r26_3, 1:r26_6 # 26| mu26_8() = ^CallSideEffect : ~m? # 16| v16_13(Void) = ReturnVoid : # 16| v16_14(Void) = AliasedUse : ~m? @@ -921,14 +921,14 @@ inoutref.cs: # 32| r32_1(glval) = VariableAddress[b] : # 32| r32_2(MyStruct) = NewObj : # 32| r32_3() = FunctionAddress[MyStruct] : -# 32| v32_4(Void) = Call : func:r32_3, this:r32_2 +# 32| v32_4(Void) = Call[MyStruct] : func:r32_3, this:r32_2 # 32| mu32_5() = ^CallSideEffect : ~m? # 32| r32_6(MyStruct) = Load : &:r32_2, ~m? # 32| mu32_7(MyStruct) = Store : &:r32_1, r32_6 # 33| r33_1(glval) = VariableAddress[c] : # 33| r33_2(MyClass) = NewObj : # 33| r33_3() = FunctionAddress[MyClass] : -# 33| v33_4(Void) = Call : func:r33_3, this:r33_2 +# 33| v33_4(Void) = Call[MyClass] : func:r33_3, this:r33_2 # 33| mu33_5() = ^CallSideEffect : ~m? # 33| mu33_6(MyClass) = Store : &:r33_1, r33_2 # 34| r34_1() = FunctionAddress[F] : @@ -937,7 +937,7 @@ inoutref.cs: # 34| r34_4(glval) = VariableAddress[b] : # 34| r34_5(glval) = VariableAddress[c] : # 34| r34_6(glval) = VariableAddress[c] : -# 34| v34_7(Void) = Call : func:r34_1, 0:r34_2, 1:r34_3, 2:r34_4, 3:r34_5, 4:r34_6 +# 34| v34_7(Void) = Call[F] : func:r34_1, 0:r34_2, 1:r34_3, 2:r34_4, 3:r34_5, 4:r34_6 # 34| mu34_8() = ^CallSideEffect : ~m? # 36| r36_1(glval) = VariableAddress[x] : # 36| r36_2(glval) = VariableAddress[b] : @@ -1058,7 +1058,7 @@ jumps.cs: # 13| Block 6 # 13| r13_1() = FunctionAddress[WriteLine] : # 13| r13_2(String) = StringConstant["BreakAndContinue"] : -# 13| v13_3(Void) = Call : func:r13_1, 0:r13_2 +# 13| v13_3(Void) = Call[WriteLine] : func:r13_1, 0:r13_2 # 13| mu13_4() = ^CallSideEffect : ~m? #-----| Goto -> Block 19 @@ -1177,7 +1177,7 @@ jumps.cs: # 37| v37_1(Void) = NoOp : # 38| r38_1() = FunctionAddress[WriteLine] : # 38| r38_2(String) = StringConstant["Done"] : -# 38| v38_3(Void) = Call : func:r38_1, 0:r38_2 +# 38| v38_3(Void) = Call[WriteLine] : func:r38_1, 0:r38_2 # 38| mu38_4() = ^CallSideEffect : ~m? # 5| v5_3(Void) = ReturnVoid : # 5| v5_4(Void) = AliasedUse : ~m? @@ -1191,7 +1191,7 @@ lock.cs: # 7| r7_1(glval) = VariableAddress[object] : # 7| r7_2(Object) = NewObj : # 7| r7_3() = FunctionAddress[Object] : -# 7| v7_4(Void) = Call : func:r7_3, this:r7_2 +# 7| v7_4(Void) = Call[Object] : func:r7_3, this:r7_2 # 7| mu7_5() = ^CallSideEffect : ~m? # 7| mu7_6(Object) = Store : &:r7_1, r7_2 # 8| r8_1(glval) = VariableAddress[#temp8:9] : @@ -1205,15 +1205,15 @@ lock.cs: # 8| r8_9(glval) = VariableAddress[#temp8:9] : # 8| r8_10(Object) = Load : &:r8_9, ~m? # 8| r8_11(glval) = VariableAddress[#temp8:9] : -# 8| v8_12(Void) = Call : func:r8_8, 0:r8_10, 1:r8_11 +# 8| v8_12(Void) = Call[Enter] : func:r8_8, 0:r8_10, 1:r8_11 # 8| mu8_13() = ^CallSideEffect : ~m? # 10| r10_1() = FunctionAddress[WriteLine] : # 10| r10_2(glval) = VariableAddress[object] : # 10| r10_3(Object) = Load : &:r10_2, ~m? # 10| r10_4() = FunctionAddress[ToString] : -# 10| r10_5(String) = Call : func:r10_4, this:r10_3 +# 10| r10_5(String) = Call[ToString] : func:r10_4, this:r10_3 # 10| mu10_6() = ^CallSideEffect : ~m? -# 10| v10_7(Void) = Call : func:r10_1, 0:r10_5 +# 10| v10_7(Void) = Call[WriteLine] : func:r10_1, 0:r10_5 # 10| mu10_8() = ^CallSideEffect : ~m? # 8| r8_14(glval) = VariableAddress[#temp8:9] : # 8| r8_15(Boolean) = Load : &:r8_14, ~m? @@ -1230,7 +1230,7 @@ lock.cs: # 8| r8_17() = FunctionAddress[Exit] : # 8| r8_18(glval) = VariableAddress[#temp8:9] : # 8| r8_19(Object) = Load : &:r8_18, ~m? -# 8| v8_20(Void) = Call : func:r8_17, 0:r8_19 +# 8| v8_20(Void) = Call[Exit] : func:r8_17, 0:r8_19 # 8| mu8_21() = ^CallSideEffect : ~m? #-----| Goto -> Block 1 @@ -1280,13 +1280,13 @@ obj_creation.cs: # 23| r23_2(MyClass) = NewObj : # 23| r23_3() = FunctionAddress[MyClass] : # 23| r23_4(Int32) = Constant[100] : -# 23| v23_5(Void) = Call : func:r23_3, this:r23_2, 0:r23_4 +# 23| v23_5(Void) = Call[MyClass] : func:r23_3, this:r23_2, 0:r23_4 # 23| mu23_6() = ^CallSideEffect : ~m? # 23| mu23_7(MyClass) = Store : &:r23_1, r23_2 # 24| r24_1(glval) = VariableAddress[obj_initlist] : # 24| r24_2(MyClass) = NewObj : # 24| r24_3() = FunctionAddress[MyClass] : -# 24| v24_4(Void) = Call : func:r24_3, this:r24_2 +# 24| v24_4(Void) = Call[MyClass] : func:r24_3, this:r24_2 # 24| mu24_5() = ^CallSideEffect : ~m? # 24| r24_6(Int32) = Constant[101] : # 24| r24_7(glval) = FieldAddress[x] : r24_2 @@ -1302,9 +1302,9 @@ obj_creation.cs: # 27| r27_2(MyClass) = NewObj : # 27| r27_3() = FunctionAddress[MyClass] : # 27| r27_4(Int32) = Constant[100] : -# 27| v27_5(Void) = Call : func:r27_3, this:r27_2, 0:r27_4 +# 27| v27_5(Void) = Call[MyClass] : func:r27_3, this:r27_2, 0:r27_4 # 27| mu27_6() = ^CallSideEffect : ~m? -# 27| v27_7(Void) = Call : func:r27_1, 0:r27_2 +# 27| v27_7(Void) = Call[SomeFun] : func:r27_1, 0:r27_2 # 27| mu27_8() = ^CallSideEffect : ~m? # 21| v21_3(Void) = ReturnVoid : # 21| v21_4(Void) = AliasedUse : ~m? @@ -1321,7 +1321,7 @@ pointers.cs: # 5| r5_2(glval) = VariableAddress[arr] : # 5| r5_3(Int32[]) = Load : &:r5_2, ~m? # 5| r5_4() = FunctionAddress[get_Length] : -# 5| r5_5(Int32) = Call : func:r5_4, this:r5_3 +# 5| r5_5(Int32) = Call[get_Length] : func:r5_4, this:r5_3 # 5| mu5_6() = ^CallSideEffect : ~m? # 5| mu5_7(Int32) = Store : &:r5_1, r5_5 # 6| r6_1(glval) = VariableAddress[b] : @@ -1377,13 +1377,13 @@ pointers.cs: # 26| r26_1(glval) = VariableAddress[o] : # 26| r26_2(MyClass) = NewObj : # 26| r26_3() = FunctionAddress[MyClass] : -# 26| v26_4(Void) = Call : func:r26_3, this:r26_2 +# 26| v26_4(Void) = Call[MyClass] : func:r26_3, this:r26_2 # 26| mu26_5() = ^CallSideEffect : ~m? # 26| mu26_6(MyClass) = Store : &:r26_1, r26_2 # 27| r27_1(glval) = VariableAddress[s] : # 27| r27_2(MyStruct) = NewObj : # 27| r27_3() = FunctionAddress[MyStruct] : -# 27| v27_4(Void) = Call : func:r27_3, this:r27_2 +# 27| v27_4(Void) = Call[MyStruct] : func:r27_3, this:r27_2 # 27| mu27_5() = ^CallSideEffect : ~m? # 27| r27_6(MyStruct) = Load : &:r27_2, ~m? # 27| mu27_7(MyStruct) = Store : &:r27_1, r27_6 @@ -1431,7 +1431,7 @@ pointers.cs: # 40| r40_1() = FunctionAddress[addone] : # 40| r40_2(glval) = VariableAddress[arr] : # 40| r40_3(Int32[]) = Load : &:r40_2, ~m? -# 40| v40_4(Void) = Call : func:r40_1, 0:r40_3 +# 40| v40_4(Void) = Call[addone] : func:r40_1, 0:r40_3 # 40| mu40_5() = ^CallSideEffect : ~m? # 25| v25_3(Void) = ReturnVoid : # 25| v25_4(Void) = AliasedUse : ~m? @@ -1446,7 +1446,7 @@ prop.cs: # 9| r9_1(glval) = VariableAddress[#return] : # 9| r9_2(PropClass) = CopyValue : r7_3 # 9| r9_3() = FunctionAddress[func] : -# 9| r9_4(Int32) = Call : func:r9_3, this:r9_2 +# 9| r9_4(Int32) = Call[func] : func:r9_3, this:r9_2 # 9| mu9_5() = ^CallSideEffect : ~m? # 9| mu9_6(Int32) = Store : &:r9_1, r9_4 # 7| r7_4(glval) = VariableAddress[#return] : @@ -1489,20 +1489,20 @@ prop.cs: # 28| r28_1(glval) = VariableAddress[obj] : # 28| r28_2(PropClass) = NewObj : # 28| r28_3() = FunctionAddress[PropClass] : -# 28| v28_4(Void) = Call : func:r28_3, this:r28_2 +# 28| v28_4(Void) = Call[PropClass] : func:r28_3, this:r28_2 # 28| mu28_5() = ^CallSideEffect : ~m? # 28| mu28_6(PropClass) = Store : &:r28_1, r28_2 # 29| r29_1(glval) = VariableAddress[obj] : # 29| r29_2(PropClass) = Load : &:r29_1, ~m? # 29| r29_3() = FunctionAddress[set_Prop] : # 29| r29_4(Int32) = Constant[5] : -# 29| v29_5(Void) = Call : func:r29_3, this:r29_2, 0:r29_4 +# 29| v29_5(Void) = Call[set_Prop] : func:r29_3, this:r29_2, 0:r29_4 # 29| mu29_6() = ^CallSideEffect : ~m? # 30| r30_1(glval) = VariableAddress[x] : # 30| r30_2(glval) = VariableAddress[obj] : # 30| r30_3(PropClass) = Load : &:r30_2, ~m? # 30| r30_4() = FunctionAddress[get_Prop] : -# 30| r30_5(Int32) = Call : func:r30_4, this:r30_3 +# 30| r30_5(Int32) = Call[get_Prop] : func:r30_4, this:r30_3 # 30| mu30_6() = ^CallSideEffect : ~m? # 30| mu30_7(Int32) = Store : &:r30_1, r30_5 # 26| v26_3(Void) = ReturnVoid : @@ -1529,7 +1529,7 @@ simple_call.cs: # 10| r10_3(glval) = InitializeThis : # 12| r12_1(glval) = VariableAddress[#return] : # 12| r12_2() = FunctionAddress[f] : -# 12| r12_3(Int32) = Call : func:r12_2 +# 12| r12_3(Int32) = Call[f] : func:r12_2 # 12| mu12_4() = ^CallSideEffect : ~m? # 12| mu12_5(Int32) = Store : &:r12_1, r12_3 # 10| r10_4(glval) = VariableAddress[#return] : @@ -1624,7 +1624,7 @@ stmts.cs: # 24| r24_1(glval) = VariableAddress[caseSwitch] : # 24| r24_2(Object) = NewObj : # 24| r24_3() = FunctionAddress[Object] : -# 24| v24_4(Void) = Call : func:r24_3, this:r24_2 +# 24| v24_4(Void) = Call[Object] : func:r24_3, this:r24_2 # 24| mu24_5() = ^CallSideEffect : ~m? # 24| mu24_6(Object) = Store : &:r24_1, r24_2 # 25| r25_1(glval) = VariableAddress[select] : @@ -1709,7 +1709,7 @@ stmts.cs: # 52| r52_1(glval) = VariableAddress[#throw52:17] : # 52| r52_2(Exception) = NewObj : # 52| r52_3() = FunctionAddress[Exception] : -# 52| v52_4(Void) = Call : func:r52_3, this:r52_2 +# 52| v52_4(Void) = Call[Exception] : func:r52_3, this:r52_2 # 52| mu52_5() = ^CallSideEffect : ~m? # 52| mu52_6(Exception) = Store : &:r52_1, r52_2 # 52| v52_7(Void) = ThrowValue : &:r52_1, ~m? @@ -1919,35 +1919,35 @@ using.cs: # 14| r14_1(glval) = VariableAddress[o1] : # 14| r14_2(MyDisposable) = NewObj : # 14| r14_3() = FunctionAddress[MyDisposable] : -# 14| v14_4(Void) = Call : func:r14_3, this:r14_2 +# 14| v14_4(Void) = Call[MyDisposable] : func:r14_3, this:r14_2 # 14| mu14_5() = ^CallSideEffect : ~m? # 14| mu14_6(MyDisposable) = Store : &:r14_1, r14_2 # 16| r16_1(glval) = VariableAddress[o1] : # 16| r16_2(MyDisposable) = Load : &:r16_1, ~m? # 16| r16_3() = FunctionAddress[DoSomething] : -# 16| v16_4(Void) = Call : func:r16_3, this:r16_2 +# 16| v16_4(Void) = Call[DoSomething] : func:r16_3, this:r16_2 # 16| mu16_5() = ^CallSideEffect : ~m? # 19| r19_1(glval) = VariableAddress[o2] : # 19| r19_2(MyDisposable) = NewObj : # 19| r19_3() = FunctionAddress[MyDisposable] : -# 19| v19_4(Void) = Call : func:r19_3, this:r19_2 +# 19| v19_4(Void) = Call[MyDisposable] : func:r19_3, this:r19_2 # 19| mu19_5() = ^CallSideEffect : ~m? # 19| mu19_6(MyDisposable) = Store : &:r19_1, r19_2 # 22| r22_1(glval) = VariableAddress[o2] : # 22| r22_2(MyDisposable) = Load : &:r22_1, ~m? # 22| r22_3() = FunctionAddress[DoSomething] : -# 22| v22_4(Void) = Call : func:r22_3, this:r22_2 +# 22| v22_4(Void) = Call[DoSomething] : func:r22_3, this:r22_2 # 22| mu22_5() = ^CallSideEffect : ~m? # 25| r25_1(glval) = VariableAddress[o3] : # 25| r25_2(MyDisposable) = NewObj : # 25| r25_3() = FunctionAddress[MyDisposable] : -# 25| v25_4(Void) = Call : func:r25_3, this:r25_2 +# 25| v25_4(Void) = Call[MyDisposable] : func:r25_3, this:r25_2 # 25| mu25_5() = ^CallSideEffect : ~m? # 25| mu25_6(MyDisposable) = Store : &:r25_1, r25_2 # 26| r26_1(glval) = VariableAddress[o3] : # 26| r26_2(MyDisposable) = Load : &:r26_1, ~m? # 26| r26_3() = FunctionAddress[DoSomething] : -# 26| v26_4(Void) = Call : func:r26_3, this:r26_2 +# 26| v26_4(Void) = Call[DoSomething] : func:r26_3, this:r26_2 # 26| mu26_5() = ^CallSideEffect : ~m? # 12| v12_3(Void) = ReturnVoid : # 12| v12_4(Void) = AliasedUse : ~m? From 4137d3f97155749240200eada286ab875363cf4f Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 16 Oct 2020 17:19:48 +0100 Subject: [PATCH 159/166] JS: Split CWE-079 tests into their own folders --- .../Security/CWE-079/Consistency.ql | 8 ---- .../ConsistencyDomBasedXss.expected} | 0 .../DomBasedXss/ConsistencyDomBasedXss.ql | 3 ++ .../CWE-079/{ => DomBasedXss}/Xss.expected | 10 ---- .../CWE-079/{ => DomBasedXss}/Xss.qlref | 0 .../XssWithAdditionalSources.expected | 9 ---- .../XssWithAdditionalSources.ql | 0 .../{ => DomBasedXss}/addEventListener.js | 0 .../{ => DomBasedXss}/angular2-client.ts | 0 .../CWE-079/{ => DomBasedXss}/encodeuri.js | 0 .../CWE-079/{ => DomBasedXss}/externs.js | 0 .../CWE-079/{ => DomBasedXss}/jquery.js | 0 .../CWE-079/{ => DomBasedXss}/nodemailer.js | 0 .../{ => DomBasedXss}/optionalSanitizer.js | 0 .../CWE-079/{ => DomBasedXss}/react-native.js | 0 .../CWE-079/{ => DomBasedXss}/sanitiser.js | 0 .../CWE-079/{ => DomBasedXss}/stored-xss.js | 0 .../{ => DomBasedXss}/string-manipulations.js | 0 .../CWE-079/{ => DomBasedXss}/translate.js | 0 .../Security/CWE-079/{ => DomBasedXss}/tst.js | 0 .../CWE-079/{ => DomBasedXss}/tst3.js | 0 .../CWE-079/{ => DomBasedXss}/typeahead.js | 0 .../CWE-079/{ => DomBasedXss}/v-html.vue | 0 .../CWE-079/{ => DomBasedXss}/winjs.js | 0 .../ConsistencyExceptionXss.expected | 0 .../ExceptionXss/ConsistencyExceptionXss.ql | 3 ++ .../{ => ExceptionXss}/ExceptionXss.expected | 20 -------- .../{ => ExceptionXss}/ExceptionXss.qlref | 0 .../{ => ExceptionXss}/exception-xss.js | 0 .../Security/CWE-079/ExceptionXss/externs.js | 48 +++++++++++++++++++ .../ConsistencyReflectedXss.expected | 0 .../ReflectedXss/ConsistencyReflectedXss.ql | 3 ++ .../{ => ReflectedXss}/ReflectedXss.expected | 5 -- .../{ => ReflectedXss}/ReflectedXss.js | 0 .../{ => ReflectedXss}/ReflectedXss.qlref | 0 .../ReflectedXssContentTypes.js | 0 .../{ => ReflectedXss}/ReflectedXssGood.js | 0 .../{ => ReflectedXss}/ReflectedXssGood2.js | 0 .../{ => ReflectedXss}/ReflectedXssGood3.js | 0 .../ReflectedXssWithCustomSanitizer.expected | 1 - .../ReflectedXssWithCustomSanitizer.ql | 0 .../CWE-079/{ => ReflectedXss}/cookies.js | 0 .../CWE-079/{ => ReflectedXss}/etherpad.js | 0 .../Security/CWE-079/ReflectedXss/externs.js | 48 +++++++++++++++++++ .../CWE-079/{ => ReflectedXss}/formatting.js | 0 .../CWE-079/{ => ReflectedXss}/partial.js | 0 .../CWE-079/{ => ReflectedXss}/promises.js | 0 .../CWE-079/{ => ReflectedXss}/tst2.js | 0 .../StoredXss/ConsistencyStoredXss.expected | 0 .../CWE-079/StoredXss/ConsistencyStoredXss.ql | 3 ++ .../{ => StoredXss}/StoredXss.expected | 0 .../CWE-079/{ => StoredXss}/StoredXss.qlref | 0 .../Security/CWE-079/StoredXss/externs.js | 48 +++++++++++++++++++ .../{ => StoredXss}/xss-through-filenames.js | 0 .../{ => StoredXss}/xss-through-torrent.js | 0 .../ConsistencyUnsafeJQueryPlugin.expected | 0 .../ConsistencyUnsafeJQueryPlugin.ql | 3 ++ .../UnsafeJQueryPlugin.expected | 0 .../UnsafeJQueryPlugin.qlref | 0 .../CWE-079/UnsafeJQueryPlugin/externs.js | 48 +++++++++++++++++++ .../unsafe-jquery-plugin.js | 0 .../ConsistencyXssThroughDom.expected | 0 .../XssThroughDom/ConsistencyXssThroughDom.ql | 3 ++ .../XssThroughDom.expected | 0 .../{ => XssThroughDom}/XssThroughDom.qlref | 0 .../Security/CWE-079/XssThroughDom/externs.js | 48 +++++++++++++++++++ .../{ => XssThroughDom}/xss-through-dom.js | 0 67 files changed, 258 insertions(+), 53 deletions(-) delete mode 100644 javascript/ql/test/query-tests/Security/CWE-079/Consistency.ql rename javascript/ql/test/query-tests/Security/CWE-079/{Consistency.expected => DomBasedXss/ConsistencyDomBasedXss.expected} (100%) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/ConsistencyDomBasedXss.ql rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/Xss.expected (99%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/Xss.qlref (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/XssWithAdditionalSources.expected (99%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/XssWithAdditionalSources.ql (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/addEventListener.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/angular2-client.ts (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/encodeuri.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/externs.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/jquery.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/nodemailer.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/optionalSanitizer.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/react-native.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/sanitiser.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/stored-xss.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/string-manipulations.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/translate.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/tst.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/tst3.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/typeahead.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/v-html.vue (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => DomBasedXss}/winjs.js (100%) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ConsistencyExceptionXss.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ConsistencyExceptionXss.ql rename javascript/ql/test/query-tests/Security/CWE-079/{ => ExceptionXss}/ExceptionXss.expected (92%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ExceptionXss}/ExceptionXss.qlref (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ExceptionXss}/exception-xss.js (100%) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/externs.js create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ConsistencyReflectedXss.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ConsistencyReflectedXss.ql rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/ReflectedXss.expected (96%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/ReflectedXss.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/ReflectedXss.qlref (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/ReflectedXssContentTypes.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/ReflectedXssGood.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/ReflectedXssGood2.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/ReflectedXssGood3.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/ReflectedXssWithCustomSanitizer.expected (94%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/ReflectedXssWithCustomSanitizer.ql (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/cookies.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/etherpad.js (100%) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/externs.js rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/formatting.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/partial.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/promises.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => ReflectedXss}/tst2.js (100%) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/StoredXss/ConsistencyStoredXss.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/StoredXss/ConsistencyStoredXss.ql rename javascript/ql/test/query-tests/Security/CWE-079/{ => StoredXss}/StoredXss.expected (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => StoredXss}/StoredXss.qlref (100%) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/StoredXss/externs.js rename javascript/ql/test/query-tests/Security/CWE-079/{ => StoredXss}/xss-through-filenames.js (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => StoredXss}/xss-through-torrent.js (100%) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/ConsistencyUnsafeJQueryPlugin.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/ConsistencyUnsafeJQueryPlugin.ql rename javascript/ql/test/query-tests/Security/CWE-079/{ => UnsafeJQueryPlugin}/UnsafeJQueryPlugin.expected (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => UnsafeJQueryPlugin}/UnsafeJQueryPlugin.qlref (100%) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/externs.js rename javascript/ql/test/query-tests/Security/CWE-079/{ => UnsafeJQueryPlugin}/unsafe-jquery-plugin.js (100%) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/ConsistencyXssThroughDom.expected create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/ConsistencyXssThroughDom.ql rename javascript/ql/test/query-tests/Security/CWE-079/{ => XssThroughDom}/XssThroughDom.expected (100%) rename javascript/ql/test/query-tests/Security/CWE-079/{ => XssThroughDom}/XssThroughDom.qlref (100%) create mode 100644 javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/externs.js rename javascript/ql/test/query-tests/Security/CWE-079/{ => XssThroughDom}/xss-through-dom.js (100%) diff --git a/javascript/ql/test/query-tests/Security/CWE-079/Consistency.ql b/javascript/ql/test/query-tests/Security/CWE-079/Consistency.ql deleted file mode 100644 index 8cc4fdc580b..00000000000 --- a/javascript/ql/test/query-tests/Security/CWE-079/Consistency.ql +++ /dev/null @@ -1,8 +0,0 @@ -import javascript -import testUtilities.ConsistencyChecking -import semmle.javascript.security.dataflow.DomBasedXss as DomXss -import semmle.javascript.security.dataflow.ReflectedXss as ReflectedXss -import semmle.javascript.security.dataflow.StoredXss as StoredXss -import semmle.javascript.security.dataflow.XssThroughDom as ThroughDomXss -import semmle.javascript.security.dataflow.ExceptionXss as ExceptionXss -import semmle.javascript.security.dataflow.UnsafeJQueryPlugin as UnsafeJqueryPlugin diff --git a/javascript/ql/test/query-tests/Security/CWE-079/Consistency.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/ConsistencyDomBasedXss.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/Consistency.expected rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/ConsistencyDomBasedXss.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/ConsistencyDomBasedXss.ql b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/ConsistencyDomBasedXss.ql new file mode 100644 index 00000000000..5144f45abc7 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/ConsistencyDomBasedXss.ql @@ -0,0 +1,3 @@ +import javascript +import testUtilities.ConsistencyChecking +import semmle.javascript.security.dataflow.DomBasedXss as DomXss diff --git a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected similarity index 99% rename from javascript/ql/test/query-tests/Security/CWE-079/Xss.expected rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected index 32f378eb504..ffdf900944c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected @@ -59,11 +59,6 @@ nodes | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | -| exception-xss.js:2:6:2:28 | foo | -| exception-xss.js:2:12:2:28 | document.location | -| exception-xss.js:2:12:2:28 | document.location | -| exception-xss.js:86:17:86:19 | foo | -| exception-xss.js:86:17:86:19 | foo | | jquery.js:2:7:2:40 | tainted | | jquery.js:2:17:2:33 | document.location | | jquery.js:2:17:2:33 | document.location | @@ -577,10 +572,6 @@ edges | angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | | angular2-client.ts:37:44:37:58 | this.router.url | angular2-client.ts:37:44:37:58 | this.router.url | | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | -| exception-xss.js:2:6:2:28 | foo | exception-xss.js:86:17:86:19 | foo | -| exception-xss.js:2:6:2:28 | foo | exception-xss.js:86:17:86:19 | foo | -| exception-xss.js:2:12:2:28 | document.location | exception-xss.js:2:6:2:28 | foo | -| exception-xss.js:2:12:2:28 | document.location | exception-xss.js:2:6:2:28 | foo | | jquery.js:2:7:2:40 | tainted | jquery.js:4:5:4:11 | tainted | | jquery.js:2:7:2:40 | tainted | jquery.js:4:5:4:11 | tainted | | jquery.js:2:7:2:40 | tainted | jquery.js:7:20:7:26 | tainted | @@ -1022,7 +1013,6 @@ edges | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | Cross-site scripting vulnerability due to $@. | angular2-client.ts:35:44:35:89 | this.ro ... .params | user-provided value | | angular2-client.ts:37:44:37:58 | this.router.url | angular2-client.ts:37:44:37:58 | this.router.url | angular2-client.ts:37:44:37:58 | this.router.url | Cross-site scripting vulnerability due to $@. | angular2-client.ts:37:44:37:58 | this.router.url | user-provided value | | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | Cross-site scripting vulnerability due to $@. | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | user-provided value | -| exception-xss.js:86:17:86:19 | foo | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:86:17:86:19 | foo | Cross-site scripting vulnerability due to $@. | exception-xss.js:2:12:2:28 | document.location | user-provided value | | jquery.js:4:5:4:11 | tainted | jquery.js:2:17:2:33 | document.location | jquery.js:4:5:4:11 | tainted | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:33 | document.location | user-provided value | | jquery.js:7:5:7:34 | "
      " | jquery.js:2:17:2:33 | document.location | jquery.js:7:5:7:34 | "
      " | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:33 | document.location | user-provided value | | jquery.js:8:18:8:34 | "XSS: " + tainted | jquery.js:2:17:2:33 | document.location | jquery.js:8:18:8:34 | "XSS: " + tainted | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:33 | document.location | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/Xss.qlref b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/Xss.qlref rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected similarity index 99% rename from javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected index ac7a05a3b1e..ec54e329e69 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected @@ -59,11 +59,6 @@ nodes | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | -| exception-xss.js:2:6:2:28 | foo | -| exception-xss.js:2:12:2:28 | document.location | -| exception-xss.js:2:12:2:28 | document.location | -| exception-xss.js:86:17:86:19 | foo | -| exception-xss.js:86:17:86:19 | foo | | jquery.js:2:7:2:40 | tainted | | jquery.js:2:17:2:33 | document.location | | jquery.js:2:17:2:33 | document.location | @@ -581,10 +576,6 @@ edges | angular2-client.ts:35:44:35:89 | this.ro ... .params | angular2-client.ts:35:44:35:91 | this.ro ... arams.x | | angular2-client.ts:37:44:37:58 | this.router.url | angular2-client.ts:37:44:37:58 | this.router.url | | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | angular2-client.ts:41:44:41:76 | routeSn ... ('foo') | -| exception-xss.js:2:6:2:28 | foo | exception-xss.js:86:17:86:19 | foo | -| exception-xss.js:2:6:2:28 | foo | exception-xss.js:86:17:86:19 | foo | -| exception-xss.js:2:12:2:28 | document.location | exception-xss.js:2:6:2:28 | foo | -| exception-xss.js:2:12:2:28 | document.location | exception-xss.js:2:6:2:28 | foo | | jquery.js:2:7:2:40 | tainted | jquery.js:4:5:4:11 | tainted | | jquery.js:2:7:2:40 | tainted | jquery.js:4:5:4:11 | tainted | | jquery.js:2:7:2:40 | tainted | jquery.js:7:20:7:26 | tainted | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.ql b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.ql similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.ql rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-079/addEventListener.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/addEventListener.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/addEventListener.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/addEventListener.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/angular2-client.ts b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/angular2-client.ts similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/angular2-client.ts rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/angular2-client.ts diff --git a/javascript/ql/test/query-tests/Security/CWE-079/encodeuri.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/encodeuri.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/encodeuri.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/encodeuri.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/externs.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/externs.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/externs.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/externs.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/jquery.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/jquery.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/jquery.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/nodemailer.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/nodemailer.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/nodemailer.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/nodemailer.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/optionalSanitizer.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/optionalSanitizer.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/optionalSanitizer.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/optionalSanitizer.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/react-native.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/react-native.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/react-native.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/react-native.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/sanitiser.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/sanitiser.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/sanitiser.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/sanitiser.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/stored-xss.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/stored-xss.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/stored-xss.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/stored-xss.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/string-manipulations.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/string-manipulations.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/string-manipulations.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/string-manipulations.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/translate.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/translate.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/translate.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/translate.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/tst.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/tst.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/tst.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/tst.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/tst3.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/tst3.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/tst3.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/tst3.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/typeahead.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/typeahead.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/typeahead.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/typeahead.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/v-html.vue b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/v-html.vue similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/v-html.vue rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/v-html.vue diff --git a/javascript/ql/test/query-tests/Security/CWE-079/winjs.js b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/winjs.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/winjs.js rename to javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/winjs.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ConsistencyExceptionXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ConsistencyExceptionXss.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ConsistencyExceptionXss.ql b/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ConsistencyExceptionXss.ql new file mode 100644 index 00000000000..584faa27c11 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ConsistencyExceptionXss.ql @@ -0,0 +1,3 @@ +import javascript +import testUtilities.ConsistencyChecking +import semmle.javascript.security.dataflow.ExceptionXss as ExceptionXss diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ExceptionXss.expected similarity index 92% rename from javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss.expected rename to javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ExceptionXss.expected index 597266b0eef..aba89bbe379 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ExceptionXss.expected @@ -86,16 +86,6 @@ nodes | exception-xss.js:180:26:180:30 | error | | exception-xss.js:182:19:182:23 | error | | exception-xss.js:182:19:182:23 | error | -| tst.js:301:9:301:16 | location | -| tst.js:301:9:301:16 | location | -| tst.js:302:10:302:10 | e | -| tst.js:303:20:303:20 | e | -| tst.js:303:20:303:20 | e | -| tst.js:308:10:308:17 | location | -| tst.js:308:10:308:17 | location | -| tst.js:310:10:310:10 | e | -| tst.js:311:20:311:20 | e | -| tst.js:311:20:311:20 | e | edges | exception-xss.js:2:6:2:28 | foo | exception-xss.js:9:11:9:13 | foo | | exception-xss.js:2:6:2:28 | foo | exception-xss.js:15:9:15:11 | foo | @@ -178,14 +168,6 @@ edges | exception-xss.js:180:10:180:22 | req.params.id | exception-xss.js:180:26:180:30 | error | | exception-xss.js:180:26:180:30 | error | exception-xss.js:182:19:182:23 | error | | exception-xss.js:180:26:180:30 | error | exception-xss.js:182:19:182:23 | error | -| tst.js:301:9:301:16 | location | tst.js:302:10:302:10 | e | -| tst.js:301:9:301:16 | location | tst.js:302:10:302:10 | e | -| tst.js:302:10:302:10 | e | tst.js:303:20:303:20 | e | -| tst.js:302:10:302:10 | e | tst.js:303:20:303:20 | e | -| tst.js:308:10:308:17 | location | tst.js:310:10:310:10 | e | -| tst.js:308:10:308:17 | location | tst.js:310:10:310:10 | e | -| tst.js:310:10:310:10 | e | tst.js:311:20:311:20 | e | -| tst.js:310:10:310:10 | e | tst.js:311:20:311:20 | e | #select | exception-xss.js:11:18:11:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:11:18:11:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:2:12:2:28 | document.location | Exception text | | exception-xss.js:17:18:17:18 | e | exception-xss.js:2:12:2:28 | document.location | exception-xss.js:17:18:17:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:2:12:2:28 | document.location | Exception text | @@ -203,5 +185,3 @@ edges | exception-xss.js:155:18:155:18 | e | exception-xss.js:146:12:146:28 | document.location | exception-xss.js:155:18:155:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:146:12:146:28 | document.location | Exception text | | exception-xss.js:175:18:175:18 | e | exception-xss.js:146:12:146:28 | document.location | exception-xss.js:175:18:175:18 | e | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:146:12:146:28 | document.location | Exception text | | exception-xss.js:182:19:182:23 | error | exception-xss.js:180:10:180:22 | req.params.id | exception-xss.js:182:19:182:23 | error | $@ is reinterpreted as HTML without escaping meta-characters. | exception-xss.js:180:10:180:22 | req.params.id | Exception text | -| tst.js:303:20:303:20 | e | tst.js:301:9:301:16 | location | tst.js:303:20:303:20 | e | $@ is reinterpreted as HTML without escaping meta-characters. | tst.js:301:9:301:16 | location | Exception text | -| tst.js:311:20:311:20 | e | tst.js:308:10:308:17 | location | tst.js:311:20:311:20 | e | $@ is reinterpreted as HTML without escaping meta-characters. | tst.js:308:10:308:17 | location | Exception text | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss.qlref b/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ExceptionXss.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss.qlref rename to javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/ExceptionXss.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-079/exception-xss.js b/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/exception-xss.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/exception-xss.js rename to javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/exception-xss.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/externs.js b/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/externs.js new file mode 100644 index 00000000000..7259b9b216d --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/ExceptionXss/externs.js @@ -0,0 +1,48 @@ +// Adapted from the Google Closure externs; original copyright header included below. +/* + * Copyright 2008 The Closure Compiler Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @externs + */ + +/** + * @interface + */ +function EventTarget() {} + +/** + * Stub for the DOM hierarchy. + * + * @constructor + * @extends {EventTarget} + */ +function DomObjectStub() {} + +/** + * @type {!DomObjectStub} + */ +DomObjectStub.prototype.body; + +/** + * @type {!DomObjectStub} + */ +DomObjectStub.prototype.value; + +/** + * @type {!DomObjectStub} + */ +var document; diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ConsistencyReflectedXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ConsistencyReflectedXss.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ConsistencyReflectedXss.ql b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ConsistencyReflectedXss.ql new file mode 100644 index 00000000000..76785917fb5 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ConsistencyReflectedXss.ql @@ -0,0 +1,3 @@ +import javascript +import testUtilities.ConsistencyChecking +import semmle.javascript.security.dataflow.ReflectedXss as ReflectedXss diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected similarity index 96% rename from javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected index b0b73f732c3..bc28be8d730 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected @@ -35,9 +35,6 @@ nodes | etherpad.js:9:16:9:53 | req.que ... e + ")" | | etherpad.js:11:12:11:19 | response | | etherpad.js:11:12:11:19 | response | -| exception-xss.js:190:12:190:24 | req.params.id | -| exception-xss.js:190:12:190:24 | req.params.id | -| exception-xss.js:190:12:190:24 | req.params.id | | formatting.js:4:9:4:29 | evil | | formatting.js:4:16:4:29 | req.query.evil | | formatting.js:4:16:4:29 | req.query.evil | @@ -129,7 +126,6 @@ edges | etherpad.js:9:16:9:30 | req.query.jsonp | etherpad.js:9:16:9:53 | req.que ... e + ")" | | etherpad.js:9:16:9:30 | req.query.jsonp | etherpad.js:9:16:9:53 | req.que ... e + ")" | | etherpad.js:9:16:9:53 | req.que ... e + ")" | etherpad.js:9:5:9:53 | response | -| exception-xss.js:190:12:190:24 | req.params.id | exception-xss.js:190:12:190:24 | req.params.id | | formatting.js:4:9:4:29 | evil | formatting.js:6:43:6:46 | evil | | formatting.js:4:9:4:29 | evil | formatting.js:7:49:7:52 | evil | | formatting.js:4:16:4:29 | req.query.evil | formatting.js:4:9:4:29 | evil | @@ -188,7 +184,6 @@ edges | ReflectedXssContentTypes.js:70:12:70:34 | "FOO: " ... rams.id | ReflectedXssContentTypes.js:70:22:70:34 | req.params.id | ReflectedXssContentTypes.js:70:12:70:34 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:70:22:70:34 | req.params.id | user-provided value | | ReflectedXssGood3.js:139:12:139:27 | escapeHtml3(url) | ReflectedXssGood3.js:135:15:135:27 | req.params.id | ReflectedXssGood3.js:139:12:139:27 | escapeHtml3(url) | Cross-site scripting vulnerability due to $@. | ReflectedXssGood3.js:135:15:135:27 | req.params.id | user-provided value | | etherpad.js:11:12:11:19 | response | etherpad.js:9:16:9:30 | req.query.jsonp | etherpad.js:11:12:11:19 | response | Cross-site scripting vulnerability due to $@. | etherpad.js:9:16:9:30 | req.query.jsonp | user-provided value | -| exception-xss.js:190:12:190:24 | req.params.id | exception-xss.js:190:12:190:24 | req.params.id | exception-xss.js:190:12:190:24 | req.params.id | Cross-site scripting vulnerability due to $@. | exception-xss.js:190:12:190:24 | req.params.id | user-provided value | | formatting.js:6:14:6:47 | util.fo ... , evil) | formatting.js:4:16:4:29 | req.query.evil | formatting.js:6:14:6:47 | util.fo ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value | | formatting.js:7:14:7:53 | require ... , evil) | formatting.js:4:16:4:29 | req.query.evil | formatting.js:7:14:7:53 | require ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value | | partial.js:10:14:10:18 | x + y | partial.js:13:42:13:48 | req.url | partial.js:10:14:10:18 | x + y | Cross-site scripting vulnerability due to $@. | partial.js:13:42:13:48 | req.url | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.js rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.qlref b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss.qlref rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssContentTypes.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssContentTypes.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssContentTypes.js rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssContentTypes.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssGood.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssGood.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssGood.js rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssGood.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssGood2.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssGood2.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssGood2.js rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssGood2.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssGood3.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssGood3.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssGood3.js rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssGood3.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.expected b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected similarity index 94% rename from javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.expected rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected index e1b55b7b825..b35bbd53c1b 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected @@ -5,7 +5,6 @@ | ReflectedXssContentTypes.js:39:13:39:35 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:39:23:39:35 | req.params.id | user-provided value | | ReflectedXssContentTypes.js:70:12:70:34 | "FOO: " ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXssContentTypes.js:70:22:70:34 | req.params.id | user-provided value | | ReflectedXssGood3.js:139:12:139:27 | escapeHtml3(url) | Cross-site scripting vulnerability due to $@. | ReflectedXssGood3.js:135:15:135:27 | req.params.id | user-provided value | -| exception-xss.js:190:12:190:24 | req.params.id | Cross-site scripting vulnerability due to $@. | exception-xss.js:190:12:190:24 | req.params.id | user-provided value | | formatting.js:6:14:6:47 | util.fo ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value | | formatting.js:7:14:7:53 | require ... , evil) | Cross-site scripting vulnerability due to $@. | formatting.js:4:16:4:29 | req.query.evil | user-provided value | | partial.js:10:14:10:18 | x + y | Cross-site scripting vulnerability due to $@. | partial.js:13:42:13:48 | req.url | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.ql b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.ql similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/ReflectedXssWithCustomSanitizer.ql rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-079/cookies.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/cookies.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/cookies.js rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/cookies.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/etherpad.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/etherpad.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/etherpad.js rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/etherpad.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/externs.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/externs.js new file mode 100644 index 00000000000..7259b9b216d --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/externs.js @@ -0,0 +1,48 @@ +// Adapted from the Google Closure externs; original copyright header included below. +/* + * Copyright 2008 The Closure Compiler Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @externs + */ + +/** + * @interface + */ +function EventTarget() {} + +/** + * Stub for the DOM hierarchy. + * + * @constructor + * @extends {EventTarget} + */ +function DomObjectStub() {} + +/** + * @type {!DomObjectStub} + */ +DomObjectStub.prototype.body; + +/** + * @type {!DomObjectStub} + */ +DomObjectStub.prototype.value; + +/** + * @type {!DomObjectStub} + */ +var document; diff --git a/javascript/ql/test/query-tests/Security/CWE-079/formatting.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/formatting.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/formatting.js rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/formatting.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/partial.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/partial.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/partial.js rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/partial.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/promises.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/promises.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/promises.js rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/promises.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/tst2.js b/javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/tst2.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/tst2.js rename to javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/tst2.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/ConsistencyStoredXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/ConsistencyStoredXss.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/ConsistencyStoredXss.ql b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/ConsistencyStoredXss.ql new file mode 100644 index 00000000000..63794389bab --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/ConsistencyStoredXss.ql @@ -0,0 +1,3 @@ +import javascript +import testUtilities.ConsistencyChecking +import semmle.javascript.security.dataflow.StoredXss as StoredXss diff --git a/javascript/ql/test/query-tests/Security/CWE-079/StoredXss.expected b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/StoredXss.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/StoredXss.expected rename to javascript/ql/test/query-tests/Security/CWE-079/StoredXss/StoredXss.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-079/StoredXss.qlref b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/StoredXss.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/StoredXss.qlref rename to javascript/ql/test/query-tests/Security/CWE-079/StoredXss/StoredXss.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/externs.js b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/externs.js new file mode 100644 index 00000000000..7259b9b216d --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/externs.js @@ -0,0 +1,48 @@ +// Adapted from the Google Closure externs; original copyright header included below. +/* + * Copyright 2008 The Closure Compiler Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @externs + */ + +/** + * @interface + */ +function EventTarget() {} + +/** + * Stub for the DOM hierarchy. + * + * @constructor + * @extends {EventTarget} + */ +function DomObjectStub() {} + +/** + * @type {!DomObjectStub} + */ +DomObjectStub.prototype.body; + +/** + * @type {!DomObjectStub} + */ +DomObjectStub.prototype.value; + +/** + * @type {!DomObjectStub} + */ +var document; diff --git a/javascript/ql/test/query-tests/Security/CWE-079/xss-through-filenames.js b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/xss-through-filenames.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/xss-through-filenames.js rename to javascript/ql/test/query-tests/Security/CWE-079/StoredXss/xss-through-filenames.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/xss-through-torrent.js b/javascript/ql/test/query-tests/Security/CWE-079/StoredXss/xss-through-torrent.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/xss-through-torrent.js rename to javascript/ql/test/query-tests/Security/CWE-079/StoredXss/xss-through-torrent.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/ConsistencyUnsafeJQueryPlugin.expected b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/ConsistencyUnsafeJQueryPlugin.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/ConsistencyUnsafeJQueryPlugin.ql b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/ConsistencyUnsafeJQueryPlugin.ql new file mode 100644 index 00000000000..6a16badb37f --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/ConsistencyUnsafeJQueryPlugin.ql @@ -0,0 +1,3 @@ +import javascript +import testUtilities.ConsistencyChecking +import semmle.javascript.security.dataflow.UnsafeJQueryPlugin as UnsafeJqueryPlugin diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin.expected b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/UnsafeJQueryPlugin.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin.expected rename to javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/UnsafeJQueryPlugin.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin.qlref b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/UnsafeJQueryPlugin.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin.qlref rename to javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/UnsafeJQueryPlugin.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/externs.js b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/externs.js new file mode 100644 index 00000000000..7259b9b216d --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/externs.js @@ -0,0 +1,48 @@ +// Adapted from the Google Closure externs; original copyright header included below. +/* + * Copyright 2008 The Closure Compiler Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @externs + */ + +/** + * @interface + */ +function EventTarget() {} + +/** + * Stub for the DOM hierarchy. + * + * @constructor + * @extends {EventTarget} + */ +function DomObjectStub() {} + +/** + * @type {!DomObjectStub} + */ +DomObjectStub.prototype.body; + +/** + * @type {!DomObjectStub} + */ +DomObjectStub.prototype.value; + +/** + * @type {!DomObjectStub} + */ +var document; diff --git a/javascript/ql/test/query-tests/Security/CWE-079/unsafe-jquery-plugin.js b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/unsafe-jquery-plugin.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/unsafe-jquery-plugin.js rename to javascript/ql/test/query-tests/Security/CWE-079/UnsafeJQueryPlugin/unsafe-jquery-plugin.js diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/ConsistencyXssThroughDom.expected b/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/ConsistencyXssThroughDom.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/ConsistencyXssThroughDom.ql b/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/ConsistencyXssThroughDom.ql new file mode 100644 index 00000000000..118a875f15d --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/ConsistencyXssThroughDom.ql @@ -0,0 +1,3 @@ +import javascript +import testUtilities.ConsistencyChecking +import semmle.javascript.security.dataflow.XssThroughDom as ThroughDomXss diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom.expected b/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/XssThroughDom.expected similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom.expected rename to javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/XssThroughDom.expected diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom.qlref b/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/XssThroughDom.qlref similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom.qlref rename to javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/XssThroughDom.qlref diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/externs.js b/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/externs.js new file mode 100644 index 00000000000..7259b9b216d --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/externs.js @@ -0,0 +1,48 @@ +// Adapted from the Google Closure externs; original copyright header included below. +/* + * Copyright 2008 The Closure Compiler Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @externs + */ + +/** + * @interface + */ +function EventTarget() {} + +/** + * Stub for the DOM hierarchy. + * + * @constructor + * @extends {EventTarget} + */ +function DomObjectStub() {} + +/** + * @type {!DomObjectStub} + */ +DomObjectStub.prototype.body; + +/** + * @type {!DomObjectStub} + */ +DomObjectStub.prototype.value; + +/** + * @type {!DomObjectStub} + */ +var document; diff --git a/javascript/ql/test/query-tests/Security/CWE-079/xss-through-dom.js b/javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/xss-through-dom.js similarity index 100% rename from javascript/ql/test/query-tests/Security/CWE-079/xss-through-dom.js rename to javascript/ql/test/query-tests/Security/CWE-079/XssThroughDom/xss-through-dom.js From f0034138ce3de4e133cc19dc49efe5ff25419474 Mon Sep 17 00:00:00 2001 From: Asger Feldthaus Date: Fri, 16 Oct 2020 18:13:13 +0100 Subject: [PATCH 160/166] JS: Fix DefaultFlowLabels test --- .../{DefaulFlowLabels.expected => DefaultFlowLabels.expected} | 0 .../FlowLabels/{DefaulFlowLabels.ql => DefaultFlowLabels.ql} | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) rename javascript/ql/test/library-tests/FlowLabels/{DefaulFlowLabels.expected => DefaultFlowLabels.expected} (100%) rename javascript/ql/test/library-tests/FlowLabels/{DefaulFlowLabels.ql => DefaultFlowLabels.ql} (60%) diff --git a/javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.expected b/javascript/ql/test/library-tests/FlowLabels/DefaultFlowLabels.expected similarity index 100% rename from javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.expected rename to javascript/ql/test/library-tests/FlowLabels/DefaultFlowLabels.expected diff --git a/javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.ql b/javascript/ql/test/library-tests/FlowLabels/DefaultFlowLabels.ql similarity index 60% rename from javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.ql rename to javascript/ql/test/library-tests/FlowLabels/DefaultFlowLabels.ql index f6fefc80ffd..026933fc123 100644 --- a/javascript/ql/test/library-tests/FlowLabels/DefaulFlowLabels.ql +++ b/javascript/ql/test/library-tests/FlowLabels/DefaultFlowLabels.ql @@ -1,6 +1,5 @@ import javascript -// Check which flow labels are materialize by importing `javascript.qll`. +// Check which flow labels are materialized by importing `javascript.qll`. // If this increases, it may indicate a performance issue. - select any(DataFlow::FlowLabel label) From 100f13f202feda7fbff9753939a383454ce5bd94 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Sat, 17 Oct 2020 13:17:08 -0400 Subject: [PATCH 161/166] C++: Annotate IR with partial flow info I've added one more property to the annotations provided by `PrintIRLocalFlow.qll`: The `pflow` property will now be emitted for any operand or instruction for which `configuration.hasPartialFlow` determines that there is partial flow to that node. This requires that partial flow be enabled via overriding `Configuration::explorationLimit()` in order to display. Otherwise, you'll still just get the local flow info as before. --- .../ir/dataflow/internal/PrintIRLocalFlow.qll | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll index edbb11db2f3..337dc71a3ca 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll @@ -130,6 +130,23 @@ private string getNodeProperty(DataFlow::Node node, string key) { | kind, ", " ) + or + // Is there partial flow from a source to this node? + // This property will only be emitted if partial flow is enabled by overriding + // `DataFlow::Configration::explorationLimit()`. + key = "pflow" and + result = + strictconcat(DataFlow::PartialPathNode sourceNode, DataFlow::PartialPathNode destNode, int dist, + int order1, int order2 | + any(DataFlow::Configuration cfg).hasPartialFlow(sourceNode, destNode, dist) and + destNode.getNode() = node and + // Only print flow from a source in the same function. + sourceNode.getNode().getEnclosingCallable() = node.getEnclosingCallable() + | + nodeId(sourceNode.getNode(), order1, order2) + "+" + dist.toString(), ", " + order by + order1, order2, dist desc + ) } /** From e1d90e90ad710e38c2a03c02137b71b56114a59f Mon Sep 17 00:00:00 2001 From: Max Schaefer Date: Fri, 16 Oct 2020 19:55:54 +0100 Subject: [PATCH 162/166] JavaScript: Add modelling for `Module.prototype._compile`. --- .../security/dataflow/CodeInjectionCustomizations.qll | 11 +++++++++++ .../CWE-094/CodeInjection/CodeInjection.expected | 5 +++++ .../HeuristicSourceCodeInjection.expected | 4 ++++ .../Security/CWE-094/CodeInjection/module.js | 10 ++++++++++ 4 files changed, 30 insertions(+) create mode 100644 javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/module.js diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/CodeInjectionCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/CodeInjectionCustomizations.qll index 265650703e5..3ce142a5316 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/CodeInjectionCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/CodeInjectionCustomizations.qll @@ -124,4 +124,15 @@ module CodeInjection { class NoSQLCodeInjectionSink extends Sink { NoSQLCodeInjectionSink() { any(NoSQL::Query q).getACodeOperator() = this } } + + /** + * The first argument to `Module.prototype._compile` from the Node.js built-in module `module`, + * considered as a code-injection sink. + */ + class ModuleCompileSink extends Sink { + ModuleCompileSink() { + this = + API::moduleImport("module").getInstance().getMember("_compile").getACall().getArgument(0) + } + } } diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected index dec023d72f3..c215da7d3c7 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected @@ -108,6 +108,9 @@ nodes | express.js:21:19:21:48 | req.par ... ntext") | | express.js:21:19:21:48 | req.par ... ntext") | | express.js:21:19:21:48 | req.par ... ntext") | +| module.js:9:16:9:29 | req.query.code | +| module.js:9:16:9:29 | req.query.code | +| module.js:9:16:9:29 | req.query.code | | react-native.js:7:7:7:33 | tainted | | react-native.js:7:17:7:33 | req.param("code") | | react-native.js:7:17:7:33 | req.param("code") | @@ -246,6 +249,7 @@ edges | express.js:17:30:17:53 | req.par ... cript") | express.js:17:30:17:53 | req.par ... cript") | | express.js:19:37:19:70 | req.par ... odule") | express.js:19:37:19:70 | req.par ... odule") | | express.js:21:19:21:48 | req.par ... ntext") | express.js:21:19:21:48 | req.par ... ntext") | +| module.js:9:16:9:29 | req.query.code | module.js:9:16:9:29 | req.query.code | | react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted | | react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted | | react-native.js:7:7:7:33 | tainted | react-native.js:10:23:10:29 | tainted | @@ -308,6 +312,7 @@ edges | express.js:17:30:17:53 | req.par ... cript") | express.js:17:30:17:53 | req.par ... cript") | express.js:17:30:17:53 | req.par ... cript") | $@ flows to here and is interpreted as code. | express.js:17:30:17:53 | req.par ... cript") | User-provided value | | express.js:19:37:19:70 | req.par ... odule") | express.js:19:37:19:70 | req.par ... odule") | express.js:19:37:19:70 | req.par ... odule") | $@ flows to here and is interpreted as code. | express.js:19:37:19:70 | req.par ... odule") | User-provided value | | express.js:21:19:21:48 | req.par ... ntext") | express.js:21:19:21:48 | req.par ... ntext") | express.js:21:19:21:48 | req.par ... ntext") | $@ flows to here and is interpreted as code. | express.js:21:19:21:48 | req.par ... ntext") | User-provided value | +| module.js:9:16:9:29 | req.query.code | module.js:9:16:9:29 | req.query.code | module.js:9:16:9:29 | req.query.code | $@ flows to here and is interpreted as code. | module.js:9:16:9:29 | req.query.code | User-provided value | | react-native.js:8:32:8:38 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:8:32:8:38 | tainted | $@ flows to here and is interpreted as code. | react-native.js:7:17:7:33 | req.param("code") | User-provided value | | react-native.js:10:23:10:29 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:10:23:10:29 | tainted | $@ flows to here and is interpreted as code. | react-native.js:7:17:7:33 | req.param("code") | User-provided value | | tst.js:2:6:2:83 | documen ... t=")+8) | tst.js:2:6:2:22 | document.location | tst.js:2:6:2:83 | documen ... t=")+8) | $@ flows to here and is interpreted as code. | tst.js:2:6:2:22 | document.location | User-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected index fa34f829b92..a0df19e9dfc 100644 --- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected @@ -112,6 +112,9 @@ nodes | express.js:21:19:21:48 | req.par ... ntext") | | express.js:21:19:21:48 | req.par ... ntext") | | express.js:21:19:21:48 | req.par ... ntext") | +| module.js:9:16:9:29 | req.query.code | +| module.js:9:16:9:29 | req.query.code | +| module.js:9:16:9:29 | req.query.code | | react-native.js:7:7:7:33 | tainted | | react-native.js:7:17:7:33 | req.param("code") | | react-native.js:7:17:7:33 | req.param("code") | @@ -254,6 +257,7 @@ edges | express.js:17:30:17:53 | req.par ... cript") | express.js:17:30:17:53 | req.par ... cript") | | express.js:19:37:19:70 | req.par ... odule") | express.js:19:37:19:70 | req.par ... odule") | | express.js:21:19:21:48 | req.par ... ntext") | express.js:21:19:21:48 | req.par ... ntext") | +| module.js:9:16:9:29 | req.query.code | module.js:9:16:9:29 | req.query.code | | react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted | | react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted | | react-native.js:7:7:7:33 | tainted | react-native.js:10:23:10:29 | tainted | diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/module.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/module.js new file mode 100644 index 00000000000..0985ada81b6 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/module.js @@ -0,0 +1,10 @@ +var express = require('express'), + Module = require('module'); + +var app = express(); + +app.get('/some/path', function (req, res) { + let filename = req.query.filename; + var m = new Module(filename, module.parent); + m._compile(req.query.code, filename); // NOT OK +}); From 5b1ed97d6840566bd4efdbf39b328c064da5bacd Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 19 Oct 2020 11:01:06 +0200 Subject: [PATCH 163/166] Update javascript/ql/src/semmle/javascript/TypeScript.qll Co-authored-by: Asger F --- javascript/ql/src/semmle/javascript/TypeScript.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/src/semmle/javascript/TypeScript.qll b/javascript/ql/src/semmle/javascript/TypeScript.qll index 9792baeaf11..d1ea9ea1d56 100644 --- a/javascript/ql/src/semmle/javascript/TypeScript.qll +++ b/javascript/ql/src/semmle/javascript/TypeScript.qll @@ -322,7 +322,7 @@ class InterfaceDeclaration extends Stmt, InterfaceDefinition, @interface_declara */ override TypeExpr getASuperInterface() { result = InterfaceDefinition.super.getASuperInterface() } - override string getAPrimaryQlClass() { result = "InterfaceDefinition" } + override string getAPrimaryQlClass() { result = "InterfaceDeclaration" } } /** An inline TypeScript interface type, such as `{x: number; y: number}`. */ From 8f6165cd5fde51af7cb5c5e4d43cc70efde5c0ef Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 19 Oct 2020 11:10:14 +0200 Subject: [PATCH 164/166] print synthetic constructors in PrintAst.ql --- .../ql/src/semmle/javascript/PrintAst.qll | 12 +------ .../TypeAnnotations/printAst.expected | 36 +++++++++++++++++++ .../TypeScript/Types/printAst.expected | 24 +++++++++++++ 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/PrintAst.qll b/javascript/ql/src/semmle/javascript/PrintAst.qll index 7f451cd7341..0499e3bd547 100644 --- a/javascript/ql/src/semmle/javascript/PrintAst.qll +++ b/javascript/ql/src/semmle/javascript/PrintAst.qll @@ -30,22 +30,12 @@ private predicate shouldPrint(Locatable e, Location l) { exists(PrintAstConfiguration config | config.shouldPrint(e, l)) } -/** Holds if the given element does not need to be rendered in the AST, due to being compiler-generated or being a `TopLevel`. */ +/** Holds if the given element does not need to be rendered in the AST, due to being the `TopLevel` for a file. */ private predicate isNotNeeded(Locatable el) { - exists(ClassDefinition c, ConstructorDeclaration constructor | - constructor = c.getConstructor() and - constructor.isSynthetic() and - el = constructor - ) - or el instanceof TopLevel and el.getLocation().getStartLine() = 0 and el.getLocation().getStartColumn() = 0 or - exists(ASTNode parent | isNotNeeded(parent) and not parent instanceof TopLevel | - el = parent.getAChild() - ) - or // relaxing aggresive type inference. none() } diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/printAst.expected b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/printAst.expected index 4ba76b51f90..3eaebefb399 100644 --- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/printAst.expected +++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/printAst.expected @@ -589,11 +589,19 @@ nodes | tst.ts:98:1:98:14 | [ClassDefinition,TypeDefinition] class C1 {} | semmle.label | [ClassDefinition,TypeDefinition] class C1 {} | | tst.ts:98:1:98:14 | [ClassDefinition,TypeDefinition] class C1 {} | semmle.order | 62 | | tst.ts:98:7:98:8 | [VarDecl] C1 | semmle.label | [VarDecl] C1 | +| tst.ts:98:9:98:8 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | +| tst.ts:98:9:98:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | +| tst.ts:98:9:98:8 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | +| tst.ts:98:9:98:8 | [Label] constructor | semmle.label | [Label] constructor | | tst.ts:98:10:98:10 | [Identifier] S | semmle.label | [Identifier] S | | tst.ts:98:10:98:10 | [TypeParameter] S | semmle.label | [TypeParameter] S | | tst.ts:99:1:99:16 | [ClassDefinition,TypeDefinition] class C2 {} | semmle.label | [ClassDefinition,TypeDefinition] class C2 {} | | tst.ts:99:1:99:16 | [ClassDefinition,TypeDefinition] class C2 {} | semmle.order | 63 | | tst.ts:99:7:99:8 | [VarDecl] C2 | semmle.label | [VarDecl] C2 | +| tst.ts:99:9:99:8 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | +| tst.ts:99:9:99:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | +| tst.ts:99:9:99:8 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | +| tst.ts:99:9:99:8 | [Label] constructor | semmle.label | [Label] constructor | | tst.ts:99:10:99:10 | [Identifier] S | semmle.label | [Identifier] S | | tst.ts:99:10:99:10 | [TypeParameter] S | semmle.label | [TypeParameter] S | | tst.ts:99:12:99:12 | [Identifier] T | semmle.label | [Identifier] T | @@ -601,6 +609,10 @@ nodes | tst.ts:100:1:100:29 | [ClassDefinition,TypeDefinition] class C ... ber> {} | semmle.label | [ClassDefinition,TypeDefinition] class C ... ber> {} | | tst.ts:100:1:100:29 | [ClassDefinition,TypeDefinition] class C ... ber> {} | semmle.order | 64 | | tst.ts:100:7:100:8 | [VarDecl] C3 | semmle.label | [VarDecl] C3 | +| tst.ts:100:9:100:8 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | +| tst.ts:100:9:100:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | +| tst.ts:100:9:100:8 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | +| tst.ts:100:9:100:8 | [Label] constructor | semmle.label | [Label] constructor | | tst.ts:100:10:100:10 | [Identifier] S | semmle.label | [Identifier] S | | tst.ts:100:10:100:25 | [TypeParameter] S extends number | semmle.label | [TypeParameter] S extends number | | tst.ts:100:20:100:25 | [KeywordTypeExpr] number | semmle.label | [KeywordTypeExpr] number | @@ -1954,12 +1966,28 @@ edges | tst.ts:98:1:98:14 | [ClassDefinition,TypeDefinition] class C1 {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | | tst.ts:98:1:98:14 | [ClassDefinition,TypeDefinition] class C1 {} | tst.ts:98:7:98:8 | [VarDecl] C1 | semmle.label | 1 | | tst.ts:98:1:98:14 | [ClassDefinition,TypeDefinition] class C1 {} | tst.ts:98:7:98:8 | [VarDecl] C1 | semmle.order | 1 | +| tst.ts:98:1:98:14 | [ClassDefinition,TypeDefinition] class C1 {} | tst.ts:98:9:98:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 2 | +| tst.ts:98:1:98:14 | [ClassDefinition,TypeDefinition] class C1 {} | tst.ts:98:9:98:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 2 | +| tst.ts:98:9:98:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:98:9:98:8 | [FunctionExpr] () {} | semmle.label | 2 | +| tst.ts:98:9:98:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:98:9:98:8 | [FunctionExpr] () {} | semmle.order | 2 | +| tst.ts:98:9:98:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:98:9:98:8 | [Label] constructor | semmle.label | 1 | +| tst.ts:98:9:98:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:98:9:98:8 | [Label] constructor | semmle.order | 1 | +| tst.ts:98:9:98:8 | [FunctionExpr] () {} | tst.ts:98:9:98:8 | [BlockStmt] {} | semmle.label | 5 | +| tst.ts:98:9:98:8 | [FunctionExpr] () {} | tst.ts:98:9:98:8 | [BlockStmt] {} | semmle.order | 5 | | tst.ts:98:10:98:10 | [TypeParameter] S | tst.ts:98:10:98:10 | [Identifier] S | semmle.label | 1 | | tst.ts:98:10:98:10 | [TypeParameter] S | tst.ts:98:10:98:10 | [Identifier] S | semmle.order | 1 | | tst.ts:99:1:99:16 | [ClassDefinition,TypeDefinition] class C2 {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | | tst.ts:99:1:99:16 | [ClassDefinition,TypeDefinition] class C2 {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | | tst.ts:99:1:99:16 | [ClassDefinition,TypeDefinition] class C2 {} | tst.ts:99:7:99:8 | [VarDecl] C2 | semmle.label | 1 | | tst.ts:99:1:99:16 | [ClassDefinition,TypeDefinition] class C2 {} | tst.ts:99:7:99:8 | [VarDecl] C2 | semmle.order | 1 | +| tst.ts:99:1:99:16 | [ClassDefinition,TypeDefinition] class C2 {} | tst.ts:99:9:99:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 2 | +| tst.ts:99:1:99:16 | [ClassDefinition,TypeDefinition] class C2 {} | tst.ts:99:9:99:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 2 | +| tst.ts:99:9:99:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:99:9:99:8 | [FunctionExpr] () {} | semmle.label | 2 | +| tst.ts:99:9:99:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:99:9:99:8 | [FunctionExpr] () {} | semmle.order | 2 | +| tst.ts:99:9:99:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:99:9:99:8 | [Label] constructor | semmle.label | 1 | +| tst.ts:99:9:99:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:99:9:99:8 | [Label] constructor | semmle.order | 1 | +| tst.ts:99:9:99:8 | [FunctionExpr] () {} | tst.ts:99:9:99:8 | [BlockStmt] {} | semmle.label | 5 | +| tst.ts:99:9:99:8 | [FunctionExpr] () {} | tst.ts:99:9:99:8 | [BlockStmt] {} | semmle.order | 5 | | tst.ts:99:10:99:10 | [TypeParameter] S | tst.ts:99:10:99:10 | [Identifier] S | semmle.label | 1 | | tst.ts:99:10:99:10 | [TypeParameter] S | tst.ts:99:10:99:10 | [Identifier] S | semmle.order | 1 | | tst.ts:99:12:99:12 | [TypeParameter] T | tst.ts:99:12:99:12 | [Identifier] T | semmle.label | 1 | @@ -1968,6 +1996,14 @@ edges | tst.ts:100:1:100:29 | [ClassDefinition,TypeDefinition] class C ... ber> {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | | tst.ts:100:1:100:29 | [ClassDefinition,TypeDefinition] class C ... ber> {} | tst.ts:100:7:100:8 | [VarDecl] C3 | semmle.label | 1 | | tst.ts:100:1:100:29 | [ClassDefinition,TypeDefinition] class C ... ber> {} | tst.ts:100:7:100:8 | [VarDecl] C3 | semmle.order | 1 | +| tst.ts:100:1:100:29 | [ClassDefinition,TypeDefinition] class C ... ber> {} | tst.ts:100:9:100:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 2 | +| tst.ts:100:1:100:29 | [ClassDefinition,TypeDefinition] class C ... ber> {} | tst.ts:100:9:100:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 2 | +| tst.ts:100:9:100:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:100:9:100:8 | [FunctionExpr] () {} | semmle.label | 2 | +| tst.ts:100:9:100:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:100:9:100:8 | [FunctionExpr] () {} | semmle.order | 2 | +| tst.ts:100:9:100:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:100:9:100:8 | [Label] constructor | semmle.label | 1 | +| tst.ts:100:9:100:8 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | tst.ts:100:9:100:8 | [Label] constructor | semmle.order | 1 | +| tst.ts:100:9:100:8 | [FunctionExpr] () {} | tst.ts:100:9:100:8 | [BlockStmt] {} | semmle.label | 5 | +| tst.ts:100:9:100:8 | [FunctionExpr] () {} | tst.ts:100:9:100:8 | [BlockStmt] {} | semmle.order | 5 | | tst.ts:100:10:100:25 | [TypeParameter] S extends number | tst.ts:100:10:100:10 | [Identifier] S | semmle.label | 1 | | tst.ts:100:10:100:25 | [TypeParameter] S extends number | tst.ts:100:10:100:10 | [Identifier] S | semmle.order | 1 | | tst.ts:100:10:100:25 | [TypeParameter] S extends number | tst.ts:100:20:100:25 | [KeywordTypeExpr] number | semmle.label | 2 | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected index dd617512a24..8b70a311f41 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected @@ -429,6 +429,10 @@ nodes | type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | semmle.order | 54 | | type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | semmle.label | [ClassDefinition,TypeDefinition] class C {} | | type_definition_objects.ts:3:14:3:14 | [VarDecl] C | semmle.label | [VarDecl] C | +| type_definition_objects.ts:3:16:3:15 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | +| type_definition_objects.ts:3:16:3:15 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | +| type_definition_objects.ts:3:16:3:15 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | +| type_definition_objects.ts:3:16:3:15 | [Label] constructor | semmle.label | [Label] constructor | | type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.label | [DeclStmt] let classObj = ... | | type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | semmle.order | 55 | | type_definition_objects.ts:4:5:4:12 | [VarDecl] classObj | semmle.label | [VarDecl] classObj | @@ -476,6 +480,10 @@ nodes | type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.label | [ClassDefinition,TypeDefinition] class C ... x: T } | | type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | semmle.order | 63 | | type_definitions.ts:8:7:8:7 | [VarDecl] C | semmle.label | [VarDecl] C | +| type_definitions.ts:8:8:8:7 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | +| type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | [ClassInitializedMember,ConstructorDefinition] constructor() {} | +| type_definitions.ts:8:8:8:7 | [FunctionExpr] () {} | semmle.label | [FunctionExpr] () {} | +| type_definitions.ts:8:8:8:7 | [Label] constructor | semmle.label | [Label] constructor | | type_definitions.ts:8:9:8:9 | [Identifier] T | semmle.label | [Identifier] T | | type_definitions.ts:8:9:8:9 | [TypeParameter] T | semmle.label | [TypeParameter] T | | type_definitions.ts:9:3:9:3 | [Label] x | semmle.label | [Label] x | @@ -1171,6 +1179,14 @@ edges | type_definition_objects.ts:3:1:3:17 | [ExportDeclaration] export class C {} | type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | semmle.order | 1 | | type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | type_definition_objects.ts:3:14:3:14 | [VarDecl] C | semmle.label | 1 | | type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | type_definition_objects.ts:3:14:3:14 | [VarDecl] C | semmle.order | 1 | +| type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | type_definition_objects.ts:3:16:3:15 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 2 | +| type_definition_objects.ts:3:8:3:17 | [ClassDefinition,TypeDefinition] class C {} | type_definition_objects.ts:3:16:3:15 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 2 | +| type_definition_objects.ts:3:16:3:15 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | type_definition_objects.ts:3:16:3:15 | [FunctionExpr] () {} | semmle.label | 2 | +| type_definition_objects.ts:3:16:3:15 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | type_definition_objects.ts:3:16:3:15 | [FunctionExpr] () {} | semmle.order | 2 | +| type_definition_objects.ts:3:16:3:15 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | type_definition_objects.ts:3:16:3:15 | [Label] constructor | semmle.label | 1 | +| type_definition_objects.ts:3:16:3:15 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | type_definition_objects.ts:3:16:3:15 | [Label] constructor | semmle.order | 1 | +| type_definition_objects.ts:3:16:3:15 | [FunctionExpr] () {} | type_definition_objects.ts:3:16:3:15 | [BlockStmt] {} | semmle.label | 5 | +| type_definition_objects.ts:3:16:3:15 | [FunctionExpr] () {} | type_definition_objects.ts:3:16:3:15 | [BlockStmt] {} | semmle.order | 5 | | type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | type_definition_objects.ts:4:5:4:16 | [VariableDeclarator] classObj = C | semmle.label | 1 | | type_definition_objects.ts:4:1:4:17 | [DeclStmt] let classObj = ... | type_definition_objects.ts:4:5:4:16 | [VariableDeclarator] classObj = C | semmle.order | 1 | | type_definition_objects.ts:4:5:4:16 | [VariableDeclarator] classObj = C | type_definition_objects.ts:4:5:4:12 | [VarDecl] classObj | semmle.label | 1 | @@ -1231,8 +1247,16 @@ edges | type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | | type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | type_definitions.ts:8:7:8:7 | [VarDecl] C | semmle.label | 1 | | type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | type_definitions.ts:8:7:8:7 | [VarDecl] C | semmle.order | 1 | +| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.label | 2 | +| type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | semmle.order | 2 | | type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | type_definitions.ts:9:3:9:6 | [FieldDeclaration] x: T | semmle.label | 4 | | type_definitions.ts:8:1:10:1 | [ClassDefinition,TypeDefinition] class C ... x: T } | type_definitions.ts:9:3:9:6 | [FieldDeclaration] x: T | semmle.order | 4 | +| type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | type_definitions.ts:8:8:8:7 | [FunctionExpr] () {} | semmle.label | 2 | +| type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | type_definitions.ts:8:8:8:7 | [FunctionExpr] () {} | semmle.order | 2 | +| type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | type_definitions.ts:8:8:8:7 | [Label] constructor | semmle.label | 1 | +| type_definitions.ts:8:8:8:7 | [ClassInitializedMember,ConstructorDefinition] constructor() {} | type_definitions.ts:8:8:8:7 | [Label] constructor | semmle.order | 1 | +| type_definitions.ts:8:8:8:7 | [FunctionExpr] () {} | type_definitions.ts:8:8:8:7 | [BlockStmt] {} | semmle.label | 5 | +| type_definitions.ts:8:8:8:7 | [FunctionExpr] () {} | type_definitions.ts:8:8:8:7 | [BlockStmt] {} | semmle.order | 5 | | type_definitions.ts:8:9:8:9 | [TypeParameter] T | type_definitions.ts:8:9:8:9 | [Identifier] T | semmle.label | 1 | | type_definitions.ts:8:9:8:9 | [TypeParameter] T | type_definitions.ts:8:9:8:9 | [Identifier] T | semmle.order | 1 | | type_definitions.ts:9:3:9:6 | [FieldDeclaration] x: T | type_definitions.ts:9:3:9:3 | [Label] x | semmle.label | 1 | From ca0870da5370075f92dbaac653185ffdbdb77103 Mon Sep 17 00:00:00 2001 From: Erik Krogh Kristensen Date: Mon, 19 Oct 2020 12:36:48 +0200 Subject: [PATCH 165/166] update expected output from InterfaceDefinition -> InterfaceDeclaration change --- .../TypeAnnotations/printAst.expected | 156 +++++++++--------- .../TypeScript/Types/printAst.expected | 16 +- 2 files changed, 86 insertions(+), 86 deletions(-) diff --git a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/printAst.expected b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/printAst.expected index 3eaebefb399..5a429553e69 100644 --- a/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/printAst.expected +++ b/javascript/ql/test/library-tests/TypeScript/TypeAnnotations/printAst.expected @@ -114,8 +114,8 @@ nodes | tst.ts:12:5:12:16 | [VarDecl] literalFalse | semmle.label | [VarDecl] literalFalse | | tst.ts:12:5:12:23 | [VariableDeclarator] literalFalse: false | semmle.label | [VariableDeclarator] literalFalse: false | | tst.ts:12:19:12:23 | [LiteralTypeExpr] false | semmle.label | [LiteralTypeExpr] false | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | semmle.label | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | semmle.order | 13 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | semmle.order | 13 | | tst.ts:14:11:14:19 | [Identifier] Interface | semmle.label | [Identifier] Interface | | tst.ts:15:3:15:13 | [Label] numberField | semmle.label | [Label] numberField | | tst.ts:15:3:15:22 | [FieldDeclaration] numberField: number; | semmle.label | [FieldDeclaration] numberField: number; | @@ -295,13 +295,13 @@ nodes | tst.ts:49:5:49:30 | [VariableDeclarator] keyofTy ... terface | semmle.label | [VariableDeclarator] keyofTy ... terface | | tst.ts:49:16:49:30 | [KeyofTypeExpr] keyof Interface | semmle.label | [KeyofTypeExpr] keyof Interface | | tst.ts:49:22:49:30 | [LocalTypeAccess] Interface | semmle.label | [LocalTypeAccess] Interface | -| tst.ts:51:1:51:23 | [InterfaceDefinition,TypeDefinition] interfa ... c {} | semmle.label | [InterfaceDefinition,TypeDefinition] interfa ... c {} | -| tst.ts:51:1:51:23 | [InterfaceDefinition,TypeDefinition] interfa ... c {} | semmle.order | 33 | +| tst.ts:51:1:51:23 | [InterfaceDeclaration,TypeDefinition] interfa ... c {} | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... c {} | +| tst.ts:51:1:51:23 | [InterfaceDeclaration,TypeDefinition] interfa ... c {} | semmle.order | 33 | | tst.ts:51:11:51:17 | [Identifier] Generic | semmle.label | [Identifier] Generic | | tst.ts:51:19:51:19 | [Identifier] T | semmle.label | [Identifier] T | | tst.ts:51:19:51:19 | [TypeParameter] T | semmle.label | [TypeParameter] T | -| tst.ts:52:1:52:34 | [InterfaceDefinition,TypeDefinition] interfa ... , U> {} | semmle.label | [InterfaceDefinition,TypeDefinition] interfa ... , U> {} | -| tst.ts:52:1:52:34 | [InterfaceDefinition,TypeDefinition] interfa ... , U> {} | semmle.order | 34 | +| tst.ts:52:1:52:34 | [InterfaceDeclaration,TypeDefinition] interfa ... , U> {} | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... , U> {} | +| tst.ts:52:1:52:34 | [InterfaceDeclaration,TypeDefinition] interfa ... , U> {} | semmle.order | 34 | | tst.ts:52:11:52:22 | [Identifier] ManyTypeArgs | semmle.label | [Identifier] ManyTypeArgs | | tst.ts:52:24:52:24 | [Identifier] S | semmle.label | [Identifier] S | | tst.ts:52:24:52:24 | [TypeParameter] S | semmle.label | [TypeParameter] S | @@ -313,10 +313,10 @@ nodes | tst.ts:53:1:61:1 | [NamespaceDeclaration] namespa ... = 5; } | semmle.order | 35 | | tst.ts:53:11:53:11 | [VarDecl] N | semmle.label | [VarDecl] N | | tst.ts:54:3:54:23 | [ExportDeclaration] export ... ce I {} | semmle.label | [ExportDeclaration] export ... ce I {} | -| tst.ts:54:10:54:23 | [InterfaceDefinition,TypeDefinition] interface I {} | semmle.label | [InterfaceDefinition,TypeDefinition] interface I {} | +| tst.ts:54:10:54:23 | [InterfaceDeclaration,TypeDefinition] interface I {} | semmle.label | [InterfaceDeclaration,TypeDefinition] interface I {} | | tst.ts:54:20:54:20 | [Identifier] I | semmle.label | [Identifier] I | | tst.ts:55:3:55:38 | [ExportDeclaration] export ... 1 {} | semmle.label | [ExportDeclaration] export ... 1 {} | -| tst.ts:55:10:55:38 | [InterfaceDefinition,TypeDefinition] interfa ... 1 {} | semmle.label | [InterfaceDefinition,TypeDefinition] interfa ... 1 {} | +| tst.ts:55:10:55:38 | [InterfaceDeclaration,TypeDefinition] interfa ... 1 {} | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... 1 {} | | tst.ts:55:20:55:32 | [Identifier] InnerGeneric1 | semmle.label | [Identifier] InnerGeneric1 | | tst.ts:55:34:55:34 | [Identifier] T | semmle.label | [Identifier] T | | tst.ts:55:34:55:34 | [TypeParameter] T | semmle.label | [TypeParameter] T | @@ -324,10 +324,10 @@ nodes | tst.ts:56:10:59:3 | [NamespaceDeclaration] namespa ... {} } | semmle.label | [NamespaceDeclaration] namespa ... {} } | | tst.ts:56:20:56:20 | [VarDecl] M | semmle.label | [VarDecl] M | | tst.ts:57:5:57:25 | [ExportDeclaration] export ... ce J {} | semmle.label | [ExportDeclaration] export ... ce J {} | -| tst.ts:57:12:57:25 | [InterfaceDefinition,TypeDefinition] interface J {} | semmle.label | [InterfaceDefinition,TypeDefinition] interface J {} | +| tst.ts:57:12:57:25 | [InterfaceDeclaration,TypeDefinition] interface J {} | semmle.label | [InterfaceDeclaration,TypeDefinition] interface J {} | | tst.ts:57:22:57:22 | [Identifier] J | semmle.label | [Identifier] J | | tst.ts:58:5:58:40 | [ExportDeclaration] export ... 2 {} | semmle.label | [ExportDeclaration] export ... 2 {} | -| tst.ts:58:12:58:40 | [InterfaceDefinition,TypeDefinition] interfa ... 2 {} | semmle.label | [InterfaceDefinition,TypeDefinition] interfa ... 2 {} | +| tst.ts:58:12:58:40 | [InterfaceDeclaration,TypeDefinition] interfa ... 2 {} | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... 2 {} | | tst.ts:58:22:58:34 | [Identifier] InnerGeneric2 | semmle.label | [Identifier] InnerGeneric2 | | tst.ts:58:36:58:36 | [Identifier] T | semmle.label | [Identifier] T | | tst.ts:58:36:58:36 | [TypeParameter] T | semmle.label | [TypeParameter] T | @@ -419,8 +419,8 @@ nodes | tst.ts:72:5:72:35 | [VariableDeclarator] typeofV ... fiedVar | semmle.label | [VariableDeclarator] typeofV ... fiedVar | | tst.ts:72:17:72:35 | [TypeofTypeExpr] typeof qualifiedVar | semmle.label | [TypeofTypeExpr] typeof qualifiedVar | | tst.ts:72:24:72:35 | [LocalVarTypeAccess] qualifiedVar | semmle.label | [LocalVarTypeAccess] qualifiedVar | -| tst.ts:74:1:77:1 | [InterfaceDefinition,TypeDefinition] interfa ... Leaf; } | semmle.label | [InterfaceDefinition,TypeDefinition] interfa ... Leaf; } | -| tst.ts:74:1:77:1 | [InterfaceDefinition,TypeDefinition] interfa ... Leaf; } | semmle.order | 46 | +| tst.ts:74:1:77:1 | [InterfaceDeclaration,TypeDefinition] interfa ... Leaf; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... Leaf; } | +| tst.ts:74:1:77:1 | [InterfaceDeclaration,TypeDefinition] interfa ... Leaf; } | semmle.order | 46 | | tst.ts:74:11:74:14 | [Identifier] Node | semmle.label | [Identifier] Node | | tst.ts:75:3:75:12 | [Label] isThisLeaf | semmle.label | [Label] isThisLeaf | | tst.ts:75:3:75:29 | [FunctionExpr] isThisL ... s Leaf; | semmle.label | [FunctionExpr] isThisL ... s Leaf; | @@ -435,8 +435,8 @@ nodes | tst.ts:76:21:76:24 | [LocalVarTypeAccess] that | semmle.label | [LocalVarTypeAccess] that | | tst.ts:76:21:76:32 | [IsTypeExpr] that is Leaf | semmle.label | [IsTypeExpr] that is Leaf | | tst.ts:76:29:76:32 | [LocalTypeAccess] Leaf | semmle.label | [LocalTypeAccess] Leaf | -| tst.ts:78:1:78:17 | [InterfaceDefinition,TypeDefinition] interface Leaf {} | semmle.label | [InterfaceDefinition,TypeDefinition] interface Leaf {} | -| tst.ts:78:1:78:17 | [InterfaceDefinition,TypeDefinition] interface Leaf {} | semmle.order | 47 | +| tst.ts:78:1:78:17 | [InterfaceDeclaration,TypeDefinition] interface Leaf {} | semmle.label | [InterfaceDeclaration,TypeDefinition] interface Leaf {} | +| tst.ts:78:1:78:17 | [InterfaceDeclaration,TypeDefinition] interface Leaf {} | semmle.order | 47 | | tst.ts:78:11:78:14 | [Identifier] Leaf | semmle.label | [Identifier] Leaf | | tst.ts:80:1:80:73 | [FunctionDeclStmt] functio ... alse; } | semmle.label | [FunctionDeclStmt] functio ... alse; } | | tst.ts:80:1:80:73 | [FunctionDeclStmt] functio ... alse; } | semmle.order | 48 | @@ -706,8 +706,8 @@ nodes | tst.ts:116:46:116:46 | [SimpleParameter] y | semmle.label | [SimpleParameter] y | | tst.ts:116:49:116:54 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string | | tst.ts:116:57:116:58 | [BlockStmt] {} | semmle.label | [BlockStmt] {} | -| tst.ts:118:1:120:1 | [InterfaceDefinition,TypeDefinition] interfa ... ram); } | semmle.label | [InterfaceDefinition,TypeDefinition] interfa ... ram); } | -| tst.ts:118:1:120:1 | [InterfaceDefinition,TypeDefinition] interfa ... ram); } | semmle.order | 76 | +| tst.ts:118:1:120:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ram); } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... ram); } | +| tst.ts:118:1:120:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ram); } | semmle.order | 76 | | tst.ts:118:11:118:32 | [Identifier] InterfaceWithThisParam | semmle.label | [Identifier] InterfaceWithThisParam | | tst.ts:119:3:119:14 | [Label] hasThisParam | semmle.label | [Label] hasThisParam | | tst.ts:119:3:119:45 | [FunctionExpr] hasThis ... Param); | semmle.label | [FunctionExpr] hasThis ... Param); | @@ -1224,28 +1224,28 @@ edges | tst.ts:12:5:12:23 | [VariableDeclarator] literalFalse: false | tst.ts:12:5:12:16 | [VarDecl] literalFalse | semmle.order | 1 | | tst.ts:12:5:12:23 | [VariableDeclarator] literalFalse: false | tst.ts:12:19:12:23 | [LiteralTypeExpr] false | semmle.label | 2 | | tst.ts:12:5:12:23 | [VariableDeclarator] literalFalse: false | tst.ts:12:19:12:23 | [LiteralTypeExpr] false | semmle.order | 2 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:14:11:14:19 | [Identifier] Interface | semmle.label | 1 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:14:11:14:19 | [Identifier] Interface | semmle.order | 1 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:15:3:15:22 | [FieldDeclaration] numberField: number; | semmle.label | 2 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:15:3:15:22 | [FieldDeclaration] numberField: number; | semmle.order | 2 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:16:3:16:22 | [FieldDeclaration] stringField: string; | semmle.label | 3 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:16:3:16:22 | [FieldDeclaration] stringField: string; | semmle.order | 3 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:17:3:17:28 | [FieldDeclaration] interfa ... erface; | semmle.label | 4 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:17:3:17:28 | [FieldDeclaration] interfa ... erface; | semmle.order | 4 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:18:3:18:18 | [FieldDeclaration] thisField: this; | semmle.label | 5 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:18:3:18:18 | [FieldDeclaration] thisField: this; | semmle.order | 5 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:20:3:20:31 | [MethodSignature] returnN ... number; | semmle.label | 6 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:20:3:20:31 | [MethodSignature] returnN ... number; | semmle.order | 6 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:21:3:21:27 | [MethodSignature] returnV ... : void; | semmle.label | 7 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:21:3:21:27 | [MethodSignature] returnV ... : void; | semmle.order | 7 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:22:3:22:27 | [MethodSignature] returnN ... : null; | semmle.label | 8 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:22:3:22:27 | [MethodSignature] returnN ... : null; | semmle.order | 8 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:23:3:23:27 | [MethodSignature] returnT ... : this; | semmle.label | 9 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:23:3:23:27 | [MethodSignature] returnT ... : this; | semmle.order | 9 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:24:3:24:46 | [MethodSignature] takeNum ... umber); | semmle.label | 10 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:24:3:24:46 | [MethodSignature] takeNum ... umber); | semmle.order | 10 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:25:3:25:55 | [MethodSignature] takeInt ... rface); | semmle.label | 11 | -| tst.ts:14:1:26:1 | [InterfaceDefinition,TypeDefinition] interfa ... ace); } | tst.ts:25:3:25:55 | [MethodSignature] takeInt ... rface); | semmle.order | 11 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:14:11:14:19 | [Identifier] Interface | semmle.label | 1 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:14:11:14:19 | [Identifier] Interface | semmle.order | 1 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:15:3:15:22 | [FieldDeclaration] numberField: number; | semmle.label | 2 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:15:3:15:22 | [FieldDeclaration] numberField: number; | semmle.order | 2 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:16:3:16:22 | [FieldDeclaration] stringField: string; | semmle.label | 3 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:16:3:16:22 | [FieldDeclaration] stringField: string; | semmle.order | 3 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:17:3:17:28 | [FieldDeclaration] interfa ... erface; | semmle.label | 4 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:17:3:17:28 | [FieldDeclaration] interfa ... erface; | semmle.order | 4 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:18:3:18:18 | [FieldDeclaration] thisField: this; | semmle.label | 5 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:18:3:18:18 | [FieldDeclaration] thisField: this; | semmle.order | 5 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:20:3:20:31 | [MethodSignature] returnN ... number; | semmle.label | 6 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:20:3:20:31 | [MethodSignature] returnN ... number; | semmle.order | 6 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:21:3:21:27 | [MethodSignature] returnV ... : void; | semmle.label | 7 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:21:3:21:27 | [MethodSignature] returnV ... : void; | semmle.order | 7 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:22:3:22:27 | [MethodSignature] returnN ... : null; | semmle.label | 8 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:22:3:22:27 | [MethodSignature] returnN ... : null; | semmle.order | 8 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:23:3:23:27 | [MethodSignature] returnT ... : this; | semmle.label | 9 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:23:3:23:27 | [MethodSignature] returnT ... : this; | semmle.order | 9 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:24:3:24:46 | [MethodSignature] takeNum ... umber); | semmle.label | 10 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:24:3:24:46 | [MethodSignature] takeNum ... umber); | semmle.order | 10 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:25:3:25:55 | [MethodSignature] takeInt ... rface); | semmle.label | 11 | +| tst.ts:14:1:26:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ace); } | tst.ts:25:3:25:55 | [MethodSignature] takeInt ... rface); | semmle.order | 11 | | tst.ts:15:3:15:22 | [FieldDeclaration] numberField: number; | tst.ts:15:3:15:13 | [Label] numberField | semmle.label | 1 | | tst.ts:15:3:15:22 | [FieldDeclaration] numberField: number; | tst.ts:15:3:15:13 | [Label] numberField | semmle.order | 1 | | tst.ts:15:3:15:22 | [FieldDeclaration] numberField: number; | tst.ts:15:16:15:21 | [KeywordTypeExpr] number | semmle.label | 2 | @@ -1504,16 +1504,16 @@ edges | tst.ts:49:5:49:30 | [VariableDeclarator] keyofTy ... terface | tst.ts:49:16:49:30 | [KeyofTypeExpr] keyof Interface | semmle.order | 2 | | tst.ts:49:16:49:30 | [KeyofTypeExpr] keyof Interface | tst.ts:49:22:49:30 | [LocalTypeAccess] Interface | semmle.label | 1 | | tst.ts:49:16:49:30 | [KeyofTypeExpr] keyof Interface | tst.ts:49:22:49:30 | [LocalTypeAccess] Interface | semmle.order | 1 | -| tst.ts:51:1:51:23 | [InterfaceDefinition,TypeDefinition] interfa ... c {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | -| tst.ts:51:1:51:23 | [InterfaceDefinition,TypeDefinition] interfa ... c {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | -| tst.ts:51:1:51:23 | [InterfaceDefinition,TypeDefinition] interfa ... c {} | tst.ts:51:11:51:17 | [Identifier] Generic | semmle.label | 1 | -| tst.ts:51:1:51:23 | [InterfaceDefinition,TypeDefinition] interfa ... c {} | tst.ts:51:11:51:17 | [Identifier] Generic | semmle.order | 1 | +| tst.ts:51:1:51:23 | [InterfaceDeclaration,TypeDefinition] interfa ... c {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | +| tst.ts:51:1:51:23 | [InterfaceDeclaration,TypeDefinition] interfa ... c {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | +| tst.ts:51:1:51:23 | [InterfaceDeclaration,TypeDefinition] interfa ... c {} | tst.ts:51:11:51:17 | [Identifier] Generic | semmle.label | 1 | +| tst.ts:51:1:51:23 | [InterfaceDeclaration,TypeDefinition] interfa ... c {} | tst.ts:51:11:51:17 | [Identifier] Generic | semmle.order | 1 | | tst.ts:51:19:51:19 | [TypeParameter] T | tst.ts:51:19:51:19 | [Identifier] T | semmle.label | 1 | | tst.ts:51:19:51:19 | [TypeParameter] T | tst.ts:51:19:51:19 | [Identifier] T | semmle.order | 1 | -| tst.ts:52:1:52:34 | [InterfaceDefinition,TypeDefinition] interfa ... , U> {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | -| tst.ts:52:1:52:34 | [InterfaceDefinition,TypeDefinition] interfa ... , U> {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | -| tst.ts:52:1:52:34 | [InterfaceDefinition,TypeDefinition] interfa ... , U> {} | tst.ts:52:11:52:22 | [Identifier] ManyTypeArgs | semmle.label | 1 | -| tst.ts:52:1:52:34 | [InterfaceDefinition,TypeDefinition] interfa ... , U> {} | tst.ts:52:11:52:22 | [Identifier] ManyTypeArgs | semmle.order | 1 | +| tst.ts:52:1:52:34 | [InterfaceDeclaration,TypeDefinition] interfa ... , U> {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | +| tst.ts:52:1:52:34 | [InterfaceDeclaration,TypeDefinition] interfa ... , U> {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | +| tst.ts:52:1:52:34 | [InterfaceDeclaration,TypeDefinition] interfa ... , U> {} | tst.ts:52:11:52:22 | [Identifier] ManyTypeArgs | semmle.label | 1 | +| tst.ts:52:1:52:34 | [InterfaceDeclaration,TypeDefinition] interfa ... , U> {} | tst.ts:52:11:52:22 | [Identifier] ManyTypeArgs | semmle.order | 1 | | tst.ts:52:24:52:24 | [TypeParameter] S | tst.ts:52:24:52:24 | [Identifier] S | semmle.label | 1 | | tst.ts:52:24:52:24 | [TypeParameter] S | tst.ts:52:24:52:24 | [Identifier] S | semmle.order | 1 | | tst.ts:52:27:52:27 | [TypeParameter] T | tst.ts:52:27:52:27 | [Identifier] T | semmle.label | 1 | @@ -1530,16 +1530,16 @@ edges | tst.ts:53:1:61:1 | [NamespaceDeclaration] namespa ... = 5; } | tst.ts:56:3:59:3 | [ExportDeclaration] export ... {} } | semmle.order | 4 | | tst.ts:53:1:61:1 | [NamespaceDeclaration] namespa ... = 5; } | tst.ts:60:3:60:19 | [ExportDeclaration] export var x = 5; | semmle.label | 5 | | tst.ts:53:1:61:1 | [NamespaceDeclaration] namespa ... = 5; } | tst.ts:60:3:60:19 | [ExportDeclaration] export var x = 5; | semmle.order | 5 | -| tst.ts:54:3:54:23 | [ExportDeclaration] export ... ce I {} | tst.ts:54:10:54:23 | [InterfaceDefinition,TypeDefinition] interface I {} | semmle.label | 1 | -| tst.ts:54:3:54:23 | [ExportDeclaration] export ... ce I {} | tst.ts:54:10:54:23 | [InterfaceDefinition,TypeDefinition] interface I {} | semmle.order | 1 | -| tst.ts:54:10:54:23 | [InterfaceDefinition,TypeDefinition] interface I {} | tst.ts:54:20:54:20 | [Identifier] I | semmle.label | 1 | -| tst.ts:54:10:54:23 | [InterfaceDefinition,TypeDefinition] interface I {} | tst.ts:54:20:54:20 | [Identifier] I | semmle.order | 1 | -| tst.ts:55:3:55:38 | [ExportDeclaration] export ... 1 {} | tst.ts:55:10:55:38 | [InterfaceDefinition,TypeDefinition] interfa ... 1 {} | semmle.label | 1 | -| tst.ts:55:3:55:38 | [ExportDeclaration] export ... 1 {} | tst.ts:55:10:55:38 | [InterfaceDefinition,TypeDefinition] interfa ... 1 {} | semmle.order | 1 | -| tst.ts:55:10:55:38 | [InterfaceDefinition,TypeDefinition] interfa ... 1 {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | -| tst.ts:55:10:55:38 | [InterfaceDefinition,TypeDefinition] interfa ... 1 {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | -| tst.ts:55:10:55:38 | [InterfaceDefinition,TypeDefinition] interfa ... 1 {} | tst.ts:55:20:55:32 | [Identifier] InnerGeneric1 | semmle.label | 1 | -| tst.ts:55:10:55:38 | [InterfaceDefinition,TypeDefinition] interfa ... 1 {} | tst.ts:55:20:55:32 | [Identifier] InnerGeneric1 | semmle.order | 1 | +| tst.ts:54:3:54:23 | [ExportDeclaration] export ... ce I {} | tst.ts:54:10:54:23 | [InterfaceDeclaration,TypeDefinition] interface I {} | semmle.label | 1 | +| tst.ts:54:3:54:23 | [ExportDeclaration] export ... ce I {} | tst.ts:54:10:54:23 | [InterfaceDeclaration,TypeDefinition] interface I {} | semmle.order | 1 | +| tst.ts:54:10:54:23 | [InterfaceDeclaration,TypeDefinition] interface I {} | tst.ts:54:20:54:20 | [Identifier] I | semmle.label | 1 | +| tst.ts:54:10:54:23 | [InterfaceDeclaration,TypeDefinition] interface I {} | tst.ts:54:20:54:20 | [Identifier] I | semmle.order | 1 | +| tst.ts:55:3:55:38 | [ExportDeclaration] export ... 1 {} | tst.ts:55:10:55:38 | [InterfaceDeclaration,TypeDefinition] interfa ... 1 {} | semmle.label | 1 | +| tst.ts:55:3:55:38 | [ExportDeclaration] export ... 1 {} | tst.ts:55:10:55:38 | [InterfaceDeclaration,TypeDefinition] interfa ... 1 {} | semmle.order | 1 | +| tst.ts:55:10:55:38 | [InterfaceDeclaration,TypeDefinition] interfa ... 1 {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | +| tst.ts:55:10:55:38 | [InterfaceDeclaration,TypeDefinition] interfa ... 1 {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | +| tst.ts:55:10:55:38 | [InterfaceDeclaration,TypeDefinition] interfa ... 1 {} | tst.ts:55:20:55:32 | [Identifier] InnerGeneric1 | semmle.label | 1 | +| tst.ts:55:10:55:38 | [InterfaceDeclaration,TypeDefinition] interfa ... 1 {} | tst.ts:55:20:55:32 | [Identifier] InnerGeneric1 | semmle.order | 1 | | tst.ts:55:34:55:34 | [TypeParameter] T | tst.ts:55:34:55:34 | [Identifier] T | semmle.label | 1 | | tst.ts:55:34:55:34 | [TypeParameter] T | tst.ts:55:34:55:34 | [Identifier] T | semmle.order | 1 | | tst.ts:56:3:59:3 | [ExportDeclaration] export ... {} } | tst.ts:56:10:59:3 | [NamespaceDeclaration] namespa ... {} } | semmle.label | 1 | @@ -1550,16 +1550,16 @@ edges | tst.ts:56:10:59:3 | [NamespaceDeclaration] namespa ... {} } | tst.ts:57:5:57:25 | [ExportDeclaration] export ... ce J {} | semmle.order | 2 | | tst.ts:56:10:59:3 | [NamespaceDeclaration] namespa ... {} } | tst.ts:58:5:58:40 | [ExportDeclaration] export ... 2 {} | semmle.label | 3 | | tst.ts:56:10:59:3 | [NamespaceDeclaration] namespa ... {} } | tst.ts:58:5:58:40 | [ExportDeclaration] export ... 2 {} | semmle.order | 3 | -| tst.ts:57:5:57:25 | [ExportDeclaration] export ... ce J {} | tst.ts:57:12:57:25 | [InterfaceDefinition,TypeDefinition] interface J {} | semmle.label | 1 | -| tst.ts:57:5:57:25 | [ExportDeclaration] export ... ce J {} | tst.ts:57:12:57:25 | [InterfaceDefinition,TypeDefinition] interface J {} | semmle.order | 1 | -| tst.ts:57:12:57:25 | [InterfaceDefinition,TypeDefinition] interface J {} | tst.ts:57:22:57:22 | [Identifier] J | semmle.label | 1 | -| tst.ts:57:12:57:25 | [InterfaceDefinition,TypeDefinition] interface J {} | tst.ts:57:22:57:22 | [Identifier] J | semmle.order | 1 | -| tst.ts:58:5:58:40 | [ExportDeclaration] export ... 2 {} | tst.ts:58:12:58:40 | [InterfaceDefinition,TypeDefinition] interfa ... 2 {} | semmle.label | 1 | -| tst.ts:58:5:58:40 | [ExportDeclaration] export ... 2 {} | tst.ts:58:12:58:40 | [InterfaceDefinition,TypeDefinition] interfa ... 2 {} | semmle.order | 1 | -| tst.ts:58:12:58:40 | [InterfaceDefinition,TypeDefinition] interfa ... 2 {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | -| tst.ts:58:12:58:40 | [InterfaceDefinition,TypeDefinition] interfa ... 2 {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | -| tst.ts:58:12:58:40 | [InterfaceDefinition,TypeDefinition] interfa ... 2 {} | tst.ts:58:22:58:34 | [Identifier] InnerGeneric2 | semmle.label | 1 | -| tst.ts:58:12:58:40 | [InterfaceDefinition,TypeDefinition] interfa ... 2 {} | tst.ts:58:22:58:34 | [Identifier] InnerGeneric2 | semmle.order | 1 | +| tst.ts:57:5:57:25 | [ExportDeclaration] export ... ce J {} | tst.ts:57:12:57:25 | [InterfaceDeclaration,TypeDefinition] interface J {} | semmle.label | 1 | +| tst.ts:57:5:57:25 | [ExportDeclaration] export ... ce J {} | tst.ts:57:12:57:25 | [InterfaceDeclaration,TypeDefinition] interface J {} | semmle.order | 1 | +| tst.ts:57:12:57:25 | [InterfaceDeclaration,TypeDefinition] interface J {} | tst.ts:57:22:57:22 | [Identifier] J | semmle.label | 1 | +| tst.ts:57:12:57:25 | [InterfaceDeclaration,TypeDefinition] interface J {} | tst.ts:57:22:57:22 | [Identifier] J | semmle.order | 1 | +| tst.ts:58:5:58:40 | [ExportDeclaration] export ... 2 {} | tst.ts:58:12:58:40 | [InterfaceDeclaration,TypeDefinition] interfa ... 2 {} | semmle.label | 1 | +| tst.ts:58:5:58:40 | [ExportDeclaration] export ... 2 {} | tst.ts:58:12:58:40 | [InterfaceDeclaration,TypeDefinition] interfa ... 2 {} | semmle.order | 1 | +| tst.ts:58:12:58:40 | [InterfaceDeclaration,TypeDefinition] interfa ... 2 {} | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | +| tst.ts:58:12:58:40 | [InterfaceDeclaration,TypeDefinition] interfa ... 2 {} | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | +| tst.ts:58:12:58:40 | [InterfaceDeclaration,TypeDefinition] interfa ... 2 {} | tst.ts:58:22:58:34 | [Identifier] InnerGeneric2 | semmle.label | 1 | +| tst.ts:58:12:58:40 | [InterfaceDeclaration,TypeDefinition] interfa ... 2 {} | tst.ts:58:22:58:34 | [Identifier] InnerGeneric2 | semmle.order | 1 | | tst.ts:58:36:58:36 | [TypeParameter] T | tst.ts:58:36:58:36 | [Identifier] T | semmle.label | 1 | | tst.ts:58:36:58:36 | [TypeParameter] T | tst.ts:58:36:58:36 | [Identifier] T | semmle.order | 1 | | tst.ts:60:3:60:19 | [ExportDeclaration] export var x = 5; | tst.ts:60:10:60:19 | [DeclStmt] var x = ... | semmle.label | 1 | @@ -1696,12 +1696,12 @@ edges | tst.ts:72:5:72:35 | [VariableDeclarator] typeofV ... fiedVar | tst.ts:72:17:72:35 | [TypeofTypeExpr] typeof qualifiedVar | semmle.order | 2 | | tst.ts:72:17:72:35 | [TypeofTypeExpr] typeof qualifiedVar | tst.ts:72:24:72:35 | [LocalVarTypeAccess] qualifiedVar | semmle.label | 1 | | tst.ts:72:17:72:35 | [TypeofTypeExpr] typeof qualifiedVar | tst.ts:72:24:72:35 | [LocalVarTypeAccess] qualifiedVar | semmle.order | 1 | -| tst.ts:74:1:77:1 | [InterfaceDefinition,TypeDefinition] interfa ... Leaf; } | tst.ts:74:11:74:14 | [Identifier] Node | semmle.label | 1 | -| tst.ts:74:1:77:1 | [InterfaceDefinition,TypeDefinition] interfa ... Leaf; } | tst.ts:74:11:74:14 | [Identifier] Node | semmle.order | 1 | -| tst.ts:74:1:77:1 | [InterfaceDefinition,TypeDefinition] interfa ... Leaf; } | tst.ts:75:3:75:29 | [MethodSignature] isThisL ... s Leaf; | semmle.label | 2 | -| tst.ts:74:1:77:1 | [InterfaceDefinition,TypeDefinition] interfa ... Leaf; } | tst.ts:75:3:75:29 | [MethodSignature] isThisL ... s Leaf; | semmle.order | 2 | -| tst.ts:74:1:77:1 | [InterfaceDefinition,TypeDefinition] interfa ... Leaf; } | tst.ts:76:3:76:33 | [MethodSignature] isThatL ... s Leaf; | semmle.label | 3 | -| tst.ts:74:1:77:1 | [InterfaceDefinition,TypeDefinition] interfa ... Leaf; } | tst.ts:76:3:76:33 | [MethodSignature] isThatL ... s Leaf; | semmle.order | 3 | +| tst.ts:74:1:77:1 | [InterfaceDeclaration,TypeDefinition] interfa ... Leaf; } | tst.ts:74:11:74:14 | [Identifier] Node | semmle.label | 1 | +| tst.ts:74:1:77:1 | [InterfaceDeclaration,TypeDefinition] interfa ... Leaf; } | tst.ts:74:11:74:14 | [Identifier] Node | semmle.order | 1 | +| tst.ts:74:1:77:1 | [InterfaceDeclaration,TypeDefinition] interfa ... Leaf; } | tst.ts:75:3:75:29 | [MethodSignature] isThisL ... s Leaf; | semmle.label | 2 | +| tst.ts:74:1:77:1 | [InterfaceDeclaration,TypeDefinition] interfa ... Leaf; } | tst.ts:75:3:75:29 | [MethodSignature] isThisL ... s Leaf; | semmle.order | 2 | +| tst.ts:74:1:77:1 | [InterfaceDeclaration,TypeDefinition] interfa ... Leaf; } | tst.ts:76:3:76:33 | [MethodSignature] isThatL ... s Leaf; | semmle.label | 3 | +| tst.ts:74:1:77:1 | [InterfaceDeclaration,TypeDefinition] interfa ... Leaf; } | tst.ts:76:3:76:33 | [MethodSignature] isThatL ... s Leaf; | semmle.order | 3 | | tst.ts:75:3:75:29 | [FunctionExpr] isThisL ... s Leaf; | tst.ts:75:17:75:28 | [IsTypeExpr] this is Leaf | semmle.label | 4 | | tst.ts:75:3:75:29 | [FunctionExpr] isThisL ... s Leaf; | tst.ts:75:17:75:28 | [IsTypeExpr] this is Leaf | semmle.order | 4 | | tst.ts:75:3:75:29 | [MethodSignature] isThisL ... s Leaf; | tst.ts:75:3:75:12 | [Label] isThisLeaf | semmle.label | 1 | @@ -1724,8 +1724,8 @@ edges | tst.ts:76:21:76:32 | [IsTypeExpr] that is Leaf | tst.ts:76:21:76:24 | [LocalVarTypeAccess] that | semmle.order | 1 | | tst.ts:76:21:76:32 | [IsTypeExpr] that is Leaf | tst.ts:76:29:76:32 | [LocalTypeAccess] Leaf | semmle.label | 2 | | tst.ts:76:21:76:32 | [IsTypeExpr] that is Leaf | tst.ts:76:29:76:32 | [LocalTypeAccess] Leaf | semmle.order | 2 | -| tst.ts:78:1:78:17 | [InterfaceDefinition,TypeDefinition] interface Leaf {} | tst.ts:78:11:78:14 | [Identifier] Leaf | semmle.label | 1 | -| tst.ts:78:1:78:17 | [InterfaceDefinition,TypeDefinition] interface Leaf {} | tst.ts:78:11:78:14 | [Identifier] Leaf | semmle.order | 1 | +| tst.ts:78:1:78:17 | [InterfaceDeclaration,TypeDefinition] interface Leaf {} | tst.ts:78:11:78:14 | [Identifier] Leaf | semmle.label | 1 | +| tst.ts:78:1:78:17 | [InterfaceDeclaration,TypeDefinition] interface Leaf {} | tst.ts:78:11:78:14 | [Identifier] Leaf | semmle.order | 1 | | tst.ts:80:1:80:73 | [FunctionDeclStmt] functio ... alse; } | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | | tst.ts:80:1:80:73 | [FunctionDeclStmt] functio ... alse; } | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | | tst.ts:80:1:80:73 | [FunctionDeclStmt] functio ... alse; } | tst.ts:80:10:80:22 | [VarDecl] complexIsType | semmle.label | 0 | @@ -2136,10 +2136,10 @@ edges | tst.ts:116:35:116:35 | [SimpleParameter] x | tst.ts:116:38:116:43 | [KeywordTypeExpr] number | semmle.order | 0 | | tst.ts:116:46:116:46 | [SimpleParameter] y | tst.ts:116:49:116:54 | [KeywordTypeExpr] string | semmle.label | 0 | | tst.ts:116:46:116:46 | [SimpleParameter] y | tst.ts:116:49:116:54 | [KeywordTypeExpr] string | semmle.order | 0 | -| tst.ts:118:1:120:1 | [InterfaceDefinition,TypeDefinition] interfa ... ram); } | tst.ts:118:11:118:32 | [Identifier] InterfaceWithThisParam | semmle.label | 1 | -| tst.ts:118:1:120:1 | [InterfaceDefinition,TypeDefinition] interfa ... ram); } | tst.ts:118:11:118:32 | [Identifier] InterfaceWithThisParam | semmle.order | 1 | -| tst.ts:118:1:120:1 | [InterfaceDefinition,TypeDefinition] interfa ... ram); } | tst.ts:119:3:119:45 | [MethodSignature] hasThis ... Param); | semmle.label | 2 | -| tst.ts:118:1:120:1 | [InterfaceDefinition,TypeDefinition] interfa ... ram); } | tst.ts:119:3:119:45 | [MethodSignature] hasThis ... Param); | semmle.order | 2 | +| tst.ts:118:1:120:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ram); } | tst.ts:118:11:118:32 | [Identifier] InterfaceWithThisParam | semmle.label | 1 | +| tst.ts:118:1:120:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ram); } | tst.ts:118:11:118:32 | [Identifier] InterfaceWithThisParam | semmle.order | 1 | +| tst.ts:118:1:120:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ram); } | tst.ts:119:3:119:45 | [MethodSignature] hasThis ... Param); | semmle.label | 2 | +| tst.ts:118:1:120:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ram); } | tst.ts:119:3:119:45 | [MethodSignature] hasThis ... Param); | semmle.order | 2 | | tst.ts:119:3:119:45 | [FunctionExpr] hasThis ... Param); | tst.ts:119:22:119:43 | [LocalTypeAccess] InterfaceWithThisParam | semmle.label | 3 | | tst.ts:119:3:119:45 | [FunctionExpr] hasThis ... Param); | tst.ts:119:22:119:43 | [LocalTypeAccess] InterfaceWithThisParam | semmle.order | 3 | | tst.ts:119:3:119:45 | [MethodSignature] hasThis ... Param); | tst.ts:119:3:119:14 | [Label] hasThisParam | semmle.label | 1 | diff --git a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected index 8b70a311f41..357da6a0263 100644 --- a/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected +++ b/javascript/ql/test/library-tests/TypeScript/Types/printAst.expected @@ -462,8 +462,8 @@ nodes | type_definitions.ts:1:8:1:17 | [ImportSpecifier] * as dummy | semmle.label | [ImportSpecifier] * as dummy | | type_definitions.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | [VarDecl] dummy | | type_definitions.ts:1:24:1:32 | [Literal] "./dummy" | semmle.label | [Literal] "./dummy" | -| type_definitions.ts:3:1:5:1 | [InterfaceDefinition,TypeDefinition] interfa ... x: S; } | semmle.label | [InterfaceDefinition,TypeDefinition] interfa ... x: S; } | -| type_definitions.ts:3:1:5:1 | [InterfaceDefinition,TypeDefinition] interfa ... x: S; } | semmle.order | 61 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | semmle.order | 61 | | type_definitions.ts:3:11:3:11 | [Identifier] I | semmle.label | [Identifier] I | | type_definitions.ts:3:13:3:13 | [Identifier] S | semmle.label | [Identifier] S | | type_definitions.ts:3:13:3:13 | [TypeParameter] S | semmle.label | [TypeParameter] S | @@ -1221,12 +1221,12 @@ edges | type_definitions.ts:1:1:1:33 | [ImportDeclaration] import ... dummy"; | type_definitions.ts:1:24:1:32 | [Literal] "./dummy" | semmle.order | 2 | | type_definitions.ts:1:8:1:17 | [ImportSpecifier] * as dummy | type_definitions.ts:1:13:1:17 | [VarDecl] dummy | semmle.label | 1 | | type_definitions.ts:1:8:1:17 | [ImportSpecifier] * as dummy | type_definitions.ts:1:13:1:17 | [VarDecl] dummy | semmle.order | 1 | -| type_definitions.ts:3:1:5:1 | [InterfaceDefinition,TypeDefinition] interfa ... x: S; } | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | -| type_definitions.ts:3:1:5:1 | [InterfaceDefinition,TypeDefinition] interfa ... x: S; } | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | -| type_definitions.ts:3:1:5:1 | [InterfaceDefinition,TypeDefinition] interfa ... x: S; } | type_definitions.ts:3:11:3:11 | [Identifier] I | semmle.label | 1 | -| type_definitions.ts:3:1:5:1 | [InterfaceDefinition,TypeDefinition] interfa ... x: S; } | type_definitions.ts:3:11:3:11 | [Identifier] I | semmle.order | 1 | -| type_definitions.ts:3:1:5:1 | [InterfaceDefinition,TypeDefinition] interfa ... x: S; } | type_definitions.ts:4:3:4:7 | [FieldDeclaration] x: S; | semmle.label | 3 | -| type_definitions.ts:3:1:5:1 | [InterfaceDefinition,TypeDefinition] interfa ... x: S; } | type_definitions.ts:4:3:4:7 | [FieldDeclaration] x: S; | semmle.order | 3 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | file://:0:0:0:0 | (TypeParameters) | semmle.label | -100 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | file://:0:0:0:0 | (TypeParameters) | semmle.order | -100 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | type_definitions.ts:3:11:3:11 | [Identifier] I | semmle.label | 1 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | type_definitions.ts:3:11:3:11 | [Identifier] I | semmle.order | 1 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | type_definitions.ts:4:3:4:7 | [FieldDeclaration] x: S; | semmle.label | 3 | +| type_definitions.ts:3:1:5:1 | [InterfaceDeclaration,TypeDefinition] interfa ... x: S; } | type_definitions.ts:4:3:4:7 | [FieldDeclaration] x: S; | semmle.order | 3 | | type_definitions.ts:3:13:3:13 | [TypeParameter] S | type_definitions.ts:3:13:3:13 | [Identifier] S | semmle.label | 1 | | type_definitions.ts:3:13:3:13 | [TypeParameter] S | type_definitions.ts:3:13:3:13 | [Identifier] S | semmle.order | 1 | | type_definitions.ts:4:3:4:7 | [FieldDeclaration] x: S; | type_definitions.ts:4:3:4:3 | [Label] x | semmle.label | 1 | From 4fa2a79b41187db7ae4f8e03cce1d58970b484d1 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Mon, 19 Oct 2020 14:57:18 +0100 Subject: [PATCH 166/166] Fix test data for WebView experimental query --- .../security/CWE-749/UnsafeAndroidAccess.expected | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected index 800af691785..660472cd7d8 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-749/UnsafeAndroidAccess.expected @@ -1,3 +1,15 @@ +edges +| UnsafeAndroidAccess.java:29:20:29:59 | getString(...) : String | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | +| UnsafeAndroidAccess.java:52:20:52:52 | getStringExtra(...) : String | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | +| UnsafeAndroidAccess.java:94:20:94:52 | getStringExtra(...) : String | UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | +nodes +| UnsafeAndroidAccess.java:29:20:29:59 | getString(...) : String | semmle.label | getString(...) : String | +| UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | semmle.label | thisUrl | +| UnsafeAndroidAccess.java:52:20:52:52 | getStringExtra(...) : String | semmle.label | getStringExtra(...) : String | +| UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | semmle.label | thisUrl | +| UnsafeAndroidAccess.java:94:20:94:52 | getStringExtra(...) : String | semmle.label | getStringExtra(...) : String | +| UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | semmle.label | thisUrl | +#select | UnsafeAndroidAccess.java:30:3:30:21 | loadUrl(...) | UnsafeAndroidAccess.java:29:20:29:59 | getString(...) : String | UnsafeAndroidAccess.java:30:14:30:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:29:20:29:59 | getString(...) | user input vulnerable to cross-origin and sensitive resource disclosure attacks | | UnsafeAndroidAccess.java:53:3:53:21 | loadUrl(...) | UnsafeAndroidAccess.java:52:20:52:52 | getStringExtra(...) : String | UnsafeAndroidAccess.java:53:14:53:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:52:20:52:52 | getStringExtra(...) | user input vulnerable to cross-origin and sensitive resource disclosure attacks | | UnsafeAndroidAccess.java:95:3:95:21 | loadUrl(...) | UnsafeAndroidAccess.java:94:20:94:52 | getStringExtra(...) : String | UnsafeAndroidAccess.java:95:14:95:20 | thisUrl | Unsafe resource fetching in Android webview due to $@. | UnsafeAndroidAccess.java:94:20:94:52 | getStringExtra(...) | user input vulnerable to XSS attacks |