diff --git a/.codeqlmanifest.json b/.codeqlmanifest.json
index 020c9ce92449..30efc842b00f 100644
--- a/.codeqlmanifest.json
+++ b/.codeqlmanifest.json
@@ -1,5 +1,6 @@
{ "provide": [ "*/ql/src/qlpack.yml",
"*/ql/test/qlpack.yml",
+ "*/ql/examples/qlpack.yml",
"*/upgrades/qlpack.yml",
"misc/legacy-support/*/qlpack.yml",
"misc/suite-helpers/qlpack.yml" ] }
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index eef634fae2a3..8f9ffdde4de4 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -4,6 +4,6 @@
"slevesque.vscode-zipexplorer"
],
"settings": {
- "codeQL.experimentalBqrsParsing": true
+ "codeQL.runningQueries.memory": 2048
}
}
diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml
new file mode 100644
index 000000000000..cbd1f33a7f66
--- /dev/null
+++ b/.github/codeql/codeql-config.yml
@@ -0,0 +1,11 @@
+name: "CodeQL config"
+
+queries:
+ - uses: security-and-quality
+
+paths-ignore:
+ - '/cpp/'
+ - '/java/'
+ - '/python/'
+ - '/javascript/ql/test'
+ - '/javascript/extractor/tests'
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 000000000000..f1844da86cff
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,52 @@
+name: "Code scanning - action"
+
+on:
+ push:
+ pull_request:
+ schedule:
+ - cron: '0 9 * * 1'
+
+jobs:
+ CodeQL-Build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v2
+ with:
+ # We must fetch at least the immediate parents so that if this is
+ # a pull request then we can checkout the head.
+ fetch-depth: 2
+
+ # If this run was triggered by a pull request event, then checkout
+ # the head of the pull request instead of the merge commit.
+ - run: git checkout HEAD^2
+ if: ${{ github.event_name == 'pull_request' }}
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v1
+ # Override language selection by uncommenting this and choosing your languages
+ with:
+ languages: csharp
+ config-file: ./.github/codeql/codeql-config.yml
+
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
+ # If this step fails, then you should remove it and run the build manually (see below)
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v1
+
+ # âšī¸ Command-line programs to run using the OS shell.
+ # đ https://git.io/JvXDl
+
+ # âī¸ If the Autobuild fails above, remove it and uncomment the following three lines
+ # and modify them (or add more) to build your code if your project
+ # uses a compiled language
+
+ #- run: |
+ # make bootstrap
+ # make release
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v1
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
new file mode 100644
index 000000000000..3606bd75cc55
--- /dev/null
+++ b/.github/workflows/labeler.yml
@@ -0,0 +1,11 @@
+name: "Pull Request Labeler"
+on:
+- pull_request_target
+
+jobs:
+ triage:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/labeler@v2
+ with:
+ repo-token: "${{ secrets.GITHUB_TOKEN }}"
diff --git a/.github/workflows/query-list.yml b/.github/workflows/query-list.yml
new file mode 100644
index 000000000000..a9f24b8c9c7c
--- /dev/null
+++ b/.github/workflows/query-list.yml
@@ -0,0 +1,49 @@
+name: Build code scanning query list
+
+on:
+ push:
+ branches:
+ - main
+ - 'rc/**'
+ pull_request:
+ paths:
+ - '.github/workflows/query-list.yml'
+ - 'misc/scripts/generate-code-scanning-query-list.py'
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Clone self (github/codeql)
+ uses: actions/checkout@v2
+ with:
+ path: codeql
+ - name: Clone github/codeql-go
+ uses: actions/checkout@v2
+ with:
+ repository: 'github/codeql-go'
+ path: codeql-go
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.8
+ - name: Download CodeQL CLI
+ uses: dsaltares/fetch-gh-release-asset@aa37ae5c44d3c9820bc12fe675e8670ecd93bd1c
+ with:
+ repo: "github/codeql-cli-binaries"
+ version: "latest"
+ file: "codeql-linux64.zip"
+ token: ${{ secrets.GITHUB_TOKEN }}
+ - name: Unzip CodeQL CLI
+ run: unzip -d codeql-cli codeql-linux64.zip
+ - name: Build code scanning query list
+ run: |
+ PATH="$PATH:codeql-cli/codeql" python codeql/misc/scripts/generate-code-scanning-query-list.py > code-scanning-query-list.csv
+ - name: Upload code scanning query list
+ uses: actions/upload-artifact@v2
+ with:
+ name: code-scanning-query-list
+ path: code-scanning-query-list.csv
+
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
index 8465a7c2f866..8eec4887a1e0 100644
--- a/.vscode/extensions.json
+++ b/.vscode/extensions.json
@@ -3,8 +3,8 @@
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
- "github.vscode-codeql"
+ "GitHub.vscode-codeql"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
-}
\ No newline at end of file
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 000000000000..b467b469f224
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "omnisharp.autoStart": false
+}
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f23df3b4e8bc..fa88395e5d09 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -53,14 +53,6 @@ After the experimental query is merged, we welcome pull requests to improve it.
## Using your personal data
-If you contribute to this project, we will record your name and email
-address (as provided by you with your contributions) as part of the code
-repositories, which are public. We might also use this information
-to contact you in relation to your contributions, as well as in the
-normal course of software development. We also store records of your
-CLA agreements. Under GDPR legislation, we do this
-on the basis of our legitimate interest in creating the CodeQL product.
-
-Please do get in touch (privacy@github.com) if you have any questions about
-this or our data protection policies.
+If you contribute to this project, we will record your name and email address (as provided by you with your contributions) as part of the code repositories, which are public. We might also use this information to contact you in relation to your contributions, as well as in the normal course of software development. We also store records of CLA agreements signed in the past, but no longer require contributors to sign a CLA. Under GDPR legislation, we do this on the basis of our legitimate interest in creating the CodeQL product.
+Please do get in touch (privacy@github.com) if you have any questions about this or our data protection policies.
diff --git a/README.md b/README.md
index 08e572a02463..9012e83f10da 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ You can use the [interactive query console](https://lgtm.com/help/lgtm/using-que
## Contributing
-We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/github/codeql/tree/master/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
+We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/github/codeql/tree/main/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
## License
diff --git a/change-notes/1.25/analysis-cpp.md b/change-notes/1.25/analysis-cpp.md
index 7ab98ffe859a..47997f070527 100644
--- a/change-notes/1.25/analysis-cpp.md
+++ b/change-notes/1.25/analysis-cpp.md
@@ -13,6 +13,8 @@ The following changes in version 1.25 affect C/C++ analysis in all applications.
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
+| Uncontrolled format string (`cpp/tainted-format-string`) | | This query is now displayed by default on LGTM. |
+| Uncontrolled format string (through global variable) (`cpp/tainted-format-string-through-global`) | | This query is now displayed by default on LGTM. |
## Changes to libraries
diff --git a/change-notes/1.25/analysis-csharp.md b/change-notes/1.25/analysis-csharp.md
index de4761c92489..10684aca4809 100644
--- a/change-notes/1.25/analysis-csharp.md
+++ b/change-notes/1.25/analysis-csharp.md
@@ -28,27 +28,51 @@ The following changes in version 1.25 affect C# analysis in all applications.
such as `A.B`, no longer are considered unbound generics. (Such nested types do,
however, still have relevant `.getSourceDeclaration()`s, for example `A<>.B`.)
* The data-flow library has been improved, which affects most security queries by potentially
- adding more results. Flow through methods now takes nested field reads/writes into account.
- For example, the library is able to track flow from `"taint"` to `Sink()` via the method
- `GetF2F1()` in
- ```csharp
- class C1
- {
- string F1;
- }
-
- class C2
- {
- C1 F2;
-
- string GetF2F1() => F2.F1; // Nested field read
-
- void M()
- {
- F2 = new C1() { F1 = "taint" };
- Sink(GetF2F1()); // NEW: "taint" reaches here
- }
- }
- ```
+ adding more results:
+ - Flow through methods now takes nested field reads/writes into account.
+ For example, the library is able to track flow from `"taint"` to `Sink()` via the method
+ `GetF2F1()` in
+ ```csharp
+ class C1
+ {
+ string F1;
+ }
+
+ class C2
+ {
+ C1 F2;
+
+ string GetF2F1() => F2.F1; // Nested field read
+
+ void M()
+ {
+ F2 = new C1() { F1 = "taint" };
+ Sink(GetF2F1()); // NEW: "taint" reaches here
+ }
+ }
+ ```
+ - Flow through collections is now modeled precisely. For example, instead of modeling an array
+ store `a[i] = x` as a taint-step from `x` to `a`, we now model it as a data-flow step that
+ stores `x` into `a`. To get the value back out, a matching read step must be taken.
+
+ For source-code based data-flow analysis, the following constructs are modeled as stores into
+ collections:
+ - Direct array assignments, `a[i] = x`.
+ - Array initializers, `new [] { x }`.
+ - C# 6-style array initializers, `new C() { Array = { [i] = x } }`.
+ - Call arguments that match a `params` parameter, where the C# compiler creates an array under-the-hood.
+ - `yield return` statements.
+
+ The following source-code constructs read from a collection:
+ - Direct array reads, `a[i]`.
+ - `foreach` statements.
+
+ For calls out to library code, existing flow summaries have been refined to precisely
+ capture how they interact with collection contents. For example, a call to
+ `System.Collections.Generic.List.Add(T)` stores the value of the argument into the
+ qualifier, and a call to `System.Collections.Generic.List.get_Item(int)` (that is, an
+ indexer call) reads contents out of the qualifier. Moreover, the effect of
+ collection-clearing methods such as `System.Collections.Generic.List.Clear()` is now
+ also modeled.
## Changes to autobuilder
diff --git a/change-notes/1.25/analysis-java.md b/change-notes/1.25/analysis-java.md
index 7cdd9e491a2b..ab11e5aaaf1e 100644
--- a/change-notes/1.25/analysis-java.md
+++ b/change-notes/1.25/analysis-java.md
@@ -4,20 +4,26 @@ The following changes in version 1.25 affect Java analysis in all applications.
## General improvements
-## New queries
-
-| **Query** | **Tags** | **Purpose** |
-|-----------------------------|-----------|--------------------------------------------------------------------|
-
+The Java autobuilder has been improved to detect more Gradle Java versions.
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
-
+| Hard-coded credential in API call (`java/hardcoded-credential-api-call`) | More results | The query now recognizes the `BasicAWSCredentials` class of the Amazon client SDK library with hardcoded access key/secret key. |
+| Deserialization of user-controlled data (`java/unsafe-deserialization`) | Fewer false positive results | The query no longer reports results using `org.apache.commons.io.serialization.ValidatingObjectInputStream`. |
+| Use of a broken or risky cryptographic algorithm (`java/weak-cryptographic-algorithm`) | More results | The query now recognizes the `MessageDigest.getInstance` method. |
+| Use of a potentially broken or risky cryptographic algorithm (`java/potentially-weak-cryptographic-algorithm`) | More results | The query now recognizes the `MessageDigest.getInstance` method. |
+| Reading from a world writable file (`java/world-writable-file-read`) | More results | The query now recognizes more JDK file operations. |
## Changes to libraries
+* The data-flow library has been improved with more taint flow modeling for the
+ Collections framework and other classes of the JDK. This affects all security
+ queries using data flow and can yield additional results.
+* The data-flow library has been improved with more taint flow modeling for the
+ Spring framework. This affects all security queries using data flow and can
+ yield additional results on project that rely on the Spring framework.
* The data-flow library has been improved, which affects most security queries by potentially
adding more results. Flow through methods now takes nested field reads/writes into account.
For example, the library is able to track flow from `"taint"` to `sink()` via the method
@@ -39,3 +45,5 @@ The following changes in version 1.25 affect Java analysis in all applications.
}
}
```
+* The library has been extended with more support for Java 14 features
+ (`switch` expressions and pattern-matching for `instanceof`).
diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md
index 2aada0cbd86a..9491ecb657fc 100644
--- a/change-notes/1.25/analysis-javascript.md
+++ b/change-notes/1.25/analysis-javascript.md
@@ -6,22 +6,33 @@
- [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
- [bluebird](http://bluebirdjs.com/)
- [express](https://www.npmjs.com/package/express)
+ - [execa](https://www.npmjs.com/package/execa)
+ - [fancy-log](https://www.npmjs.com/package/fancy-log)
- [fastify](https://www.npmjs.com/package/fastify)
+ - [foreground-child](https://www.npmjs.com/package/foreground-child)
- [fstream](https://www.npmjs.com/package/fstream)
- [jGrowl](https://github.com/stanlemon/jGrowl)
- [jQuery](https://jquery.com/)
- [marsdb](https://www.npmjs.com/package/marsdb)
+ - [micro](https://www.npmjs.com/package/micro/)
- [minimongo](https://www.npmjs.com/package/minimongo/)
- [mssql](https://www.npmjs.com/package/mssql)
- [mysql](https://www.npmjs.com/package/mysql)
+ - [npmlog](https://www.npmjs.com/package/npmlog)
+ - [opener](https://www.npmjs.com/package/opener)
- [pg](https://www.npmjs.com/package/pg)
- [sequelize](https://www.npmjs.com/package/sequelize)
- [spanner](https://www.npmjs.com/package/spanner)
- [sqlite](https://www.npmjs.com/package/sqlite)
- [ssh2-streams](https://www.npmjs.com/package/ssh2-streams)
- [ssh2](https://www.npmjs.com/package/ssh2)
+ - [vue](https://www.npmjs.com/package/vue)
+ - [yargs](https://www.npmjs.com/package/yargs)
+ - [webpack-dev-server](https://www.npmjs.com/package/webpack-dev-server)
-* TypeScript 3.9 is now supported.
+* TypeScript 4.0 is now supported.
+
+* TypeScript code embedded in HTML and Vue files is now extracted and analyzed.
* The analysis of sanitizers has improved, leading to more accurate
results from the security queries.
@@ -30,10 +41,17 @@
| **Query** | **Tags** | **Purpose** |
|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Cross-site scripting through DOM (`js/xss-through-dom`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where existing text from the DOM is used as HTML. Results are not shown on LGTM by default. |
+| DOM text reinterpreted as HTML (`js/xss-through-dom`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where existing text from the DOM is used as HTML. Results are shown on LGTM by default. |
| Incomplete HTML attribute sanitization (`js/incomplete-html-attribute-sanitization`) | security, external/cwe/cwe-20, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities due to incomplete sanitization of HTML meta-characters. Results are shown on LGTM by default. |
| Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. |
| Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. |
+| Download of sensitive file through insecure connection (`js/insecure-download`) | security, external/cwe/cwe-829 | Highlights downloads of sensitive files through an unencrypted protocol. Results are shown on LGTM by default. |
+| Exposure of private files (`js/exposure-of-private-files`) | security, external/cwe/cwe-200 | Highlights servers that serve private files. Results are shown on LGTM by default. |
+| Creating biased random numbers from a cryptographically secure source (`js/biased-cryptographic-random`) | security, external/cwe/cwe-327 | Highlights mathematical operations on cryptographically secure numbers that can create biased results. Results are shown on LGTM by default. |
+| Storage of sensitive information in build artifact (`js/build-artifact-leak`) | security, external/cwe/cwe-312 | Highlights storage of sensitive information in build artifacts. Results are shown on LGTM by default. |
+| Improper code sanitization (`js/bad-code-sanitization`) | security, external/cwe/cwe-094, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights string concatenation where code is constructed without proper sanitization. Results are shown on LGTM by default. |
+| Disabling certificate validation (`js/disabling-certificate-validation`) | security, external/cwe-295 | Highlights locations where SSL certificate validation is disabled. Results are shown on LGTM by default. |
+| Incomplete multi-character sanitization (`js/incomplete-multi-character-sanitization`) | correctness, security, external/cwe/cwe-20, external/cwe/cwe-116 | Highlights sanitizers that fail to remove dangerous substrings completely. Results are shown on LGTM by default. |
## Changes to existing queries
@@ -42,14 +60,19 @@
| Client-side cross-site scripting (`js/xss`) | Fewer results | This query now recognizes additional safe patterns of constructing HTML. |
| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | Fewer results | This query now recognizes additional safe patterns of doing URL redirects. |
| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving NoSQL code operators are now recognized. |
+| Exception text reinterpreted as HTML (`js/exception-xss`) | Rephrased and changed visibility | Rephrased name and alert message. Severity lowered from error to warning. Results are now shown on LGTM by default. |
| Expression has no effect (`js/useless-expression`) | Fewer results | This query no longer flags an expression when that expression is the only content of the containing file. |
| Hard-coded credentials (`js/hardcoded-credentials`) | More results | This query now recognizes hard-coded credentials sent via HTTP authorization headers. |
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. |
+| Insecure randomness (`js/insecure-randomness`) | Fewer results | This query now recognizes when an insecure random value is used as a fallback when secure random values are unsupported. |
| Misspelled variable name (`js/misspelled-variable-name`) | Message changed | The message for this query now correctly identifies the misspelled variable in additional cases. |
+| Non-linear pattern (`js/non-linear-pattern`) | Fewer duplicates and message changed | This query now generates fewer duplicate alerts and has a clearer explanation in case of type annotations used in a pattern. |
| Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. |
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. |
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. |
+| Uncontrolled data used in path expression (`js/path-injection`) | Fewer results | This query no longer flags paths that have been checked to be part of a collection. |
| Unknown directive (`js/unknown-directive`) | Fewer results | This query no longer flags directives generated by the Babel compiler. |
+| Unneeded defensive code (`js/unneeded-defensive-code`) | Fewer false-positive results | This query now recognizes checks meant to handle the `document.all` object. |
| Unused property (`js/unused-property`) | Fewer results | This query no longer flags properties of objects that are operands of `yield` expressions. |
| Zip Slip (`js/zipslip`) | More results | This query now recognizes additional vulnerabilities. |
@@ -85,3 +108,4 @@ The following low-precision queries are no longer run by default on LGTM (their
- `ParameterNode.asExpr()` and `.getAstNode()` now gets the parameter's AST node, whereas previously it had no result.
- `Expr.flow()` now has a more meaningful result for destructuring patterns. Previously this node was disconnected from the data flow graph. Now it represents the values being destructured by the pattern.
* The global data-flow and taint-tracking libraries now model indirect parameter accesses through the `arguments` object in some cases, which may lead to additional results from some of the security queries, particularly "Prototype pollution in utility function".
+* The predicates `Type.getProperty()` and variants of `Type.getMethod()` have been deprecated due to lack of use-cases. Looking up a named property of a static type is no longer supported, favoring faster extraction times instead.
diff --git a/change-notes/1.25/analysis-python.md b/change-notes/1.25/analysis-python.md
index 5d0fc69ec808..ed3496bc7349 100644
--- a/change-notes/1.25/analysis-python.md
+++ b/change-notes/1.25/analysis-python.md
@@ -1,22 +1,9 @@
# Improvements to Python analysis
-The following changes in version 1.25 affect Python analysis in all applications.
-
-## General improvements
-
-
-## New queries
-
-| **Query** | **Tags** | **Purpose** |
-|-----------------------------|-----------|--------------------------------------------------------------------|
-
-
-## Changes to existing queries
-
-| **Query** | **Expected impact** | **Change** |
-|----------------------------|------------------------|------------------------------------------------------------------|
-
-
-## Changes to libraries
-
* Importing `semmle.python.web.HttpRequest` will no longer import `UntrustedStringKind` transitively. `UntrustedStringKind` is the most commonly used non-abstract subclass of `ExternalStringKind`. If not imported (by one mean or another), taint-tracking queries that concern `ExternalStringKind` will not produce any results. Please ensure such queries contain an explicit import (`import semmle.python.security.strings.Untrusted`).
+* Added model of taint sources for HTTP servers using `http.server`.
+* Added taint modeling of routed parameters in Flask.
+* Improved modeling of built-in methods on strings for taint tracking.
+* Improved classification of test files.
+* New class `BoundMethodValue` represents a bound method during runtime.
+* The query `py/command-line-injection` now recognizes command execution with the `fabric` and `invoke` Python libraries.
diff --git a/change-notes/1.26/analysis-cpp.md b/change-notes/1.26/analysis-cpp.md
new file mode 100644
index 000000000000..6b31cf76050f
--- /dev/null
+++ b/change-notes/1.26/analysis-cpp.md
@@ -0,0 +1,30 @@
+# Improvements to C/C++ analysis
+
+The following changes in version 1.26 affect C/C++ analysis in all applications.
+
+## General improvements
+
+## New queries
+
+| **Query** | **Tags** | **Purpose** |
+|-----------------------------|-----------|--------------------------------------------------------------------|
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|----------------------------|------------------------|------------------------------------------------------------------|
+| Declaration hides parameter (`cpp/declaration-hides-parameter`) | Fewer false positive results | False positives involving template functions have been fixed. |
+| Inconsistent direction of for loop (`cpp/inconsistent-loop-direction`) | Fewer false positive results | The query now accounts for intentional wrapping of an unsigned loop counter. |
+| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | | The precision of this query has been decreased from "high" to "medium". As a result, the query is still run but results are no longer displayed on LGTM by default. |
+| Comparison result is always the same (`cpp/constant-comparison`) | More correct results | Bounds on expressions involving multiplication can now be determined in more cases. |
+
+## Changes to libraries
+
+* The QL class `Block`, denoting the `{ ... }` statement, is renamed to `BlockStmt`.
+* The models library now models many taint flows through `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`.
+* The models library now models many more taint flows through `std::string`.
+* The models library now models many taint flows through `std::istream` and `std::ostream`.
+* The models library now models some taint flows through `std::shared_ptr`, `std::unique_ptr`, `std::make_shared` and `std::make_unique`.
+* The models library now models many taint flows through `std::pair`, `std::map`, `std::unordered_map`, `std::set` and `std::unordered_set`.
+* The `SimpleRangeAnalysis` library now supports multiplications of the form
+ `e1 * e2` and `x *= e2` when `e1` and `e2` are unsigned or constant.
diff --git a/change-notes/1.26/analysis-csharp.md b/change-notes/1.26/analysis-csharp.md
new file mode 100644
index 000000000000..3d17e00ab704
--- /dev/null
+++ b/change-notes/1.26/analysis-csharp.md
@@ -0,0 +1,35 @@
+# Improvements to C# analysis
+
+The following changes in version 1.26 affect C# analysis in all applications.
+
+## New queries
+
+| **Query** | **Tags** | **Purpose** |
+|-----------------------------|-----------|--------------------------------------------------------------------|
+
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|------------------------------|------------------------|-----------------------------------|
+| Weak encryption: Insufficient key size (`cs/insufficient-key-size`) | More results | The required key size has been increased from 1024 to 2048. |
+
+## Removal of old queries
+
+## Changes to code extraction
+
+* Partial method bodies are extracted. Previously, partial method bodies were skipped completely.
+* Inferring the lengths of implicitely sized arrays is fixed. Previously, multidimensional arrays were always extracted with the same length for
+each dimension. With the fix, the array sizes `2` and `1` are extracted for `new int[,]{{1},{2}}`. Previously `2` and `2` were extracted.
+* The extractor is now assembly-insensitive by default. This means that two entities with the same
+ fully-qualified name are now mapped to the same entity in the resulting database, regardless of
+ whether they belong to different assemblies. Assembly sensitivity can be reenabled by passing
+ `--assemblysensitivetrap` to the extractor.
+
+## Changes to libraries
+
+## Changes to autobuilder
+
+## Changes to tooling support
+
+* The Abstract Syntax Tree of C# files can be printed in Visual Studio Code.
diff --git a/change-notes/1.26/analysis-java.md b/change-notes/1.26/analysis-java.md
new file mode 100644
index 000000000000..cf87be2d8b85
--- /dev/null
+++ b/change-notes/1.26/analysis-java.md
@@ -0,0 +1,21 @@
+# Improvements to Java analysis
+
+The following changes in version 1.26 affect Java analysis in all applications.
+
+## General improvements
+
+## New queries
+
+| **Query** | **Tags** | **Purpose** |
+|-----------------------------|-----------|--------------------------------------------------------------------|
+
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|------------------------------|------------------------|-----------------------------------|
+
+
+## Changes to libraries
+
+* The QL class `Block`, denoting the `{ ... }` statement, is renamed to `BlockStmt`.
diff --git a/change-notes/1.26/analysis-javascript.md b/change-notes/1.26/analysis-javascript.md
new file mode 100644
index 000000000000..01d35a12c2dc
--- /dev/null
+++ b/change-notes/1.26/analysis-javascript.md
@@ -0,0 +1,49 @@
+# Improvements to JavaScript analysis
+
+## General improvements
+
+* Support for the following frameworks and libraries has been improved:
+ - [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)
+ - [express](https://www.npmjs.com/package/express)
+ - [fast-json-stable-stringify](https://www.npmjs.com/package/fast-json-stable-stringify)
+ - [fast-safe-stringify](https://www.npmjs.com/package/fast-safe-stringify)
+ - [http](https://nodejs.org/api/http.html)
+ - [javascript-stringify](https://www.npmjs.com/package/javascript-stringify)
+ - [js-stringify](https://www.npmjs.com/package/js-stringify)
+ - [json-stable-stringify](https://www.npmjs.com/package/json-stable-stringify)
+ - [json-stringify-safe](https://www.npmjs.com/package/json-stringify-safe)
+ - [json3](https://www.npmjs.com/package/json3)
+ - [lodash](https://www.npmjs.com/package/lodash)
+ - [needle](https://www.npmjs.com/package/needle)
+ - [object-inspect](https://www.npmjs.com/package/object-inspect)
+ - [pretty-format](https://www.npmjs.com/package/pretty-format)
+ - [stringify-object](https://www.npmjs.com/package/stringify-object)
+ - [underscore](https://www.npmjs.com/package/underscore)
+
+* Analyzing files with the ".cjs" extension is now supported.
+* ES2021 features are now supported.
+
+## New queries
+
+| **Query** | **Tags** | **Purpose** |
+|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|--------------------------------|------------------------------|---------------------------------------------------------------------------|
+| Potentially unsafe external link (`js/unsafe-external-link`) | Fewer results | This query no longer flags URLs constructed using a template system where only the hash or query part of the URL is dynamic. |
+| Incomplete URL substring sanitization (`js/incomplete-url-substring-sanitization`) | More results | This query now recognizes additional URLs when the substring check is an inclusion check. |
+| Ambiguous HTML id attribute (`js/duplicate-html-id`) | Results no longer shown | Precision tag reduced to "low". The query is no longer run by default. |
+| Unused loop iteration variable (`js/unused-loop-variable`) | Fewer results | This query no longer flags variables in a destructuring array assignment that are not the last variable in the destructed array. |
+| Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | More results | This query now recognizes more commands where colon, dash, and underscore are used. |
+| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | More results | This query now detects more unsafe uses of nested option properties. |
+| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | More results | This query now recognizes some unsafe uses of `importScripts()` inside WebWorkers. |
+| Missing CSRF middleware (`js/missing-token-validation`) | More results | This query now recognizes writes to cookie and session variables as potentially vulnerable to CSRF attacks. |
+
+
+## Changes to libraries
+* The predicate `TypeAnnotation.hasQualifiedName` now works in more cases when the imported library was not present during extraction.
diff --git a/change-notes/1.26/analysis-python.md b/change-notes/1.26/analysis-python.md
new file mode 100644
index 000000000000..f60eb6b4354d
--- /dev/null
+++ b/change-notes/1.26/analysis-python.md
@@ -0,0 +1,22 @@
+# Improvements to Python analysis
+
+The following changes in version 1.26 affect Python analysis in all applications.
+
+## General improvements
+
+
+## New queries
+
+| **Query** | **Tags** | **Purpose** |
+|-----------------------------|-----------|--------------------------------------------------------------------|
+
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|----------------------------|------------------------|------------------------------------------------------------------|
+
+
+## Changes to libraries
+
+* Added taint tracking support for string formatting through f-strings.
diff --git a/config/identical-files.json b/config/identical-files.json
index 1a1324687a0d..9be5c0d0dd2d 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -1,5 +1,5 @@
{
- "DataFlow Java/C++/C#": [
+ "DataFlow Java/C++/C#/Python": [
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll",
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll",
@@ -18,15 +18,18 @@
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll",
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
- "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll"
+ "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
+ "python/ql/src/experimental/dataflow/internal/DataFlowImpl.qll",
+ "python/ql/src/experimental/dataflow/internal/DataFlowImpl2.qll"
],
- "DataFlow Java/C++/C# Common": [
+ "DataFlow Java/C++/C#/Python Common": [
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
- "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll"
+ "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
+ "python/ql/src/experimental/dataflow/internal/DataFlowImplCommon.qll"
],
- "TaintTracking::Configuration Java/C++/C#": [
+ "TaintTracking::Configuration Java/C++/C#/Python": [
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
@@ -37,13 +40,35 @@
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll",
"csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll",
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
- "java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll"
+ "java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
+ "python/ql/src/experimental/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
],
- "DataFlow Java/C++/C# Consistency checks": [
+ "DataFlow Java/C++/C#/Python Consistency checks": [
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
- "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll"
+ "csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
+ "python/ql/src/experimental/dataflow/internal/DataFlowImplConsistency.qll"
+ ],
+ "SsaReadPosition Java/C#": [
+ "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
+ "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
+ ],
+ "Sign Java/C#": [
+ "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
+ "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
+ ],
+ "SignAnalysis Java/C#": [
+ "java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll",
+ "csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll"
+ ],
+ "Bound Java/C#": [
+ "java/ql/src/semmle/code/java/dataflow/Bound.qll",
+ "csharp/ql/src/semmle/code/csharp/dataflow/Bound.qll"
+ ],
+ "ModulusAnalysis Java/C#": [
+ "java/ql/src/semmle/code/java/dataflow/ModulusAnalysis.qll",
+ "csharp/ql/src/semmle/code/csharp/dataflow/ModulusAnalysis.qll"
],
"C++ SubBasicBlocks": [
"cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
@@ -53,114 +78,122 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll"
],
"IR IRBlock": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRBlock.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRBlock.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll"
],
"IR IRVariable": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll"
],
"IR IRFunction": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRFunction.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRFunction.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll"
],
"IR Operand": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/Operand.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
],
"IR IRType": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll"
+ "csharp/ql/src/experimental/ir/implementation/IRType.qll"
],
"IR IRConfiguration": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/IRConfiguration.qll"
+ "csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll"
],
"IR UseSoundEscapeAnalysis": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/UseSoundEscapeAnalysis.qll"
+ "csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll"
+ ],
+ "IR IRFunctionBase": [
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
+ "csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll"
],
"IR Operand Tag": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/internal/OperandTag.qll"
+ "csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
],
- "IR TIRVariable":[
+ "IR TInstruction": [
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
+ "csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
+ ],
+ "IR TIRVariable": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll"
+ "csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
],
"IR IR": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/IR.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll"
],
"IR IRConsistency": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRConsistency.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRConsistency.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll"
],
"IR PrintIR": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/PrintIR.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll"
],
"IR IntegerConstant": [
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
- "csharp/ql/src/semmle/code/csharp/ir/internal/IntegerConstant.qll"
+ "csharp/ql/src/experimental/ir/internal/IntegerConstant.qll"
],
"IR IntegerInteval": [
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerInterval.qll",
- "csharp/ql/src/semmle/code/csharp/ir/internal/IntegerInterval.qll"
+ "csharp/ql/src/experimental/ir/internal/IntegerInterval.qll"
],
"IR IntegerPartial": [
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerPartial.qll",
- "csharp/ql/src/semmle/code/csharp/ir/internal/IntegerPartial.qll"
+ "csharp/ql/src/experimental/ir/internal/IntegerPartial.qll"
],
"IR Overlap": [
"cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll",
- "csharp/ql/src/semmle/code/csharp/ir/internal/Overlap.qll"
+ "csharp/ql/src/experimental/ir/internal/Overlap.qll"
],
"IR EdgeKind": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/EdgeKind.qll"
+ "csharp/ql/src/experimental/ir/implementation/EdgeKind.qll"
],
"IR MemoryAccessKind": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll"
+ "csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll"
],
"IR TempVariableTag": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/TempVariableTag.qll"
+ "csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll"
],
"IR Opcode": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll"
+ "csharp/ql/src/experimental/ir/implementation/Opcode.qll"
],
"IR SSAConsistency": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll"
],
"C++ IR InstructionImports": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
@@ -177,6 +210,11 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRBlockImports.qll"
],
+ "C++ IR IRFunctionImports": [
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll",
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll",
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll"
+ ],
"C++ IR IRVariableImports": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll",
@@ -199,7 +237,7 @@
"SSA AliasAnalysis": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
],
"C++ SSA AliasAnalysisImports": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysisImports.qll",
@@ -212,42 +250,42 @@
],
"IR SSA SimpleSSA": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
],
"IR AliasConfiguration (unaliased_ssa)": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
],
"IR SSA SSAConstruction": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
],
"IR SSA PrintSSA": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
],
"IR ValueNumberInternal": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
],
"C++ IR ValueNumber": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/gvn/ValueNumbering.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
],
"C++ IR PrintValueNumbering": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/gvn/PrintValueNumbering.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
],
"C++ IR ConstantAnalysis": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
@@ -276,32 +314,40 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
],
"C# IR InstructionImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
],
"C# IR IRImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/IRImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRImports.qll"
],
"C# IR IRBlockImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRBlockImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
+ ],
+ "C# IR IRFunctionImports": [
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll"
],
"C# IR IRVariableImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
],
"C# IR OperandImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/OperandImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
],
"C# IR PrintIRImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/internal/PrintIRImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
],
"C# IR ValueNumberingImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
+ "csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
+ "csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
+ ],
+ "Inline Test Expectations": [
+ "cpp/ql/test/TestUtilities/InlineExpectationsTest.qll",
+ "python/ql/test/TestUtilities/InlineExpectationsTest.qll"
],
"XML": [
"cpp/ql/src/semmle/code/cpp/XML.qll",
@@ -309,5 +355,50 @@
"java/ql/src/semmle/code/xml/XML.qll",
"javascript/ql/src/semmle/javascript/XML.qll",
"python/ql/src/semmle/python/xml/XML.qll"
+ ],
+ "DuplicationProblems.qhelp": [
+ "cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp",
+ "csharp/ql/src/Metrics/Files/DuplicationProblems.qhelp",
+ "javascript/ql/src/Metrics/DuplicationProblems.qhelp",
+ "python/ql/src/Metrics/DuplicationProblems.qhelp"
+ ],
+ "CommentedOutCodeQuery.qhelp": [
+ "cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp",
+ "python/ql/src/Lexical/CommentedOutCodeQuery.qhelp",
+ "csharp/ql/src/Bad Practices/Comments/CommentedOutCodeQuery.qhelp",
+ "java/ql/src/Violations of Best Practice/Comments/CommentedOutCodeQuery.qhelp",
+ "javascript/ql/src/Comments/CommentedOutCodeQuery.qhelp"
+ ],
+ "FLinesOfCodeReferences.qhelp": [
+ "java/ql/src/Metrics/Files/FLinesOfCodeReferences.qhelp",
+ "javascript/ql/src/Metrics/FLinesOfCodeReferences.qhelp"
+ ],
+ "FCommentRatioCommon.qhelp": [
+ "java/ql/src/Metrics/Files/FCommentRatioCommon.qhelp",
+ "javascript/ql/src/Metrics/FCommentRatioCommon.qhelp"
+ ],
+ "FLinesOfCodeOverview.qhelp": [
+ "java/ql/src/Metrics/Files/FLinesOfCodeOverview.qhelp",
+ "javascript/ql/src/Metrics/FLinesOfCodeOverview.qhelp"
+ ],
+ "CommentedOutCodeMetricOverview.qhelp": [
+ "cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp",
+ "csharp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp",
+ "java/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp",
+ "javascript/ql/src/Comments/CommentedOutCodeMetricOverview.qhelp",
+ "python/ql/src/Lexical/CommentedOutCodeMetricOverview.qhelp"
+ ],
+ "FLinesOfDuplicatedCodeCommon.qhelp": [
+ "cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp",
+ "java/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp",
+ "javascript/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.qhelp",
+ "python/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.qhelp"
+ ],
+ "CommentedOutCodeReferences.qhelp": [
+ "cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp",
+ "csharp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp",
+ "java/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp",
+ "javascript/ql/src/Comments/CommentedOutCodeReferences.qhelp",
+ "python/ql/src/Lexical/CommentedOutCodeReferences.qhelp"
]
-}
+}
\ No newline at end of file
diff --git a/config/opcode-qldoc.py b/config/opcode-qldoc.py
new file mode 100644
index 000000000000..e379e6a3ea96
--- /dev/null
+++ b/config/opcode-qldoc.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+
+import os
+import re
+path = os.path
+
+needs_an_re = re.compile(r'^(?!Unary)[AEIOU]') # Name requiring "an" instead of "a".
+start_qldoc_re = re.compile(r'^\s*/\*\*') # Start of a QLDoc comment
+end_qldoc_re = re.compile(r'\*/\s*$') # End of a QLDoc comment
+blank_qldoc_line_re = re.compile(r'^\s*\*\s*$') # A line in a QLDoc comment with only the '*'
+instruction_class_re = re.compile(r'^class (?P[A-aa-z0-9]+)Instruction\s') # Declaration of an `Instruction` class
+opcode_base_class_re = re.compile(r'^abstract class (?P[A-aa-z0-9]+)Opcode\s') # Declaration of an `Opcode` base class
+opcode_class_re = re.compile(r'^ class (?P[A-aa-z0-9]+)\s') # Declaration of an `Opcode` class
+
+script_dir = path.realpath(path.dirname(__file__))
+instruction_path = path.realpath(path.join(script_dir, '../cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll'))
+opcode_path = path.realpath(path.join(script_dir, '../cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll'))
+
+# Scan `Instruction.qll`, keeping track of the QLDoc comment attached to each declaration of a class
+# whose name ends with `Instruction`.
+instruction_comments = {}
+in_qldoc = False
+saw_blank_line_in_qldoc = False
+qldoc_lines = []
+with open(instruction_path, 'r', encoding='utf-8') as instr:
+ for line in instr:
+ if in_qldoc:
+ if end_qldoc_re.search(line):
+ qldoc_lines.append(line)
+ in_qldoc = False
+ elif blank_qldoc_line_re.search(line):
+ # We're going to skip any lines after the first blank line, to avoid duplicating all
+ # of the verbose description.
+ saw_blank_line_in_qldoc = True
+ elif not saw_blank_line_in_qldoc:
+ qldoc_lines.append(line)
+ else:
+ if start_qldoc_re.search(line):
+ # Starting a new QLDoc comment.
+ saw_blank_line_in_qldoc = False
+ qldoc_lines.append(line)
+ if not end_qldoc_re.search(line):
+ in_qldoc = True
+ else:
+ instruction_match = instruction_class_re.search(line)
+ if instruction_match:
+ # Found the declaration of an `Instruction` class. Record the QLDoc comments.
+ instruction_comments[instruction_match.group('name')] = qldoc_lines
+ qldoc_lines = []
+
+# Scan `Opcode.qll`. Whenever we see the declaration of an `Opcode` class for which we have a
+# corresponding `Instruction` class, we'll attach a copy of the `Instruction`'s QLDoc comment.
+in_qldoc = False
+qldoc_lines = []
+output_lines = []
+with open(opcode_path, 'r', encoding='utf-8') as opcode:
+ for line in opcode:
+ if in_qldoc:
+ qldoc_lines.append(line)
+ if end_qldoc_re.search(line):
+ in_qldoc = False
+ else:
+ if start_qldoc_re.search(line):
+ qldoc_lines.append(line)
+ if not end_qldoc_re.search(line):
+ in_qldoc = True
+ else:
+ name_without_suffix = None
+ name = None
+ indent = ''
+ opcode_base_match = opcode_base_class_re.search(line)
+ if opcode_base_match:
+ name_without_suffix = opcode_base_match.group('name')
+ name = name_without_suffix + 'Opcode'
+ else:
+ opcode_match = opcode_class_re.search(line)
+ if opcode_match:
+ name_without_suffix = opcode_match.group('name')
+ name = name_without_suffix
+ # Indent by two additional spaces, since opcodes are declared in the
+ # `Opcode` module.
+ indent = ' '
+
+ if name_without_suffix:
+ # Found an `Opcode` that matches a known `Instruction`. Replace the QLDoc with
+ # a copy of the one from the `Instruction`.
+ if instruction_comments.get(name_without_suffix):
+ article = 'an' if needs_an_re.search(name_without_suffix) else 'a'
+ qldoc_lines = [
+ indent + '/**\n',
+ indent + ' * The `Opcode` for ' + article + ' `' + name_without_suffix + 'Instruction`.\n',
+ indent + ' *\n',
+ indent + ' * See the `' + name_without_suffix + 'Instruction` documentation for more details.\n',
+ indent + ' */\n'
+ ]
+ output_lines.extend(qldoc_lines)
+ qldoc_lines = []
+ output_lines.append(line)
+
+# Write out the updated `Opcode.qll`
+with open(opcode_path, 'w', encoding='utf-8') as opcode:
+ opcode.writelines(output_lines)
diff --git a/constraintsolving/generation/data.py b/constraintsolving/generation/data.py
index b39bb99381e0..4cbf0d59ce81 100644
--- a/constraintsolving/generation/data.py
+++ b/constraintsolving/generation/data.py
@@ -112,7 +112,11 @@ def _get_tsm_query_file(self, query_type: str, filename: str) -> str:
return os.path.join(global_config.sources_root, "javascript", "ql", "src", "TSM", query_type, filename)
def _get_tsm_bqrs_file_for_entity(self, queried_entity: str, query_type: str) -> str:
- return self._get_tsm_bqrs_file(f"{queried_entity}-{query_type}.bqrs")
+ # Add query_type as prefix folder since database analyze
+ # reads the ql file from query_type/file.ql and generates the same route
+ # this will change if we replace database analyze with database query
+ return self._get_tsm_bqrs_file( f"{query_type}/{queried_entity}-{query_type}.bqrs")
+
def _get_tsm_bqrs_file(self, filename: str) -> str:
return os.path.join(constaintssolving_dir, self.project_dir, "results", "codeql-javascript", "TSM", filename)
diff --git a/constraintsolving/main.py b/constraintsolving/main.py
index 6a959590ddfe..eaa95d491900 100644
--- a/constraintsolving/main.py
+++ b/constraintsolving/main.py
@@ -34,10 +34,10 @@ def create_project_list(projectListFile):
parser.add_argument("--project-dir", dest="project_dir", required=True, type=str,
help="Directory of the CodeQL database")
-parser.add_argument("--query-type", dest="query_type", required=True, type=str, choices=["Xss", "NoSql", "Sql", "Sel"],
+parser.add_argument("--query-type", dest="query_type", required=True, type=str, choices=["Xss", "NoSql", "Sql", "Sel", "Path"],
help="Type of the query to solve")
parser.add_argument("--query-name", dest="query_name", required=True, type=str,
- choices=["NosqlInjectionWorse", "SqlInjectionWorse", "DomBasedXssWorse", "SeldonWorse"],
+ choices=["NosqlInjectionWorse", "SqlInjectionWorse", "DomBasedXssWorse", "SeldonWorse", "TaintedPathWorse"],
help="Name of the query to solve")
parser.add_argument("--project-list", dest="projectListFile", required=False, type=str,
help="Run all steps on the project passed on this list")
diff --git a/constraintsolving/misc/combinescores.py b/constraintsolving/misc/combinescores.py
index fc4313cd5a67..a75393f17894 100644
--- a/constraintsolving/misc/combinescores.py
+++ b/constraintsolving/misc/combinescores.py
@@ -74,7 +74,7 @@ def combine_scores(query, \
parser.add_argument("--project-dir", dest="project_dir", required=True, type=str,
help="Directory of the results score")
parser.add_argument("--query-name", dest="query_name", required=True, type=str,
- choices=["NosqlInjectionWorse", "SqlInjectionWorse", "DomBasedXssWorse"],
+ choices=["NosqlInjectionWorse", "SqlInjectionWorse", "DomBasedXssWorse","TaintedPathWorse"],
help="Name of the query to solve")
parsed_arguments = parser.parse_args()
diff --git a/constraintsolving/taintedpath_projects.txt b/constraintsolving/taintedpath_projects.txt
new file mode 100644
index 000000000000..81a1950c3d87
--- /dev/null
+++ b/constraintsolving/taintedpath_projects.txt
@@ -0,0 +1,49 @@
+ably/ably-js
+adaptlearning/adapt_authoring
+alecperkins/missive
+alphagov/public-sector-contract-prototype
+cdnjs/new-website
+cliss/camel
+#killed deltaepsilon/quiver-cms
+Espacorede/WikiStats
+FurryBotCo/FurryBot
+gitseo/vm
+gpac/node-gpac-dash
+hexparrot/mineos-node
+#hobbyquaker/ccu.io
+huangtao/cloud-test
+ibm-cloud-security/appid-serversdk-nodejs
+#idettman/idettman.github.io
+infor-design/enterprise
+JayK0720/Front-End
+juan-carlos-correa/mini-spotify
+mapbox/mapbox-studio-classic
+mgh3326/JSWebCrawler
+moeiscool/Shinobi
+muaz-khan/Chrome-Extensions
+nodecg/nodecg
+nodulusteam/-nodulus-zipem
+onap/ccsdk-distribution
+opencomponents/oc
+openhybrid/object
+OstlerDev/PopcornTV
+paypal/react-engine
+rap1ds/dippa
+rddill-IBM/ZeroToBlockchain
+ShinobiCCTV/Shinobi
+shy2850/node-server
+SkillsFundingAgency/das-frontend
+starikan/kartofan
+substance/io
+suculent/thinx-device-api
+Sv443/JokeAPI
+TheEvilCorp/OverReact
+thinx-cloud/thinx
+tocttou/choreograph-parent
+TrapTeamCCNZ/TTMS-DEPLOY
+#wdshare-group/wdshare-site
+#xesf/agrippa
+YiFeng0755/cloud-test
+zhufengnodejs/201602node
+zohooo/jekyde
+ztaom/liveomg
\ No newline at end of file
diff --git a/cpp/autobuilder/.gitignore b/cpp/autobuilder/.gitignore
new file mode 100644
index 000000000000..f81ecc73dffa
--- /dev/null
+++ b/cpp/autobuilder/.gitignore
@@ -0,0 +1,13 @@
+obj/
+TestResults/
+*.manifest
+*.pdb
+*.suo
+*.mdb
+*.vsmdi
+csharp.log
+**/bin/Debug
+**/bin/Release
+*.tlog
+.vs
+*.user
\ No newline at end of file
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs
new file mode 100644
index 000000000000..87390b7bf8fe
--- /dev/null
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/BuildScripts.cs
@@ -0,0 +1,296 @@
+īģŋusing Xunit;
+using Semmle.Autobuild.Shared;
+using System.Collections.Generic;
+using System;
+using System.Linq;
+using Microsoft.Build.Construction;
+using System.Xml;
+
+namespace Semmle.Autobuild.Cpp.Tests
+{
+ ///
+ /// Test class to script Autobuilder scenarios.
+ /// For most methods, it uses two fields:
+ /// - an IList to capture the the arguments passed to it
+ /// - an IDictionary of possible return values.
+ ///
+ class TestActions : IBuildActions
+ {
+ ///
+ /// List of strings passed to FileDelete.
+ ///
+ public IList FileDeleteIn = new List();
+
+ void IBuildActions.FileDelete(string file)
+ {
+ FileDeleteIn.Add(file);
+ }
+
+ public IList FileExistsIn = new List();
+ public IDictionary FileExists = new Dictionary();
+
+ bool IBuildActions.FileExists(string file)
+ {
+ FileExistsIn.Add(file);
+ if (FileExists.TryGetValue(file, out var ret))
+ return ret;
+ if (FileExists.TryGetValue(System.IO.Path.GetFileName(file), out ret))
+ return ret;
+ throw new ArgumentException("Missing FileExists " + file);
+ }
+
+ public IList RunProcessIn = new List();
+ public IDictionary RunProcess = new Dictionary();
+ public IDictionary RunProcessOut = new Dictionary();
+ public IDictionary RunProcessWorkingDirectory = new Dictionary();
+
+ int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? env, out IList stdOut)
+ {
+ var pattern = cmd + " " + args;
+ RunProcessIn.Add(pattern);
+ if (RunProcessOut.TryGetValue(pattern, out var str))
+ stdOut = str.Split("\n");
+ else
+ throw new ArgumentException("Missing RunProcessOut " + pattern);
+ RunProcessWorkingDirectory.TryGetValue(pattern, out var wd);
+ if (wd != workingDirectory)
+ throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern);
+ if (RunProcess.TryGetValue(pattern, out var ret))
+ return ret;
+ throw new ArgumentException("Missing RunProcess " + pattern);
+ }
+
+ int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary? env)
+ {
+ var pattern = cmd + " " + args;
+ RunProcessIn.Add(pattern);
+ RunProcessWorkingDirectory.TryGetValue(pattern, out var wd);
+ if (wd != workingDirectory)
+ throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern);
+ if (RunProcess.TryGetValue(pattern, out var ret))
+ return ret;
+ throw new ArgumentException("Missing RunProcess " + pattern);
+ }
+
+ public IList DirectoryDeleteIn = new List();
+
+ void IBuildActions.DirectoryDelete(string dir, bool recursive)
+ {
+ DirectoryDeleteIn.Add(dir);
+ }
+
+ public IDictionary DirectoryExists = new Dictionary();
+ public IList DirectoryExistsIn = new List();
+
+ bool IBuildActions.DirectoryExists(string dir)
+ {
+ DirectoryExistsIn.Add(dir);
+ if (DirectoryExists.TryGetValue(dir, out var ret))
+ return ret;
+ throw new ArgumentException("Missing DirectoryExists " + dir);
+ }
+
+ public IDictionary GetEnvironmentVariable = new Dictionary();
+
+ string? IBuildActions.GetEnvironmentVariable(string name)
+ {
+ if (GetEnvironmentVariable.TryGetValue(name, out var ret))
+ return ret;
+ throw new ArgumentException("Missing GetEnvironmentVariable " + name);
+ }
+
+ public string GetCurrentDirectory = "";
+
+ string IBuildActions.GetCurrentDirectory()
+ {
+ return GetCurrentDirectory;
+ }
+
+ public IDictionary EnumerateFiles = new Dictionary();
+
+ IEnumerable IBuildActions.EnumerateFiles(string dir)
+ {
+ if (EnumerateFiles.TryGetValue(dir, out var str))
+ return str.Split("\n");
+ throw new ArgumentException("Missing EnumerateFiles " + dir);
+ }
+
+ public IDictionary EnumerateDirectories = new Dictionary();
+
+ IEnumerable IBuildActions.EnumerateDirectories(string dir)
+ {
+ if (EnumerateDirectories.TryGetValue(dir, out var str))
+ return string.IsNullOrEmpty(str) ? Enumerable.Empty() : str.Split("\n");
+ throw new ArgumentException("Missing EnumerateDirectories " + dir);
+ }
+
+ public bool IsWindows;
+
+ bool IBuildActions.IsWindows() => IsWindows;
+
+ string IBuildActions.PathCombine(params string[] parts)
+ {
+ return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p)));
+ }
+
+ string IBuildActions.GetFullPath(string path) => path;
+
+ void IBuildActions.WriteAllText(string filename, string contents)
+ {
+ }
+
+ public IDictionary LoadXml = new Dictionary();
+ XmlDocument IBuildActions.LoadXml(string filename)
+ {
+ if (LoadXml.TryGetValue(filename, out var xml))
+ return xml;
+ throw new ArgumentException("Missing LoadXml " + filename);
+ }
+
+ public string EnvironmentExpandEnvironmentVariables(string s)
+ {
+ foreach (var kvp in GetEnvironmentVariable)
+ s = s.Replace($"%{kvp.Key}%", kvp.Value);
+ return s;
+ }
+ }
+
+ ///
+ /// A fake solution to build.
+ ///
+ class TestSolution : ISolution
+ {
+ public IEnumerable Configurations => throw new NotImplementedException();
+
+ public string DefaultConfigurationName => "Release";
+
+ public string DefaultPlatformName => "x86";
+
+ public string FullPath { get; set; }
+
+ public Version ToolsVersion => new Version("14.0");
+
+ public IEnumerable IncludedProjects => throw new NotImplementedException();
+
+ public TestSolution(string path)
+ {
+ FullPath = path;
+ }
+ }
+
+ public class BuildScriptTests
+ {
+ TestActions Actions = new TestActions();
+
+ // Records the arguments passed to StartCallback.
+ IList StartCallbackIn = new List();
+
+ void StartCallback(string s, bool silent)
+ {
+ StartCallbackIn.Add(s);
+ }
+
+ // Records the arguments passed to EndCallback
+ IList EndCallbackIn = new List();
+ IList EndCallbackReturn = new List();
+
+ void EndCallback(int ret, string s, bool silent)
+ {
+ EndCallbackReturn.Add(ret);
+ EndCallbackIn.Add(s);
+ }
+
+ CppAutobuilder CreateAutoBuilder(bool isWindows,
+ string? buildless = null, string? solution = null, string? buildCommand = null, string? ignoreErrors = null,
+ string? msBuildArguments = null, string? msBuildPlatform = null, string? msBuildConfiguration = null, string? msBuildTarget = null,
+ string? dotnetArguments = null, string? dotnetVersion = null, string? vsToolsVersion = null,
+ string? nugetRestore = null, string? allSolutions = null,
+ string cwd = @"C:\Project")
+ {
+ string codeqlUpperLanguage = Language.Cpp.UpperCaseName;
+ Actions.GetEnvironmentVariable[$"CODEQL_AUTOBUILDER_{codeqlUpperLanguage}_NO_INDEXING"] = "false";
+ Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_TRAP_DIR"] = "";
+ Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_SOURCE_ARCHIVE_DIR"] = "";
+ Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{codeqlUpperLanguage.ToLowerInvariant()}";
+ Actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java";
+ Actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa";
+ Actions.GetEnvironmentVariable["SEMMLE_JAVA_HOME"] = @"C:\odasa\tools\java";
+ Actions.GetEnvironmentVariable["SEMMLE_PLATFORM_TOOLS"] = @"C:\odasa\tools";
+ Actions.GetEnvironmentVariable["LGTM_INDEX_VSTOOLS_VERSION"] = vsToolsVersion;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_ARGUMENTS"] = msBuildArguments;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_PLATFORM"] = msBuildPlatform;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_CONFIGURATION"] = msBuildConfiguration;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_TARGET"] = msBuildTarget;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_ARGUMENTS"] = dotnetArguments;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_DOTNET_VERSION"] = dotnetVersion;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_BUILD_COMMAND"] = buildCommand;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_SOLUTION"] = solution;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_IGNORE_ERRORS"] = ignoreErrors;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_BUILDLESS"] = buildless;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_ALL_SOLUTIONS"] = allSolutions;
+ Actions.GetEnvironmentVariable["LGTM_INDEX_NUGET_RESTORE"] = nugetRestore;
+ Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = isWindows ? @"C:\Program Files (x86)" : null;
+ Actions.GetCurrentDirectory = cwd;
+ Actions.IsWindows = isWindows;
+
+ var options = new AutobuildOptions(Actions, Language.Cpp);
+ return new CppAutobuilder(Actions, options);
+ }
+
+ void TestAutobuilderScript(Autobuilder autobuilder, int expectedOutput, int commandsRun)
+ {
+ Assert.Equal(expectedOutput, autobuilder.GetBuildScript().Run(Actions, StartCallback, EndCallback));
+
+ // Check expected commands actually ran
+ Assert.Equal(commandsRun, StartCallbackIn.Count);
+ Assert.Equal(commandsRun, EndCallbackIn.Count);
+ Assert.Equal(commandsRun, EndCallbackReturn.Count);
+
+ var action = Actions.RunProcess.GetEnumerator();
+ for (int cmd = 0; cmd < commandsRun; ++cmd)
+ {
+ Assert.True(action.MoveNext());
+
+ Assert.Equal(action.Current.Key, StartCallbackIn[cmd]);
+ Assert.Equal(action.Current.Value, EndCallbackReturn[cmd]);
+ }
+ }
+
+
+ [Fact]
+ public void TestDefaultCppAutobuilder()
+ {
+ Actions.EnumerateFiles[@"C:\Project"] = "";
+ Actions.EnumerateDirectories[@"C:\Project"] = "";
+
+ var autobuilder = CreateAutoBuilder(true);
+ var script = autobuilder.GetBuildScript();
+
+ // Fails due to no solutions present.
+ Assert.NotEqual(0, script.Run(Actions, StartCallback, EndCallback));
+ }
+
+ [Fact]
+ public void TestCppAutobuilderSuccess()
+ {
+ Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore C:\Project\test.sln"] = 1;
+ Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && C:\odasa\tools\odasa index --auto msbuild C:\Project\test.sln /p:UseSharedCompilation=false /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"" /p:MvcBuildViews=true"] = 0;
+ Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
+ Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
+ Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;
+ Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = "";
+ Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = true;
+ Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true;
+ Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = true;
+ Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true;
+ Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = true;
+ Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.slx";
+ Actions.EnumerateDirectories[@"C:\Project"] = "";
+
+ var autobuilder = CreateAutoBuilder(true);
+ var solution = new TestSolution(@"C:\Project\test.sln");
+ autobuilder.ProjectsOrSolutionsToBuild.Add(solution);
+ TestAutobuilderScript(autobuilder, 0, 2);
+ }
+ }
+}
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj
new file mode 100644
index 000000000000..7de677b56100
--- /dev/null
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests/Semmle.Autobuild.Cpp.Tests.csproj
@@ -0,0 +1,25 @@
+
+
+
+ Exe
+ netcoreapp3.1
+ false
+ win-x64;linux-x64;osx-x64
+ enable
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
+
+
+
+
+
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs
new file mode 100644
index 000000000000..44c34656a2a8
--- /dev/null
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/CppAutobuilder.cs
@@ -0,0 +1,23 @@
+īģŋusing Semmle.Autobuild.Shared;
+
+namespace Semmle.Autobuild.Cpp
+{
+ public class CppAutobuilder : Autobuilder
+ {
+ public CppAutobuilder(IBuildActions actions, AutobuildOptions options) : base(actions, options) { }
+
+ public override BuildScript GetBuildScript()
+ {
+ if (Options.BuildCommand != null)
+ return new BuildCommandRule((_, f) => f(null)).Analyse(this, false);
+
+ return
+ // First try MSBuild
+ new MsBuildRule().Analyse(this, true) |
+ // Then look for a script that might be a build script
+ (() => new BuildCommandAutoRule((_, f) => f(null)).Analyse(this, true)) |
+ // All attempts failed: print message
+ AutobuildFailure();
+ }
+ }
+}
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs
new file mode 100644
index 000000000000..3f4627c53d54
--- /dev/null
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Program.cs
@@ -0,0 +1,33 @@
+īģŋusing System;
+using Semmle.Autobuild.Shared;
+
+namespace Semmle.Autobuild.Cpp
+{
+ class Program
+ {
+ static int Main()
+ {
+
+ try
+ {
+ var actions = SystemBuildActions.Instance;
+ var options = new AutobuildOptions(actions, Language.Cpp);
+ try
+ {
+ Console.WriteLine("CodeQL C++ autobuilder");
+ var builder = new CppAutobuilder(actions, options);
+ return builder.AttemptBuild();
+ }
+ catch(InvalidEnvironmentException ex)
+ {
+ Console.WriteLine("The environment is invalid: {0}", ex.Message);
+ }
+ }
+ catch (ArgumentOutOfRangeException ex)
+ {
+ Console.WriteLine("The value \"{0}\" for parameter \"{1}\" is invalid", ex.ActualValue, ex.ParamName);
+ }
+ return 1;
+ }
+ }
+}
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/Properties/AssemblyInfo.cs b/cpp/autobuilder/Semmle.Autobuild.Cpp/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000000..2d14b0e909d6
--- /dev/null
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Properties/AssemblyInfo.cs
@@ -0,0 +1,32 @@
+īģŋusing System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Semmle.Autobuild.Cpp")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("GitHub")]
+[assembly: AssemblyProduct("CodeQL autobuilder for C++")]
+[assembly: AssemblyCopyright("Copyright Š GitHub 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj
new file mode 100644
index 000000000000..aadcc07568df
--- /dev/null
+++ b/cpp/autobuilder/Semmle.Autobuild.Cpp/Semmle.Autobuild.Cpp.csproj
@@ -0,0 +1,28 @@
+
+
+
+ netcoreapp3.1
+ Semmle.Autobuild.Cpp
+ Semmle.Autobuild.Cpp
+
+ Exe
+
+ false
+ win-x64;linux-x64;osx-x64
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cpp/change-notes/2020-09-29-range-analysis-rollup.md b/cpp/change-notes/2020-09-29-range-analysis-rollup.md
new file mode 100644
index 000000000000..d5b2c729bcbc
--- /dev/null
+++ b/cpp/change-notes/2020-09-29-range-analysis-rollup.md
@@ -0,0 +1,14 @@
+lgtm,codescanning
+* The `SimpleRangeAnalysis` library has gained support for several language
+ constructs it did not support previously. These improvements primarily affect
+ the queries `cpp/constant-comparison`, `cpp/comparison-with-wider-type`, and
+ `cpp/integer-multiplication-cast-to-long`. The newly supported language
+ features are:
+ * Multiplication of unsigned numbers.
+ * Multiplication by a constant.
+ * Reference-typed function parameters.
+ * Comparing a variable not equal to an endpoint of its range, thus narrowing the range by one.
+ * Using `if (x)` or `if (!x)` or similar to test for equality to zero.
+* The `SimpleRangeAnalysis` library can now be extended with custom rules. See
+ examples in
+ `cpp/ql/src/experimental/semmle/code/cpp/rangeanalysis/extensions/`.
diff --git a/cpp/ql/examples/qlpack.yml b/cpp/ql/examples/qlpack.yml
new file mode 100644
index 000000000000..67289f8e360c
--- /dev/null
+++ b/cpp/ql/examples/qlpack.yml
@@ -0,0 +1,3 @@
+name: codeql-cpp-examples
+version: 0.0.0
+libraryPathDependencies: codeql-cpp
diff --git a/cpp/ql/examples/snippets/emptyblock.ql b/cpp/ql/examples/snippets/emptyblock.ql
index 2f1e198fc595..46ee35a68644 100644
--- a/cpp/ql/examples/snippets/emptyblock.ql
+++ b/cpp/ql/examples/snippets/emptyblock.ql
@@ -9,6 +9,6 @@
import cpp
-from Block blk
+from BlockStmt blk
where blk.getNumStmt() = 0
select blk
diff --git a/cpp/ql/examples/snippets/emptythen.ql b/cpp/ql/examples/snippets/emptythen.ql
index 0ae060838d26..7e31ac7e4e18 100644
--- a/cpp/ql/examples/snippets/emptythen.ql
+++ b/cpp/ql/examples/snippets/emptythen.ql
@@ -13,5 +13,5 @@
import cpp
from IfStmt i
-where i.getThen().(Block).getNumStmt() = 0
+where i.getThen().(BlockStmt).getNumStmt() = 0
select i
diff --git a/cpp/ql/examples/snippets/singletonblock.ql b/cpp/ql/examples/snippets/singletonblock.ql
index a265d84a0da3..265bc85927c8 100644
--- a/cpp/ql/examples/snippets/singletonblock.ql
+++ b/cpp/ql/examples/snippets/singletonblock.ql
@@ -8,6 +8,6 @@
import cpp
-from Block b
+from BlockStmt b
where b.getNumStmt() = 1
select b
diff --git a/cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql b/cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql
index cfa197109347..97481bc8b038 100644
--- a/cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql
+++ b/cpp/ql/src/Best Practices/BlockWithTooManyStatements.ql
@@ -14,7 +14,7 @@ import cpp
class ComplexStmt extends Stmt {
ComplexStmt() {
- exists(Block body |
+ exists(BlockStmt body |
body = this.(Loop).getStmt() or
body = this.(SwitchStmt).getStmt()
|
@@ -24,7 +24,7 @@ class ComplexStmt extends Stmt {
}
}
-from Block b, int n, ComplexStmt complexStmt
+from BlockStmt b, int n, ComplexStmt complexStmt
where
n = strictcount(ComplexStmt s | s = b.getAStmt()) and
n > 3 and
diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql
index 91317c53af85..64e5760ed2b4 100644
--- a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql
+++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesParameter.ql
@@ -11,6 +11,17 @@
import cpp
+/**
+ * Gets the template that a function `f` is constructed from, or just `f` if it
+ * is not from a template instantiation.
+ */
+Function getConstructedFrom(Function f) {
+ f.isConstructedFrom(result)
+ or
+ not f.isConstructedFrom(_) and
+ result = f
+}
+
/**
* Gets the parameter of `f` with name `name`, which has to come from the
* _definition_ of `f` and not a prototype declaration.
@@ -18,13 +29,17 @@ import cpp
* This should not happen in a single application but since we
* have a system wide view it is likely to happen for instance for
* the main function.
+ *
+ * Note: we use `getConstructedFrom` to ensure that we look at template
+ * functions rather than their instantiations. We get better results this way
+ * as the instantiation is artificial and may have inherited parameter names
+ * from the declaration rather than the definition.
*/
ParameterDeclarationEntry functionParameterNames(Function f, string name) {
exists(FunctionDeclarationEntry fe |
result.getFunctionDeclarationEntry() = fe and
- fe.getFunction() = f and
+ getConstructedFrom(f).getDefinition() = fe and
fe.getLocation() = f.getDefinitionLocation() and
- result.getFile() = fe.getFile() and // Work around CPP-331
strictcount(f.getDefinitionLocation()) = 1 and
result.getName() = name
)
diff --git a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql
index 675fd7178d8a..d10d346c5135 100644
--- a/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql
+++ b/cpp/ql/src/Best Practices/Hiding/DeclarationHidesVariable.ql
@@ -17,7 +17,7 @@ where
shadowing(lv1, lv2) and
not lv1.isCompilerGenerated() and
not lv2.isCompilerGenerated() and
- not lv1.getParentScope().(Block).isInMacroExpansion() and
- not lv2.getParentScope().(Block).isInMacroExpansion()
+ not lv1.getParentScope().(BlockStmt).isInMacroExpansion() and
+ not lv2.getParentScope().(BlockStmt).isInMacroExpansion()
select lv1, "Variable " + lv1.getName() + " hides another variable of the same name (on $@).", lv2,
"line " + lv2.getLocation().getStartLine().toString()
diff --git a/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql
index 9880a3ae2a0a..9fa8c4e5e3fb 100644
--- a/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql
+++ b/cpp/ql/src/Best Practices/Likely Errors/EmptyBlock.ql
@@ -14,7 +14,7 @@
import cpp
-predicate emptyBlock(ControlStructure s, Block b) {
+predicate emptyBlock(ControlStructure s, BlockStmt b) {
b = s.getAChild() and
not exists(b.getAChild()) and
not b.isInMacroExpansion() and
@@ -23,7 +23,7 @@ predicate emptyBlock(ControlStructure s, Block b) {
class AffectedFile extends File {
AffectedFile() {
- exists(Block b |
+ exists(BlockStmt b |
emptyBlock(_, b) and
this = b.getFile()
)
@@ -37,7 +37,7 @@ class AffectedFile extends File {
class BlockOrNonChild extends Element {
BlockOrNonChild() {
(
- this instanceof Block
+ this instanceof BlockStmt
or
this instanceof Comment
or
@@ -78,7 +78,7 @@ class BlockOrNonChild extends Element {
/**
* A block that contains a non-child element.
*/
-predicate emptyBlockContainsNonchild(Block b) {
+predicate emptyBlockContainsNonchild(BlockStmt b) {
emptyBlock(_, b) and
exists(BlockOrNonChild c, AffectedFile file |
c.(BlockOrNonChild).getStartRankIn(file) = 1 + b.(BlockOrNonChild).getStartRankIn(file) and
@@ -91,7 +91,7 @@ predicate emptyBlockContainsNonchild(Block b) {
* A block that is entirely on one line, which also contains a comment. Chances
* are the comment is intended to refer to the block.
*/
-predicate lineComment(Block b) {
+predicate lineComment(BlockStmt b) {
emptyBlock(_, b) and
exists(Location bLocation, File f, int line |
bLocation = b.getLocation() and
@@ -106,7 +106,7 @@ predicate lineComment(Block b) {
)
}
-from ControlStructure s, Block eb
+from ControlStructure s, BlockStmt eb
where
emptyBlock(s, eb) and
not emptyBlockContainsNonchild(eb) and
diff --git a/cpp/ql/src/Critical/DeadCodeGoto.ql b/cpp/ql/src/Critical/DeadCodeGoto.ql
index 4e16325f943d..a2f8db771b07 100644
--- a/cpp/ql/src/Critical/DeadCodeGoto.ql
+++ b/cpp/ql/src/Critical/DeadCodeGoto.ql
@@ -12,7 +12,7 @@
import cpp
import semmle.code.cpp.commons.Exclusions
-Stmt getNextRealStmt(Block b, int i) {
+Stmt getNextRealStmt(BlockStmt b, int i) {
result = b.getStmt(i + 1) and
not result instanceof EmptyStmt
or
@@ -20,7 +20,7 @@ Stmt getNextRealStmt(Block b, int i) {
result = getNextRealStmt(b, i + 1)
}
-from JumpStmt js, Block b, int i, Stmt s
+from JumpStmt js, BlockStmt b, int i, Stmt s
where
b.getStmt(i) = js and
s = getNextRealStmt(b, i) and
diff --git a/cpp/ql/src/Critical/OverflowDestination.ql b/cpp/ql/src/Critical/OverflowDestination.ql
index ad925daed628..bff3cac9326c 100644
--- a/cpp/ql/src/Critical/OverflowDestination.ql
+++ b/cpp/ql/src/Critical/OverflowDestination.ql
@@ -23,10 +23,7 @@ import semmle.code.cpp.security.TaintTracking
* ```
*/
predicate sourceSized(FunctionCall fc, Expr src) {
- exists(string name |
- (name = "strncpy" or name = "strncat" or name = "memcpy" or name = "memmove") and
- fc.getTarget().hasGlobalOrStdName(name)
- ) and
+ fc.getTarget().hasGlobalOrStdName(["strncpy", "strncat", "memcpy", "memmove"]) and
exists(Expr dest, Expr size, Variable v |
fc.getArgument(0) = dest and
fc.getArgument(1) = src and
diff --git a/cpp/ql/src/Critical/SizeCheck2.ql b/cpp/ql/src/Critical/SizeCheck2.ql
index 1b716d79d49e..1b7e1347c308 100644
--- a/cpp/ql/src/Critical/SizeCheck2.ql
+++ b/cpp/ql/src/Critical/SizeCheck2.ql
@@ -15,12 +15,7 @@
import cpp
class Allocation extends FunctionCall {
- Allocation() {
- exists(string name |
- this.getTarget().hasGlobalOrStdName(name) and
- (name = "malloc" or name = "calloc" or name = "realloc")
- )
- }
+ Allocation() { this.getTarget().hasGlobalOrStdName(["malloc", "calloc", "realloc"]) }
private string getName() { this.getTarget().hasGlobalOrStdName(result) }
diff --git a/cpp/ql/src/Critical/aliasAnalysisWarning.qhelp b/cpp/ql/src/Critical/aliasAnalysisWarning.qhelp
new file mode 100644
index 000000000000..77d395c7da53
--- /dev/null
+++ b/cpp/ql/src/Critical/aliasAnalysisWarning.qhelp
@@ -0,0 +1,11 @@
+
+
+
+
+This check is an approximation, so some results may not be actual defects in the program.
+It is not possible in general to compute the exact value of the variable without running the program with all possible input data.
+
+
+
diff --git a/cpp/ql/src/Critical/callGraphWarning.qhelp b/cpp/ql/src/Critical/callGraphWarning.qhelp
new file mode 100644
index 000000000000..1e408db15270
--- /dev/null
+++ b/cpp/ql/src/Critical/callGraphWarning.qhelp
@@ -0,0 +1,12 @@
+
+
+
+
+This check is an approximation, so some results may not be actual defects in the program.
+It is not possible in general to compute which function is actually called in a virtual call,
+or a call through a pointer, without running the program with all possible input data.
+
+
+
diff --git a/cpp/ql/src/Critical/dataFlowWarning.qhelp b/cpp/ql/src/Critical/dataFlowWarning.qhelp
new file mode 100644
index 000000000000..f96c82226c21
--- /dev/null
+++ b/cpp/ql/src/Critical/dataFlowWarning.qhelp
@@ -0,0 +1,13 @@
+
+
+
+
+This check is an approximation, so some results may not be actual defects in the program.
+It is not possible in general to compute the actual branch taken in conditional statements such
+as "if" without running the program with all possible input data. This means that it is not possible
+to determine if a particular statement is going to be executed.
+
+
+
diff --git a/cpp/ql/src/Critical/pointsToWarning.qhelp b/cpp/ql/src/Critical/pointsToWarning.qhelp
new file mode 100644
index 000000000000..2fa777ebfe97
--- /dev/null
+++ b/cpp/ql/src/Critical/pointsToWarning.qhelp
@@ -0,0 +1,11 @@
+
+
+
+
+This check is an approximation, so some results may not be actual defects in the program. It is not possible
+in general to compute the values of pointers without running the program with all input data.
+
+
+
diff --git a/cpp/ql/src/Documentation/CommentedOutCode.qhelp b/cpp/ql/src/Documentation/CommentedOutCode.qhelp
index 4ce0ee029b62..ba056ab73f77 100644
--- a/cpp/ql/src/Documentation/CommentedOutCode.qhelp
+++ b/cpp/ql/src/Documentation/CommentedOutCode.qhelp
@@ -3,5 +3,5 @@
"qhelp.dtd">
-
+
diff --git a/cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp b/cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp
new file mode 100644
index 000000000000..eb40ecdb7088
--- /dev/null
+++ b/cpp/ql/src/Documentation/CommentedOutCodeQuery.qhelp
@@ -0,0 +1,25 @@
+
+
+
+
+
+Commented-out code is distracting and confusing for developers who read the surrounding code,
+and its significance is often unclear. It will not get compiled or tested when the code around
+it changes, so it's likely to break over time. For these reasons, commented-out code should be
+avoided.
+
+
+
+
+
+
+
+Remove or reinstate the commented-out code. If you want to include a snippet of example code
+in a comment, consider enclosing it in quotes or marking it up as appropriate for the source
+language.
+
+
+
+
diff --git a/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowJmp.ql b/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowJmp.ql
index 7468b6e71748..0623036a5924 100644
--- a/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowJmp.ql
+++ b/cpp/ql/src/JPL_C/LOC-2/Rule 11/SimpleControlFlowJmp.ql
@@ -13,14 +13,7 @@
import cpp
class ForbiddenFunction extends Function {
- ForbiddenFunction() {
- exists(string name | name = this.getName() |
- name = "setjmp" or
- name = "longjmp" or
- name = "sigsetjmp" or
- name = "siglongjmp"
- )
- }
+ ForbiddenFunction() { this.getName() = ["setjmp", "longjmp", "sigsetjmp", "siglongjmp"] }
}
from FunctionCall call
diff --git a/cpp/ql/src/JPL_C/LOC-4/Rule 21/MacroInBlock.ql b/cpp/ql/src/JPL_C/LOC-4/Rule 21/MacroInBlock.ql
index 602be971763b..9ed5675e7955 100644
--- a/cpp/ql/src/JPL_C/LOC-4/Rule 21/MacroInBlock.ql
+++ b/cpp/ql/src/JPL_C/LOC-4/Rule 21/MacroInBlock.ql
@@ -12,7 +12,7 @@
import cpp
int lineInBlock(File f) {
- exists(Block block, Location blockLocation |
+ exists(BlockStmt block, Location blockLocation |
block.getFile() = f and blockLocation = block.getLocation()
|
result in [blockLocation.getStartLine() .. blockLocation.getEndLine()]
diff --git a/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.ql b/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.ql
index 31af5b3ce794..ca36e6d1ce2e 100644
--- a/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.ql
+++ b/cpp/ql/src/Likely Bugs/Leap Year/UncheckedReturnValueForTimeFunctions.ql
@@ -40,9 +40,7 @@ class DateStructModifiedFieldAccess extends LeapYearFieldAccess {
*/
class SafeTimeGatheringFunction extends Function {
SafeTimeGatheringFunction() {
- this.getQualifiedName() = "GetFileTime" or
- this.getQualifiedName() = "GetSystemTime" or
- this.getQualifiedName() = "NtQuerySystemTime"
+ this.getQualifiedName() = ["GetFileTime", "GetSystemTime", "NtQuerySystemTime"]
}
}
@@ -51,15 +49,11 @@ class SafeTimeGatheringFunction extends Function {
*/
class TimeConversionFunction extends Function {
TimeConversionFunction() {
- this.getQualifiedName() = "FileTimeToSystemTime" or
- this.getQualifiedName() = "SystemTimeToFileTime" or
- this.getQualifiedName() = "SystemTimeToTzSpecificLocalTime" or
- this.getQualifiedName() = "SystemTimeToTzSpecificLocalTimeEx" or
- this.getQualifiedName() = "TzSpecificLocalTimeToSystemTime" or
- this.getQualifiedName() = "TzSpecificLocalTimeToSystemTimeEx" or
- this.getQualifiedName() = "RtlLocalTimeToSystemTime" or
- this.getQualifiedName() = "RtlTimeToSecondsSince1970" or
- this.getQualifiedName() = "_mkgmtime"
+ this.getQualifiedName() =
+ ["FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime",
+ "SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
+ "TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
+ "RtlTimeToSecondsSince1970", "_mkgmtime"]
}
}
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.ql b/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.ql
index 4f23ed50b365..bf724de3d70c 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.ql
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/FutileConditional.ql
@@ -27,11 +27,11 @@ predicate macroUseLocation(File f, int start, int end) {
}
pragma[noopt]
-predicate emptyIf(IfStmt s, Block b, File f, int start, int end) {
+predicate emptyIf(IfStmt s, BlockStmt b, File f, int start, int end) {
s instanceof IfStmt and
not exists(s.getElse()) and
b = s.getThen() and
- b instanceof Block and
+ b instanceof BlockStmt and
not exists(b.getAChild()) and
f = b.getFile() and
exists(Location l |
@@ -42,7 +42,7 @@ predicate emptyIf(IfStmt s, Block b, File f, int start, int end) {
}
pragma[noopt]
-predicate query(IfStmt s, Block b) {
+predicate query(IfStmt s, BlockStmt b) {
exists(File f, int blockStart, int blockEnd |
emptyIf(s, b, f, blockStart, blockEnd) and
not exists(int macroStart, int macroEnd |
@@ -53,7 +53,7 @@ predicate query(IfStmt s, Block b) {
)
}
-from IfStmt s, Block b
+from IfStmt s, BlockStmt b
where
query(s, b) and
not b.isInMacroExpansion()
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql
index b53cb1d50b32..074c82bc03b1 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql
@@ -15,6 +15,15 @@ import cpp
import semmle.code.cpp.models.implementations.Strcpy
import semmle.code.cpp.dataflow.DataFlow
+/**
+ * A string copy function that returns a string, rather than an error code (for
+ * example, `strcpy` returns a string, whereas `strcpy_s` returns an error
+ * code).
+ */
+class InterestingStrcpyFunction extends StrcpyFunction {
+ InterestingStrcpyFunction() { getType().getUnspecifiedType() instanceof PointerType }
+}
+
predicate isBoolean(Expr e1) {
exists(Type t1 |
t1 = e1.getType() and
@@ -25,12 +34,12 @@ predicate isBoolean(Expr e1) {
predicate isStringCopyCastedAsBoolean(FunctionCall func, Expr expr1, string msg) {
DataFlow::localExprFlow(func, expr1) and
isBoolean(expr1.getConversion*()) and
- func.getTarget() instanceof StrcpyFunction and
+ func.getTarget() instanceof InterestingStrcpyFunction and
msg = "Return value of " + func.getTarget().getName() + " used as a Boolean."
}
predicate isStringCopyUsedInLogicalOperationOrCondition(FunctionCall func, Expr expr1, string msg) {
- func.getTarget() instanceof StrcpyFunction and
+ func.getTarget() instanceof InterestingStrcpyFunction and
(
(
// it is being used in an equality or logical operation
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql b/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql
index aaf52883e059..5e3af3478214 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql
@@ -50,7 +50,12 @@ predicate illDefinedDecrForStmt(
DataFlow::localFlowStep(DataFlow::exprNode(initialCondition), DataFlow::exprNode(lesserOperand)) and
// `initialCondition` < `terminalCondition`
(
- upperBound(initialCondition) < lowerBound(terminalCondition)
+ upperBound(initialCondition) < lowerBound(terminalCondition) and
+ (
+ // exclude cases where the loop counter is `unsigned` (where wrapping behaviour can be used deliberately)
+ v.getUnspecifiedType().(IntegralType).isSigned() or
+ initialCondition.getValue().toInt() = 0
+ )
or
(forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue())
)
diff --git a/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql b/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql
index 6ba835acbdc8..65ba665dff29 100644
--- a/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql
+++ b/cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql
@@ -23,7 +23,7 @@ import semmle.code.cpp.ir.ValueNumbering
class NullInstruction extends ConstantValueInstruction {
NullInstruction() {
this.getValue() = "0" and
- this.getResultType().getUnspecifiedType() instanceof PointerType
+ this.getResultIRType() instanceof IRAddressType
}
}
@@ -44,8 +44,8 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
bool =
any(ConvertInstruction convert |
checked = convert.getUnary() and
- convert.getResultType() instanceof BoolType and
- checked.getResultType() instanceof PointerType
+ convert.getResultIRType() instanceof IRBooleanType and
+ checked.getResultIRType() instanceof IRAddressType
)
}
diff --git a/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.ql b/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.ql
index 7062911ca4df..47ddb9d1f1e6 100644
--- a/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.ql
+++ b/cpp/ql/src/Metrics/Dependencies/ExternalDependencies.ql
@@ -1,4 +1,5 @@
/**
+ * @deprecated
* @name External dependencies
* @description Count the number of dependencies a C/C++ source file has on external libraries.
* @kind treemap
diff --git a/cpp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql b/cpp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql
index f2c110a0ac19..cf305ab0aeaf 100644
--- a/cpp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql
+++ b/cpp/ql/src/Metrics/Dependencies/ExternalDependenciesSourceLinks.ql
@@ -1,4 +1,5 @@
/**
+ * @deprecated
* @name External dependency source links
* @kind source-link
* @metricType externalDependency
diff --git a/cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp b/cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp
new file mode 100644
index 000000000000..217b6d175295
--- /dev/null
+++ b/cpp/ql/src/Metrics/Files/CommentedOutCodeMetricOverview.qhelp
@@ -0,0 +1,12 @@
+
+
+
+
+This metric counts the number of lines of commented-out code in each file. Large amounts of
+commented-out code often indicate poorly maintained code.
+
+
+
+
diff --git a/cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp b/cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp
new file mode 100644
index 000000000000..462eb7795f91
--- /dev/null
+++ b/cpp/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp
@@ -0,0 +1,12 @@
+
+
+
+
+Mark Needham: The danger of commenting out code.
+Los Techies: Commented Code == Technical Debt.
+High Integrity C++ Coding Standard: 2.3.2 Do not comment out code.
+
+
+
diff --git a/cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp b/cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp
new file mode 100644
index 000000000000..54397da6c994
--- /dev/null
+++ b/cpp/ql/src/Metrics/Files/DuplicationProblems.qhelp
@@ -0,0 +1,16 @@
+
+
+
+
+Duplicated code increases overall code size, making the code base
+harder to maintain and harder to understand. It also becomes harder to fix bugs,
+since a programmer applying a fix to one copy has to always remember to update
+other copies accordingly. Finally, code duplication is generally an indication of
+a poorly designed or hastily written code base, which typically suffers from other
+problems as well.
+
+
+
+
diff --git a/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql
index 3a11eb3575a1..ab9317ea316e 100644
--- a/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql
+++ b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCode.ql
@@ -1,4 +1,5 @@
/**
+ * @deprecated
* @name Duplicated lines in files
* @description The number of lines in a file, including code, comment
* and whitespace lines, which are duplicated in at least
diff --git a/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp
new file mode 100644
index 000000000000..17171fb75871
--- /dev/null
+++ b/cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.qhelp
@@ -0,0 +1,35 @@
+
+
+
+
+
+This metric measures the number of lines in a file that are contained within a block that is duplicated elsewhere. These lines may include code, comments and whitespace, and the duplicate block may be in this file or in another file.
+
+
+
+A file that contains many lines that are duplicated within the code base is problematic
+for a number of reasons.
+
+
+
+
+
+
+
+
+Refactor files with lots of duplicated code to extract the common code into
+a shared library or module.
+
+
+
+
+
+
+Wikipedia: Duplicate code.
+M. Fowler, Refactoring. Addison-Wesley, 1999.
+
+
+
+
diff --git a/cpp/ql/src/Metrics/Files/FNumberOfTests.ql b/cpp/ql/src/Metrics/Files/FNumberOfTests.ql
index a474b9862037..421fc2baba1f 100644
--- a/cpp/ql/src/Metrics/Files/FNumberOfTests.ql
+++ b/cpp/ql/src/Metrics/Files/FNumberOfTests.ql
@@ -18,6 +18,9 @@ Expr getTest() {
or
// boost tests; http://www.boost.org/
result.(FunctionCall).getTarget().hasQualifiedName("boost::unit_test", "make_test_case")
+ or
+ // googletest tests; https://github.com/google/googletest/
+ result.(FunctionCall).getTarget().hasQualifiedName("testing::internal", "MakeAndRegisterTestInfo")
}
from File f, int n
diff --git a/cpp/ql/src/Microsoft/SAL.qll b/cpp/ql/src/Microsoft/SAL.qll
index 04ea9a9c5fa8..46fedbb5d80c 100644
--- a/cpp/ql/src/Microsoft/SAL.qll
+++ b/cpp/ql/src/Microsoft/SAL.qll
@@ -1,14 +1,17 @@
+/**
+ * Provides classes for identifying and reasoning about Microsoft source code
+ * annotation language (SAL) macros.
+ */
+
import cpp
+/**
+ * A SAL macro defined in `sal.h` or a similar header file.
+ */
class SALMacro extends Macro {
SALMacro() {
- exists(string filename | filename = this.getFile().getBaseName() |
- filename = "sal.h" or
- filename = "specstrings_strict.h" or
- filename = "specstrings.h" or
- filename = "w32p.h" or
- filename = "minwindef.h"
- ) and
+ this.getFile().getBaseName() =
+ ["sal.h", "specstrings_strict.h", "specstrings.h", "w32p.h", "minwindef.h"] and
(
// Dialect for Windows 8 and above
this.getName().matches("\\_%\\_")
@@ -20,36 +23,44 @@ class SALMacro extends Macro {
}
pragma[noinline]
-predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
+private predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
+/**
+ * An invocation of a SAL macro (excluding invocations inside other macros).
+ */
class SALAnnotation extends MacroInvocation {
SALAnnotation() {
this.getMacro() instanceof SALMacro and
isTopLevelMacroAccess(this)
}
- /** Returns the `Declaration` annotated by `this`. */
+ /** Gets the `Declaration` annotated by `this`. */
Declaration getDeclaration() {
annotatesAt(this, result.getADeclarationEntry(), _, _) and
not result instanceof Type // exclude typedefs
}
- /** Returns the `DeclarationEntry` annotated by `this`. */
+ /** Gets the `DeclarationEntry` annotated by `this`. */
DeclarationEntry getDeclarationEntry() {
annotatesAt(this, result, _, _) and
not result instanceof TypeDeclarationEntry // exclude typedefs
}
}
+/**
+ * A SAL macro indicating that the return value of a function should always be
+ * checked.
+ */
class SALCheckReturn extends SALAnnotation {
SALCheckReturn() {
- exists(SALMacro m | m = this.getMacro() |
- m.getName() = "_Check_return_" or
- m.getName() = "_Must_inspect_result_"
- )
+ this.getMacro().(SALMacro).getName() = ["_Check_return_", "_Must_inspect_result_"]
}
}
+/**
+ * A SAL macro indicating that a pointer variable or return value should not be
+ * `NULL`.
+ */
class SALNotNull extends SALAnnotation {
SALNotNull() {
exists(SALMacro m | m = this.getMacro() |
@@ -69,6 +80,9 @@ class SALNotNull extends SALAnnotation {
}
}
+/**
+ * A SAL macro indicating that a value may be `NULL`.
+ */
class SALMaybeNull extends SALAnnotation {
SALMaybeNull() {
exists(SALMacro m | m = this.getMacro() |
@@ -79,13 +93,29 @@ class SALMaybeNull extends SALAnnotation {
}
}
+/**
+ * A parameter annotated by one or more SAL annotations.
+ */
+class SALParameter extends Parameter {
+ /** One of this parameter's annotations. */
+ SALAnnotation a;
+
+ SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
+
+ predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
+
+ predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") }
+
+ predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Implementation details
/**
* Holds if `a` annotates the declaration entry `d` and
* its start position is the `idx`th position in `file` that holds a SAL element.
*/
-predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
+private predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
annotatesAtPosition(a.(SALElement).getStartPosition(), d, file, idx)
}
@@ -109,22 +139,6 @@ private predicate annotatesAtPosition(SALPosition pos, DeclarationEntry d, File
)
}
-/**
- * A parameter annotated by one or more SAL annotations.
- */
-class SALParameter extends Parameter {
- /** One of this parameter's annotations. */
- SALAnnotation a;
-
- SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
-
- predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
-
- predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") }
-
- predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
-}
-
/**
* A SAL element, that is, a SAL annotation or a declaration entry
* that may have SAL annotations.
diff --git a/cpp/ql/src/Power of 10/Rule 4/FunctionTooLong.ql b/cpp/ql/src/Power of 10/Rule 4/FunctionTooLong.ql
index 2b26a8356830..701d56e6ce24 100644
--- a/cpp/ql/src/Power of 10/Rule 4/FunctionTooLong.ql
+++ b/cpp/ql/src/Power of 10/Rule 4/FunctionTooLong.ql
@@ -27,7 +27,7 @@ int logicalLength(FunctionDeclarationEntry f) {
count(Stmt s |
s.getEnclosingFunction() = f.getFunction() and
s.getFile() = f.getFile() and
- not s instanceof Block and
+ not s instanceof BlockStmt and
not s instanceof EmptyStmt and
not exists(ForStmt for | s = for.getInitialization()) and
not s.isAffectedByMacro()
diff --git a/cpp/ql/src/Power of 10/Rule 4/OneStmtPerLine.ql b/cpp/ql/src/Power of 10/Rule 4/OneStmtPerLine.ql
index c3936e1ed89d..7c66cdd41f40 100644
--- a/cpp/ql/src/Power of 10/Rule 4/OneStmtPerLine.ql
+++ b/cpp/ql/src/Power of 10/Rule 4/OneStmtPerLine.ql
@@ -14,7 +14,7 @@ import cpp
class OneLineStmt extends Stmt {
OneLineStmt() {
this.getLocation().getStartLine() = this.getLocation().getEndLine() and
- not this instanceof Block and
+ not this instanceof BlockStmt and
not exists(ForStmt for | this = for.getInitialization()) and
(
// Either this statement is not touched by a macro at all...
diff --git a/cpp/ql/src/Power of 10/Rule 5/AssertionDensity.ql b/cpp/ql/src/Power of 10/Rule 5/AssertionDensity.ql
index 1fbbaf99b58d..3bb926b8a649 100644
--- a/cpp/ql/src/Power of 10/Rule 5/AssertionDensity.ql
+++ b/cpp/ql/src/Power of 10/Rule 5/AssertionDensity.ql
@@ -27,7 +27,7 @@ int logicalLength(FunctionDeclarationEntry f) {
count(Stmt s |
s.getEnclosingFunction() = f.getFunction() and
s.getFile() = f.getFile() and
- not s instanceof Block and
+ not s instanceof BlockStmt and
not s instanceof EmptyStmt and
not exists(ForStmt for | s = for.getInitialization()) and
not s.isAffectedByMacro()
diff --git a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp
index d7432369f0e8..eba2ede58f52 100644
--- a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp
+++ b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.qhelp
@@ -39,7 +39,7 @@ access all the system's passwords.
-
+
This query highlights calls to the standard library functions abort, exit, getenv
and system
.
diff --git a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql
index d78bc0ee2a70..dedd5658bdf2 100644
--- a/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql
+++ b/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql
@@ -18,6 +18,7 @@ from Include i, File f, string extension
where
f = i.getIncludedFile() and
extension = f.getExtension().toLowerCase() and
+ extension != "inc" and
extension != "inl" and
extension != "tcc" and
extension != "tpp" and
diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 53.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 53.ql
index db8ad52e1f3c..a01e0c16bd4e 100644
--- a/cpp/ql/src/jsf/4.09 Style/AV Rule 53.ql
+++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 53.ql
@@ -18,6 +18,6 @@ import cpp
from File f
where
- (f.getExtension().toLowerCase() = "h" or f.getExtension().toLowerCase() = "hpp") and
+ f.getExtension().toLowerCase() = ["h", "hpp"] and
f.getExtension() != "h"
select f, "AV Rule 53: Header files will always have a file name extension of .h."
diff --git a/cpp/ql/src/jsf/4.09 Style/AV Rule 59.ql b/cpp/ql/src/jsf/4.09 Style/AV Rule 59.ql
index 2a5681093c7b..63e318125dc6 100644
--- a/cpp/ql/src/jsf/4.09 Style/AV Rule 59.ql
+++ b/cpp/ql/src/jsf/4.09 Style/AV Rule 59.ql
@@ -13,7 +13,7 @@ import cpp
from Stmt parent, Stmt child
where
- not child instanceof Block and
+ not child instanceof BlockStmt and
(
child = parent.(IfStmt).getThen()
or
diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql
index 02220f3d4d3a..67559557c3cc 100644
--- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql
+++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 82.ql
@@ -45,6 +45,16 @@ predicate dereferenceThis(Expr e) {
or
// `*this = ...` (where `=` is not overloaded, so an `AssignExpr`)
dereferenceThis(e.(AssignExpr).getLValue())
+ or
+ // `e ? ... : ... `
+ exists(ConditionalExpr cond |
+ cond = e and
+ dereferenceThis(cond.getThen()) and
+ dereferenceThis(cond.getElse())
+ )
+ or
+ // `..., ... `
+ dereferenceThis(e.(CommaExpr).getRightOperand())
}
/**
diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp
index 61275fedefab..69cc5322ad5f 100644
--- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp
+++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.qhelp
@@ -7,7 +7,7 @@
-
+
This query ensures that all operators with opposites (e.g. == and !=) are both defined, and
diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql
index dcfaf06f49ba..26a56faab5c0 100644
--- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql
+++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 85.ql
@@ -28,7 +28,7 @@ predicate oppositeOperators(string op1, string op2) {
* `!op2(_, _)`.
*/
predicate implementedAsNegationOf(Operator op1, Operator op2) {
- exists(Block b, ReturnStmt r, NotExpr n, Expr o |
+ exists(BlockStmt b, ReturnStmt r, NotExpr n, Expr o |
b = op1.getBlock() and
b.getNumStmt() = 1 and
r = b.getStmt(0) and
diff --git a/cpp/ql/src/jsf/4.12 Templates/AV Rule 104.ql b/cpp/ql/src/jsf/4.12 Templates/AV Rule 104.ql
index bc1ebf8c1d91..0a608f1dbe9c 100644
--- a/cpp/ql/src/jsf/4.12 Templates/AV Rule 104.ql
+++ b/cpp/ql/src/jsf/4.12 Templates/AV Rule 104.ql
@@ -21,8 +21,8 @@ import cpp
*/
class WarningLateTemplateSpecialization extends CompilerWarning {
WarningLateTemplateSpecialization() {
- this.getTag() = "partial_spec_after_instantiation" or
- this.getTag() = "partial_spec_after_instantiation_ambiguous"
+ this.getTag() =
+ ["partial_spec_after_instantiation", "partial_spec_after_instantiation_ambiguous"]
}
}
diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp
index 7dbbb12dba31..614fe4711fd1 100644
--- a/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp
+++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 111.qhelp
@@ -7,7 +7,7 @@
-
+
This query highlights return statements that return pointers to an object allocated on the stack. The lifetime
@@ -18,7 +18,7 @@ memory after the function has already returned will have undefined results.
-
+
diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp
index e47368f5d664..086d1ea1fb60 100644
--- a/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp
+++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.qhelp
@@ -12,7 +12,7 @@ calling convention for x86, it would be whatever value was in the AX/EAX registe
assuming the function had a non-float return type that can fit in a machine word.
-
+
diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp
index 6729583ff772..d67d84c5e064 100644
--- a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp
+++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.qhelp
@@ -7,7 +7,7 @@
-
+
This query highlights identifiers in an inner scope that hide (have the same name as) an identifier in an outer scope.
diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql
index 0b3572af97e5..d3e23c9a7f6b 100644
--- a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql
+++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 135.ql
@@ -29,7 +29,7 @@ predicate localShadowsParameter(LocalVariable lv, Parameter p) {
from Variable v, Variable shadowed
where
- not v.getParentScope().(Block).isInMacroExpansion() and
+ not v.getParentScope().(BlockStmt).isInMacroExpansion() and
(
v.(LocalVariableOrParameter).shadowsGlobal(shadowed.(GlobalVariable)) or
localShadowsParameter(v, shadowed) or
diff --git a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp
index e184b9edd6f7..73e66f0b0067 100644
--- a/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp
+++ b/cpp/ql/src/jsf/4.15 Declarations and Definitions/AV Rule 140.qhelp
@@ -7,7 +7,7 @@
-
+
This query highlights variables with the register
storage class specifier. Modern compilers are now capable of
diff --git a/cpp/ql/src/jsf/4.16 Initialization/AV Rule 143.ql b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 143.ql
index 973cb0165795..1f7fe79fd77b 100644
--- a/cpp/ql/src/jsf/4.16 Initialization/AV Rule 143.ql
+++ b/cpp/ql/src/jsf/4.16 Initialization/AV Rule 143.ql
@@ -38,7 +38,7 @@ predicate noDefUsePath(LocalVariable lv, ControlFlowNode n) {
}
predicate neighbouringStmts(Stmt s1, Stmt s2) {
- exists(Block b, int i |
+ exists(BlockStmt b, int i |
i in [0 .. b.getNumStmt() - 2] and
s1 = b.getStmt(i) and
s2 = b.getStmt(i + 1)
diff --git a/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp
index 75c4f03e9800..1633cb2a8674 100644
--- a/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp
+++ b/cpp/ql/src/jsf/4.17 Types/AV Rule 147.qhelp
@@ -7,7 +7,7 @@
-
+
This query highlights portions of code that can expose the floating point implementation of the underlying
diff --git a/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp
index 9c6c369cfed9..b30e828428c9 100644
--- a/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp
+++ b/cpp/ql/src/jsf/4.18 Constants/AV Rule 151.1.qhelp
@@ -7,7 +7,7 @@
-
+
This query highlights string literals that are assigned to a non-const
variable. String literals
diff --git a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp
index 3c502a249285..946e0873ab0d 100644
--- a/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp
+++ b/cpp/ql/src/jsf/4.20 Unions and Bit Fields/AV Rule 154.qhelp
@@ -7,7 +7,7 @@
-
+
This query finds bit fields with members that are not explicitly declared to be unsigned.
diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp
index 5961991fcdc1..1beb8b94f35b 100644
--- a/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp
+++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 165.qhelp
@@ -7,7 +7,7 @@
-
+
This query finds unsigned values that are being negated. Behavior is undefined in such cases.
diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 187.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 187.ql
index 7a8919faf896..5893f3f35a39 100644
--- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 187.ql
+++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 187.ql
@@ -22,6 +22,6 @@ where
not s instanceof ControlStructure and
// Exclude blocks; if a child of the block violates the rule that will still
// be picked up so there is no point in blaming the block as well
- not s instanceof Block and
+ not s instanceof BlockStmt and
s.isPure()
select s, "AV Rule 187: All non-null statements shall potentially have a side-effect."
diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp
index e65f5b8dfb42..85db1463d56b 100644
--- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp
+++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 189.qhelp
@@ -6,7 +6,7 @@
-
+
Use of goto statements makes code more difficult to understand and maintain. Consequently, the use
of goto statements is deprecated except as a mechanism for breaking out of multiple nested loops.
diff --git a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 191.ql b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 191.ql
index 579a615c27fd..2e730b666549 100644
--- a/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 191.ql
+++ b/cpp/ql/src/jsf/4.24 Control Flow Structures/AV Rule 191.ql
@@ -18,7 +18,7 @@ import cpp
// whether t is the last statement of s, possibly peeling off blocks
predicate isTerminatingStmt(Stmt s, Stmt t) {
- s = t or isTerminatingStmt(s.(Block).getLastStmt(), t)
+ s = t or isTerminatingStmt(s.(BlockStmt).getLastStmt(), t)
}
from BreakStmt s
diff --git a/cpp/ql/src/jsf/jsfNote.qhelp b/cpp/ql/src/jsf/jsfNote.qhelp
new file mode 100644
index 000000000000..4a05d0c6e344
--- /dev/null
+++ b/cpp/ql/src/jsf/jsfNote.qhelp
@@ -0,0 +1,18 @@
+
+
+
+
+This query is part of a suite that tests code against
+the Joint Strike Fighter Air Vehicle C++ Coding Standard (JSF).
+Alerts reported by this query highlight code that may break the
+JSF rule listed in the References section.
+
+
+
+The JSF rule this query tests is likely to be too strict for projects
+that do not follow the JSF standard.
+
+
+
diff --git a/cpp/ql/src/printAst.ql b/cpp/ql/src/printAst.ql
new file mode 100644
index 000000000000..622e5812be13
--- /dev/null
+++ b/cpp/ql/src/printAst.ql
@@ -0,0 +1,27 @@
+/**
+ * @name Print AST
+ * @description Outputs a representation of a file's Abstract Syntax Tree. This
+ * query is used by the VS Code extension.
+ * @id cpp/print-ast
+ * @kind graph
+ * @tags ide-contextual-queries/print-ast
+ */
+
+import cpp
+import semmle.code.cpp.PrintAST
+import definitions
+
+/**
+ * The source file to generate an AST from.
+ */
+external string selectedSourceFile();
+
+class Cfg extends PrintASTConfiguration {
+ /**
+ * Holds if the AST for `func` should be printed.
+ * Print All functions from the selected file.
+ */
+ override predicate shouldPrintFunction(Function func) {
+ func.getFile() = getEncodedFile(selectedSourceFile())
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/Class.qll b/cpp/ql/src/semmle/code/cpp/Class.qll
index 11ebef3e5ff2..f81f00064d7d 100644
--- a/cpp/ql/src/semmle/code/cpp/Class.qll
+++ b/cpp/ql/src/semmle/code/cpp/Class.qll
@@ -33,7 +33,7 @@ private import semmle.code.cpp.internal.ResolveClass
class Class extends UserType {
Class() { isClass(underlyingElement(this)) }
- override string getCanonicalQLClass() { result = "Class" }
+ override string getAPrimaryQlClass() { result = "Class" }
/** Gets a child declaration of this class, struct or union. */
override Declaration getADeclaration() { result = this.getAMember() }
@@ -768,7 +768,7 @@ class ClassDerivation extends Locatable, @derivation {
*/
Class getBaseClass() { result = getBaseType().getUnderlyingType() }
- override string getCanonicalQLClass() { result = "ClassDerivation" }
+ override string getAPrimaryQlClass() { result = "ClassDerivation" }
/**
* Gets the type from which we are deriving, without resolving any
@@ -849,9 +849,7 @@ class ClassDerivation extends Locatable, @derivation {
class LocalClass extends Class {
LocalClass() { isLocal() }
- override string getCanonicalQLClass() {
- not this instanceof LocalStruct and result = "LocalClass"
- }
+ override string getAPrimaryQlClass() { not this instanceof LocalStruct and result = "LocalClass" }
override Function getEnclosingAccessHolder() { result = this.getEnclosingFunction() }
}
@@ -872,7 +870,7 @@ class LocalClass extends Class {
class NestedClass extends Class {
NestedClass() { this.isMember() }
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not this instanceof NestedStruct and result = "NestedClass"
}
@@ -893,7 +891,7 @@ class NestedClass extends Class {
class AbstractClass extends Class {
AbstractClass() { exists(PureVirtualFunction f | this.getAMemberFunction() = f) }
- override string getCanonicalQLClass() { result = "AbstractClass" }
+ override string getAPrimaryQlClass() { result = "AbstractClass" }
}
/**
@@ -934,7 +932,7 @@ class TemplateClass extends Class {
exists(result.getATemplateArgument())
}
- override string getCanonicalQLClass() { result = "TemplateClass" }
+ override string getAPrimaryQlClass() { result = "TemplateClass" }
}
/**
@@ -955,7 +953,7 @@ class ClassTemplateInstantiation extends Class {
ClassTemplateInstantiation() { tc.getAnInstantiation() = this }
- override string getCanonicalQLClass() { result = "ClassTemplateInstantiation" }
+ override string getAPrimaryQlClass() { result = "ClassTemplateInstantiation" }
/**
* Gets the class template from which this instantiation was instantiated.
@@ -996,7 +994,7 @@ abstract class ClassTemplateSpecialization extends Class {
count(int i | exists(result.getTemplateArgument(i)))
}
- override string getCanonicalQLClass() { result = "ClassTemplateSpecialization" }
+ override string getAPrimaryQlClass() { result = "ClassTemplateSpecialization" }
}
/**
@@ -1025,7 +1023,7 @@ class FullClassTemplateSpecialization extends ClassTemplateSpecialization {
not this instanceof ClassTemplateInstantiation
}
- override string getCanonicalQLClass() { result = "FullClassTemplateSpecialization" }
+ override string getAPrimaryQlClass() { result = "FullClassTemplateSpecialization" }
}
/**
@@ -1064,7 +1062,7 @@ class PartialClassTemplateSpecialization extends ClassTemplateSpecialization {
count(int i | exists(getTemplateArgument(i)))
}
- override string getCanonicalQLClass() { result = "PartialClassTemplateSpecialization" }
+ override string getAPrimaryQlClass() { result = "PartialClassTemplateSpecialization" }
}
/**
@@ -1089,7 +1087,7 @@ deprecated class Interface extends Class {
)
}
- override string getCanonicalQLClass() { result = "Interface" }
+ override string getAPrimaryQlClass() { result = "Interface" }
}
/**
@@ -1104,7 +1102,7 @@ deprecated class Interface extends Class {
class VirtualClassDerivation extends ClassDerivation {
VirtualClassDerivation() { hasSpecifier("virtual") }
- override string getCanonicalQLClass() { result = "VirtualClassDerivation" }
+ override string getAPrimaryQlClass() { result = "VirtualClassDerivation" }
}
/**
@@ -1124,7 +1122,7 @@ class VirtualClassDerivation extends ClassDerivation {
class VirtualBaseClass extends Class {
VirtualBaseClass() { exists(VirtualClassDerivation cd | cd.getBaseClass() = this) }
- override string getCanonicalQLClass() { result = "VirtualBaseClass" }
+ override string getAPrimaryQlClass() { result = "VirtualBaseClass" }
/** A virtual class derivation of which this class/struct is the base. */
VirtualClassDerivation getAVirtualDerivation() { result.getBaseClass() = this }
@@ -1146,7 +1144,7 @@ class VirtualBaseClass extends Class {
class ProxyClass extends UserType {
ProxyClass() { usertypes(underlyingElement(this), _, 9) }
- override string getCanonicalQLClass() { result = "ProxyClass" }
+ override string getAPrimaryQlClass() { result = "ProxyClass" }
/** Gets the location of the proxy class. */
override Location getLocation() { result = getTemplateParameter().getDefinitionLocation() }
diff --git a/cpp/ql/src/semmle/code/cpp/Declaration.qll b/cpp/ql/src/semmle/code/cpp/Declaration.qll
index 6245005fd410..e5d3d83326ba 100644
--- a/cpp/ql/src/semmle/code/cpp/Declaration.qll
+++ b/cpp/ql/src/semmle/code/cpp/Declaration.qll
@@ -124,7 +124,7 @@ class Declaration extends Locatable, @declaration {
* To test whether this declaration has a particular name in the global
* namespace, use `hasGlobalName`.
*/
- abstract string getName();
+ string getName() { none() } // overridden in subclasses
/** Holds if this declaration has the given name. */
predicate hasName(string name) { name = this.getName() }
@@ -140,7 +140,7 @@ class Declaration extends Locatable, @declaration {
}
/** Gets a specifier of this declaration. */
- abstract Specifier getASpecifier();
+ Specifier getASpecifier() { none() } // overridden in subclasses
/** Holds if this declaration has a specifier with the given name. */
predicate hasSpecifier(string name) { this.getASpecifier().hasName(name) }
@@ -156,7 +156,7 @@ class Declaration extends Locatable, @declaration {
* Gets the location of a declaration entry corresponding to this
* declaration.
*/
- abstract Location getADeclarationLocation();
+ Location getADeclarationLocation() { none() } // overridden in subclasses
/**
* Gets the declaration entry corresponding to this declaration that is a
@@ -165,7 +165,7 @@ class Declaration extends Locatable, @declaration {
DeclarationEntry getDefinition() { none() }
/** Gets the location of the definition, if any. */
- abstract Location getDefinitionLocation();
+ Location getDefinitionLocation() { none() } // overridden in subclasses
/** Holds if the declaration has a definition. */
predicate hasDefinition() { exists(this.getDefinition()) }
@@ -289,6 +289,8 @@ class Declaration extends Locatable, @declaration {
}
}
+private class TDeclarationEntry = @var_decl or @type_decl or @fun_decl;
+
/**
* A C/C++ declaration entry. For example the following code contains five
* declaration entries:
@@ -304,9 +306,9 @@ class Declaration extends Locatable, @declaration {
* See the comment above `Declaration` for an explanation of the relationship
* between `Declaration` and `DeclarationEntry`.
*/
-abstract class DeclarationEntry extends Locatable {
+class DeclarationEntry extends Locatable, TDeclarationEntry {
/** Gets a specifier associated with this declaration entry. */
- abstract string getASpecifier();
+ string getASpecifier() { none() } // overridden in subclasses
/**
* Gets the name associated with the corresponding definition (where
@@ -329,10 +331,10 @@ abstract class DeclarationEntry extends Locatable {
* `I.getADeclarationEntry()` returns `D`
* but `D.getDeclaration()` only returns `C`
*/
- abstract Declaration getDeclaration();
+ Declaration getDeclaration() { none() } // overridden in subclasses
/** Gets the name associated with this declaration entry, if any. */
- abstract string getName();
+ string getName() { none() } // overridden in subclasses
/**
* Gets the type associated with this declaration entry.
@@ -341,7 +343,7 @@ abstract class DeclarationEntry extends Locatable {
* For function declarations, get the return type of the function.
* For type declarations, get the type being declared.
*/
- abstract Type getType();
+ Type getType() { none() } // overridden in subclasses
/**
* Gets the type associated with this declaration entry after specifiers
@@ -359,7 +361,7 @@ abstract class DeclarationEntry extends Locatable {
predicate hasSpecifier(string specifier) { getASpecifier() = specifier }
/** Holds if this declaration entry is a definition. */
- abstract predicate isDefinition();
+ predicate isDefinition() { none() } // overridden in subclasses
override string toString() {
if isDefinition()
@@ -371,6 +373,8 @@ abstract class DeclarationEntry extends Locatable {
}
}
+private class TAccessHolder = @function or @usertype;
+
/**
* A declaration that can potentially have more C++ access rights than its
* enclosing element. This comprises `Class` (they have access to their own
@@ -392,7 +396,7 @@ abstract class DeclarationEntry extends Locatable {
* the informal phrase "_R_ occurs in a member or friend of class C", where
* `AccessHolder` corresponds to this _R_.
*/
-abstract class AccessHolder extends Declaration {
+class AccessHolder extends Declaration, TAccessHolder {
/**
* Holds if `this` can access private members of class `c`.
*
@@ -410,7 +414,7 @@ abstract class AccessHolder extends Declaration {
/**
* Gets the nearest enclosing `AccessHolder`.
*/
- abstract AccessHolder getEnclosingAccessHolder();
+ AccessHolder getEnclosingAccessHolder() { none() } // overridden in subclasses
/**
* Holds if a base class `base` of `derived` _is accessible at_ `this` (N4140
diff --git a/cpp/ql/src/semmle/code/cpp/Element.qll b/cpp/ql/src/semmle/code/cpp/Element.qll
index 50b72037ff77..c7cc4a65b368 100644
--- a/cpp/ql/src/semmle/code/cpp/Element.qll
+++ b/cpp/ql/src/semmle/code/cpp/Element.qll
@@ -55,12 +55,21 @@ class ElementBase extends @element {
cached
string toString() { none() }
+ /** DEPRECATED: use `getAPrimaryQlClass` instead. */
+ deprecated string getCanonicalQLClass() { result = this.getAPrimaryQlClass() }
+
/**
- * Canonical QL class corresponding to this element.
+ * Gets the name of a primary CodeQL class to which this element belongs.
+ *
+ * For most elements, this is simply the most precise syntactic category to
+ * which they belong; for example, `AddExpr` is a primary class, but
+ * `BinaryOperation` is not.
*
- * ElementBase is the root class for this predicate.
+ * This predicate always has a result. If no primary class can be
+ * determined, the result is `"???"`. If multiple primary classes match,
+ * this predicate can have multiple results.
*/
- string getCanonicalQLClass() { result = "???" }
+ string getAPrimaryQlClass() { result = "???" }
}
/**
@@ -119,7 +128,7 @@ class Element extends ElementBase {
/**
* Gets the parent scope of this `Element`, if any.
- * A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `Block`, a `Function`,
+ * A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `BlockStmt`, a `Function`,
* or certain kinds of `Statement`.
*/
Element getParentScope() {
@@ -152,7 +161,7 @@ class Element extends ElementBase {
exists(EnumConstant e | this = e and result = e.getDeclaringEnum())
or
// result instanceof block|function
- exists(Block b | this = b and blockscope(unresolveElement(b), unresolveElement(result)))
+ exists(BlockStmt b | this = b and blockscope(unresolveElement(b), unresolveElement(result)))
or
exists(TemplateFunction tf | this = tf.getATemplateArgument() and result = tf)
or
@@ -188,7 +197,8 @@ class Element extends ElementBase {
initialisers(underlyingElement(this), unresolveElement(result), _, _) or
exprconv(unresolveElement(result), underlyingElement(this)) or
param_decl_bind(underlyingElement(this), _, unresolveElement(result)) or
- using_container(unresolveElement(result), underlyingElement(this))
+ using_container(unresolveElement(result), underlyingElement(this)) or
+ static_asserts(unresolveElement(this), _, _, _, underlyingElement(result))
}
/** Gets the closest `Element` enclosing this one. */
@@ -269,12 +279,12 @@ class StaticAssert extends Locatable, @static_assert {
/**
* Gets the expression which this static assertion ensures is true.
*/
- Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _) }
+ Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _, _) }
/**
* Gets the message which will be reported by the compiler if this static assertion fails.
*/
- string getMessage() { static_asserts(underlyingElement(this), _, result, _) }
+ string getMessage() { static_asserts(underlyingElement(this), _, result, _, _) }
- override Location getLocation() { static_asserts(underlyingElement(this), _, _, result) }
+ override Location getLocation() { static_asserts(underlyingElement(this), _, _, result, _) }
}
diff --git a/cpp/ql/src/semmle/code/cpp/Enum.qll b/cpp/ql/src/semmle/code/cpp/Enum.qll
index 2c51a5228d98..9cddeb78f9b1 100644
--- a/cpp/ql/src/semmle/code/cpp/Enum.qll
+++ b/cpp/ql/src/semmle/code/cpp/Enum.qll
@@ -38,7 +38,7 @@ class Enum extends UserType, IntegralOrEnumType {
enumconstants(unresolveElement(result), underlyingElement(this), index, _, _, _)
}
- override string getCanonicalQLClass() { result = "Enum" }
+ override string getAPrimaryQlClass() { result = "Enum" }
/**
* Gets a descriptive string for the enum. This method is only intended to
@@ -87,7 +87,7 @@ class Enum extends UserType, IntegralOrEnumType {
class LocalEnum extends Enum {
LocalEnum() { isLocal() }
- override string getCanonicalQLClass() { result = "LocalEnum" }
+ override string getAPrimaryQlClass() { result = "LocalEnum" }
}
/**
@@ -105,7 +105,7 @@ class LocalEnum extends Enum {
class NestedEnum extends Enum {
NestedEnum() { this.isMember() }
- override string getCanonicalQLClass() { result = "NestedEnum" }
+ override string getAPrimaryQlClass() { result = "NestedEnum" }
/** Holds if this member is private. */
predicate isPrivate() { this.hasSpecifier("private") }
@@ -130,7 +130,7 @@ class NestedEnum extends Enum {
class ScopedEnum extends Enum {
ScopedEnum() { usertypes(underlyingElement(this), _, 13) }
- override string getCanonicalQLClass() { result = "ScopedEnum" }
+ override string getAPrimaryQlClass() { result = "ScopedEnum" }
}
/**
@@ -153,7 +153,7 @@ class EnumConstant extends Declaration, @enumconstant {
enumconstants(underlyingElement(this), unresolveElement(result), _, _, _, _)
}
- override string getCanonicalQLClass() { result = "EnumConstant" }
+ override string getAPrimaryQlClass() { result = "EnumConstant" }
override Class getDeclaringType() { result = this.getDeclaringEnum().getDeclaringType() }
diff --git a/cpp/ql/src/semmle/code/cpp/Field.qll b/cpp/ql/src/semmle/code/cpp/Field.qll
index 79c9b58dfea1..5ed5e8e4b4bc 100644
--- a/cpp/ql/src/semmle/code/cpp/Field.qll
+++ b/cpp/ql/src/semmle/code/cpp/Field.qll
@@ -23,7 +23,7 @@ import semmle.code.cpp.exprs.Access
class Field extends MemberVariable {
Field() { fieldoffsets(underlyingElement(this), _, _) }
- override string getCanonicalQLClass() { result = "Field" }
+ override string getAPrimaryQlClass() { result = "Field" }
/**
* Gets the offset of this field in bytes from the start of its declaring
@@ -90,7 +90,7 @@ class Field extends MemberVariable {
class BitField extends Field {
BitField() { bitfield(underlyingElement(this), _, _) }
- override string getCanonicalQLClass() { result = "BitField" }
+ override string getAPrimaryQlClass() { result = "BitField" }
/**
* Gets the size of this bitfield in bits (on the machine where facts
diff --git a/cpp/ql/src/semmle/code/cpp/File.qll b/cpp/ql/src/semmle/code/cpp/File.qll
index 061e79c7d452..1887f43b70cb 100644
--- a/cpp/ql/src/semmle/code/cpp/File.qll
+++ b/cpp/ql/src/semmle/code/cpp/File.qll
@@ -178,7 +178,7 @@ class Folder extends Container, @folder {
result.hasLocationInfo(_, 0, 0, 0, 0)
}
- override string getCanonicalQLClass() { result = "Folder" }
+ override string getAPrimaryQlClass() { result = "Folder" }
/**
* DEPRECATED: Use `getLocation` instead.
@@ -246,7 +246,7 @@ class File extends Container, @file {
override string toString() { result = Container.super.toString() }
- override string getCanonicalQLClass() { result = "File" }
+ override string getAPrimaryQlClass() { result = "File" }
override Location getLocation() {
result.getContainer() = this and
@@ -382,7 +382,7 @@ class HeaderFile extends File {
exists(Include i | i.getIncludedFile() = this)
}
- override string getCanonicalQLClass() { result = "HeaderFile" }
+ override string getAPrimaryQlClass() { result = "HeaderFile" }
/**
* Holds if this header file does not contain any declaration entries or top level
@@ -408,7 +408,7 @@ class HeaderFile extends File {
class CFile extends File {
CFile() { exists(string ext | ext = this.getExtension().toLowerCase() | ext = "c" or ext = "i") }
- override string getCanonicalQLClass() { result = "CFile" }
+ override string getAPrimaryQlClass() { result = "CFile" }
}
/**
@@ -436,7 +436,7 @@ class CppFile extends File {
)
}
- override string getCanonicalQLClass() { result = "CppFile" }
+ override string getAPrimaryQlClass() { result = "CppFile" }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/FriendDecl.qll b/cpp/ql/src/semmle/code/cpp/FriendDecl.qll
index e0a6b04b1fcd..9a35f7527d52 100644
--- a/cpp/ql/src/semmle/code/cpp/FriendDecl.qll
+++ b/cpp/ql/src/semmle/code/cpp/FriendDecl.qll
@@ -27,7 +27,7 @@ class FriendDecl extends Declaration, @frienddecl {
*/
override Location getADeclarationLocation() { result = this.getLocation() }
- override string getCanonicalQLClass() { result = "FriendDecl" }
+ override string getAPrimaryQlClass() { result = "FriendDecl" }
/**
* Implements the abstract method `Declaration.getDefinitionLocation`. A
diff --git a/cpp/ql/src/semmle/code/cpp/Function.qll b/cpp/ql/src/semmle/code/cpp/Function.qll
index 20abef21d7b8..2c45fa184cf6 100644
--- a/cpp/ql/src/semmle/code/cpp/Function.qll
+++ b/cpp/ql/src/semmle/code/cpp/Function.qll
@@ -1,10 +1,8 @@
/**
- * Provides classes for working with functions, including C++ constructors, destructors,
- * user-defined operators, and template functions.
+ * Provides classes for working with functions, including template functions.
*/
import semmle.code.cpp.Location
-import semmle.code.cpp.Member
import semmle.code.cpp.Class
import semmle.code.cpp.Parameter
import semmle.code.cpp.exprs.Call
@@ -270,7 +268,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
* block, this gives the block guarded by the try statement. See
* `FunctionTryStmt` for further information.
*/
- Block getBlock() { result.getParentScope() = this }
+ BlockStmt getBlock() { result.getParentScope() = this }
/** Holds if this function has an entry point. */
predicate hasEntryPoint() { exists(getEntryPoint()) }
@@ -278,7 +276,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
/**
* Gets the first node in this function's control flow graph.
*
- * For most functions, this first node will be the `Block` returned by
+ * For most functions, this first node will be the `BlockStmt` returned by
* `getBlock`. However in C++, the first node can also be a
* `FunctionTryStmt`.
*/
@@ -515,7 +513,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
/** Gets the function which is being declared or defined. */
override Function getDeclaration() { result = getFunction() }
- override string getCanonicalQLClass() { result = "FunctionDeclarationEntry" }
+ override string getAPrimaryQlClass() { result = "FunctionDeclarationEntry" }
/** Gets the function which is being declared or defined. */
Function getFunction() { fun_decls(underlyingElement(this), unresolveElement(result), _, _, _) }
@@ -566,7 +564,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
* If this is a function definition, get the block containing the
* function body.
*/
- Block getBlock() {
+ BlockStmt getBlock() {
this.isDefinition() and
result = getFunction().getBlock() and
result.getFile() = this.getFile()
@@ -578,7 +576,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
*/
pragma[noopt]
int getNumberOfLines() {
- exists(Block b, Location l, int start, int end, int diff | b = getBlock() |
+ exists(BlockStmt b, Location l, int start, int end, int diff | b = getBlock() |
l = b.getLocation() and
start = l.getStartLine() and
end = l.getEndLine() and
@@ -700,430 +698,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
class TopLevelFunction extends Function {
TopLevelFunction() { not this.isMember() }
- override string getCanonicalQLClass() { result = "TopLevelFunction" }
-}
-
-/**
- * A C++ function declared as a member of a class [N4140 9.3]. This includes
- * static member functions. For example the functions `MyStaticMemberFunction`
- * and `MyMemberFunction` in:
- * ```
- * class MyClass {
- * public:
- * void MyMemberFunction() {
- * DoSomething();
- * }
- *
- * static void MyStaticMemberFunction() {
- * DoSomething();
- * }
- * };
- * ```
- */
-class MemberFunction extends Function {
- MemberFunction() { this.isMember() }
-
- override string getCanonicalQLClass() {
- not this instanceof CopyAssignmentOperator and
- not this instanceof MoveAssignmentOperator and
- result = "MemberFunction"
- }
-
- /**
- * Gets the number of parameters of this function, including any implicit
- * `this` parameter.
- */
- override int getEffectiveNumberOfParameters() {
- if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1
- }
-
- /** Holds if this member is private. */
- predicate isPrivate() { this.hasSpecifier("private") }
-
- /** Holds if this member is protected. */
- predicate isProtected() { this.hasSpecifier("protected") }
-
- /** Holds if this member is public. */
- predicate isPublic() { this.hasSpecifier("public") }
-
- /** Holds if this function overrides that function. */
- predicate overrides(MemberFunction that) {
- overrides(underlyingElement(this), unresolveElement(that))
- }
-
- /** Gets a directly overridden function. */
- MemberFunction getAnOverriddenFunction() { this.overrides(result) }
-
- /** Gets a directly overriding function. */
- MemberFunction getAnOverridingFunction() { result.overrides(this) }
-
- /**
- * Gets the declaration entry for this member function that is within the
- * class body.
- */
- FunctionDeclarationEntry getClassBodyDeclarationEntry() {
- if strictcount(getADeclarationEntry()) = 1
- then result = getDefinition()
- else (
- result = getADeclarationEntry() and result != getDefinition()
- )
- }
-}
-
-/**
- * A C++ virtual function. For example the two functions called
- * `myVirtualFunction` in the following code are each a
- * `VirtualFunction`:
- * ```
- * class A {
- * public:
- * virtual void myVirtualFunction() = 0;
- * };
- *
- * class B: public A {
- * public:
- * virtual void myVirtualFunction() {
- * doSomething();
- * }
- * };
- * ```
- */
-class VirtualFunction extends MemberFunction {
- VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) }
-
- override string getCanonicalQLClass() { result = "VirtualFunction" }
-
- /** Holds if this virtual function is pure. */
- predicate isPure() { this instanceof PureVirtualFunction }
-
- /**
- * Holds if this function was declared with the `override` specifier
- * [N4140 10.3].
- */
- predicate isOverrideExplicit() { this.hasSpecifier("override") }
-}
-
-/**
- * A C++ pure virtual function [N4140 10.4]. For example the first function
- * called `myVirtualFunction` in the following code:
- * ```
- * class A {
- * public:
- * virtual void myVirtualFunction() = 0;
- * };
- *
- * class B: public A {
- * public:
- * virtual void myVirtualFunction() {
- * doSomething();
- * }
- * };
- * ```
- */
-class PureVirtualFunction extends VirtualFunction {
- PureVirtualFunction() { purefunctions(underlyingElement(this)) }
-
- override string getCanonicalQLClass() { result = "PureVirtualFunction" }
-}
-
-/**
- * A const C++ member function [N4140 9.3.1/4]. A const function has the
- * `const` specifier and does not modify the state of its class. For example
- * the member function `day` in the following code:
- * ```
- * class MyClass {
- * ...
- *
- * int day() const {
- * return d;
- * }
- *
- * ...
- * };
- * ```
- */
-class ConstMemberFunction extends MemberFunction {
- ConstMemberFunction() { this.hasSpecifier("const") }
-
- override string getCanonicalQLClass() { result = "ConstMemberFunction" }
-}
-
-/**
- * A C++ constructor [N4140 12.1]. For example the function `MyClass` in the
- * following code is a constructor:
- * ```
- * class MyClass {
- * public:
- * MyClass() {
- * ...
- * }
- * };
- * ```
- */
-class Constructor extends MemberFunction {
- Constructor() { functions(underlyingElement(this), _, 2) }
-
- override string getCanonicalQLClass() { result = "Constructor" }
-
- /**
- * Holds if this constructor serves as a default constructor.
- *
- * This holds for constructors with zero formal parameters. It also holds
- * for constructors which have a non-zero number of formal parameters,
- * provided that every parameter has a default value.
- */
- predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) }
-
- /**
- * Gets an entry in the constructor's initializer list, or a
- * compiler-generated action which initializes a base class or member
- * variable.
- */
- ConstructorInit getAnInitializer() { result = getInitializer(_) }
-
- /**
- * Gets an entry in the constructor's initializer list, or a
- * compiler-generated action which initializes a base class or member
- * variable. The index specifies the order in which the initializer is
- * to be evaluated.
- */
- ConstructorInit getInitializer(int i) {
- exprparents(unresolveElement(result), i, underlyingElement(this))
- }
-}
-
-/**
- * A function that defines an implicit conversion.
- */
-abstract class ImplicitConversionFunction extends MemberFunction {
- /** Gets the type this `ImplicitConversionFunction` takes as input. */
- abstract Type getSourceType();
-
- /** Gets the type this `ImplicitConversionFunction` converts to. */
- abstract Type getDestType();
-}
-
-/**
- * A C++ constructor that also defines an implicit conversion. For example the
- * function `MyClass` in the following code is a `ConversionConstructor`:
- * ```
- * class MyClass {
- * public:
- * MyClass(const MyOtherClass &from) {
- * ...
- * }
- * };
- * ```
- */
-class ConversionConstructor extends Constructor, ImplicitConversionFunction {
- ConversionConstructor() {
- strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
- not hasSpecifier("explicit") and
- not this instanceof CopyConstructor
- }
-
- override string getCanonicalQLClass() {
- not this instanceof MoveConstructor and result = "ConversionConstructor"
- }
-
- /** Gets the type this `ConversionConstructor` takes as input. */
- override Type getSourceType() { result = this.getParameter(0).getType() }
-
- /** Gets the type this `ConversionConstructor` is a constructor of. */
- override Type getDestType() { result = this.getDeclaringType() }
-}
-
-private predicate hasCopySignature(MemberFunction f) {
- f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType()
-}
-
-private predicate hasMoveSignature(MemberFunction f) {
- f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType()
-}
-
-/**
- * A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in
- * the following code is a `CopyConstructor`:
- * ```
- * class MyClass {
- * public:
- * MyClass(const MyClass &from) {
- * ...
- * }
- * };
- * ```
- *
- * As per the standard, a copy constructor of class `T` is a non-template
- * constructor whose first parameter has type `T&`, `const T&`, `volatile
- * T&`, or `const volatile T&`, and either there are no other parameters,
- * or the rest of the parameters all have default values.
- *
- * For template classes, it can generally not be determined until instantiation
- * whether a constructor is a copy constructor. For such classes, `CopyConstructor`
- * over-approximates the set of copy constructors; if an under-approximation is
- * desired instead, see the member predicate
- * `mayNotBeCopyConstructorInInstantiation`.
- */
-class CopyConstructor extends Constructor {
- CopyConstructor() {
- hasCopySignature(this) and
- (
- // The rest of the parameters all have default values
- forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
- or
- // or this is a template class, in which case the default values have
- // not been extracted even if they exist. In that case, we assume that
- // there are default values present since that is the most common case
- // in real-world code.
- getDeclaringType() instanceof TemplateClass
- ) and
- not exists(getATemplateArgument())
- }
-
- override string getCanonicalQLClass() { result = "CopyConstructor" }
-
- /**
- * Holds if we cannot determine that this constructor will become a copy
- * constructor in all instantiations. Depending on template parameters of the
- * enclosing class, this may become an ordinary constructor or a copy
- * constructor.
- */
- predicate mayNotBeCopyConstructorInInstantiation() {
- // In general, default arguments of template classes can only be
- // type-checked for each template instantiation; if an argument in an
- // instantiation fails to type-check then the corresponding parameter has
- // no default argument in the instantiation.
- getDeclaringType() instanceof TemplateClass and
- getNumberOfParameters() > 1
- }
-}
-
-/**
- * A C++ move constructor [N4140 12.8]. For example the function `MyClass` in
- * the following code is a `MoveConstructor`:
- * ```
- * class MyClass {
- * public:
- * MyClass(MyClass &&from) {
- * ...
- * }
- * };
- * ```
- *
- * As per the standard, a move constructor of class `T` is a non-template
- * constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`,
- * or `const volatile T&&`, and either there are no other parameters, or
- * the rest of the parameters all have default values.
- *
- * For template classes, it can generally not be determined until instantiation
- * whether a constructor is a move constructor. For such classes, `MoveConstructor`
- * over-approximates the set of move constructors; if an under-approximation is
- * desired instead, see the member predicate
- * `mayNotBeMoveConstructorInInstantiation`.
- */
-class MoveConstructor extends Constructor {
- MoveConstructor() {
- hasMoveSignature(this) and
- (
- // The rest of the parameters all have default values
- forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
- or
- // or this is a template class, in which case the default values have
- // not been extracted even if they exist. In that case, we assume that
- // there are default values present since that is the most common case
- // in real-world code.
- getDeclaringType() instanceof TemplateClass
- ) and
- not exists(getATemplateArgument())
- }
-
- override string getCanonicalQLClass() { result = "MoveConstructor" }
-
- /**
- * Holds if we cannot determine that this constructor will become a move
- * constructor in all instantiations. Depending on template parameters of the
- * enclosing class, this may become an ordinary constructor or a move
- * constructor.
- */
- predicate mayNotBeMoveConstructorInInstantiation() {
- // In general, default arguments of template classes can only be
- // type-checked for each template instantiation; if an argument in an
- // instantiation fails to type-check then the corresponding parameter has
- // no default argument in the instantiation.
- getDeclaringType() instanceof TemplateClass and
- getNumberOfParameters() > 1
- }
-}
-
-/**
- * A C++ constructor that takes no arguments ('default' constructor). This
- * is the constructor that is invoked when no initializer is given. For
- * example the function `MyClass` in the following code is a
- * `NoArgConstructor`:
- * ```
- * class MyClass {
- * public:
- * MyClass() {
- * ...
- * }
- * };
- * ```
- */
-class NoArgConstructor extends Constructor {
- NoArgConstructor() { this.getNumberOfParameters() = 0 }
-}
-
-/**
- * A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the
- * following code is a destructor:
- * ```
- * class MyClass {
- * public:
- * ~MyClass() {
- * ...
- * }
- * };
- * ```
- */
-class Destructor extends MemberFunction {
- Destructor() { functions(underlyingElement(this), _, 3) }
-
- override string getCanonicalQLClass() { result = "Destructor" }
-
- /**
- * Gets a compiler-generated action which destructs a base class or member
- * variable.
- */
- DestructorDestruction getADestruction() { result = getDestruction(_) }
-
- /**
- * Gets a compiler-generated action which destructs a base class or member
- * variable. The index specifies the order in which the destruction should
- * be evaluated.
- */
- DestructorDestruction getDestruction(int i) {
- exprparents(unresolveElement(result), i, underlyingElement(this))
- }
-}
-
-/**
- * A C++ conversion operator [N4140 12.3.2]. For example the function
- * `operator int` in the following code is a `ConversionOperator`:
- * ```
- * class MyClass {
- * public:
- * operator int();
- * };
- * ```
- */
-class ConversionOperator extends MemberFunction, ImplicitConversionFunction {
- ConversionOperator() { functions(underlyingElement(this), _, 4) }
-
- override string getCanonicalQLClass() { result = "ConversionOperator" }
-
- override Type getSourceType() { result = this.getDeclaringType() }
-
- override Type getDestType() { result = this.getType() }
+ override string getAPrimaryQlClass() { result = "TopLevelFunction" }
}
/**
@@ -1132,69 +707,11 @@ class ConversionOperator extends MemberFunction, ImplicitConversionFunction {
class Operator extends Function {
Operator() { functions(underlyingElement(this), _, 5) }
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not this instanceof MemberFunction and result = "Operator"
}
}
-/**
- * A C++ copy assignment operator [N4140 12.8]. For example the function
- * `operator=` in the following code is a `CopyAssignmentOperator`:
- * ```
- * class MyClass {
- * public:
- * MyClass &operator=(const MyClass &other);
- * };
- * ```
- *
- * As per the standard, a copy assignment operator of class `T` is a
- * non-template non-static member function with the name `operator=` that
- * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile
- * T&`, or `const volatile T&`.
- */
-class CopyAssignmentOperator extends Operator {
- CopyAssignmentOperator() {
- hasName("operator=") and
- (
- hasCopySignature(this)
- or
- // Unlike CopyConstructor, this member allows a non-reference
- // parameter.
- getParameter(0).getUnspecifiedType() = getDeclaringType()
- ) and
- not exists(this.getParameter(1)) and
- not exists(getATemplateArgument())
- }
-
- override string getCanonicalQLClass() { result = "CopyAssignmentOperator" }
-}
-
-/**
- * A C++ move assignment operator [N4140 12.8]. For example the function
- * `operator=` in the following code is a `MoveAssignmentOperator`:
- * ```
- * class MyClass {
- * public:
- * MyClass &operator=(MyClass &&other);
- * };
- * ```
- *
- * As per the standard, a move assignment operator of class `T` is a
- * non-template non-static member function with the name `operator=` that
- * takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`,
- * or `const volatile T&&`.
- */
-class MoveAssignmentOperator extends Operator {
- MoveAssignmentOperator() {
- hasName("operator=") and
- hasMoveSignature(this) and
- not exists(this.getParameter(1)) and
- not exists(getATemplateArgument())
- }
-
- override string getCanonicalQLClass() { result = "MoveAssignmentOperator" }
-}
-
/**
* A C++ function which has a non-empty template argument list. For example
* the function `myTemplateFunction` in the following code:
@@ -1221,7 +738,7 @@ class TemplateFunction extends Function {
is_function_template(underlyingElement(this)) and exists(getATemplateArgument())
}
- override string getCanonicalQLClass() { result = "TemplateFunction" }
+ override string getAPrimaryQlClass() { result = "TemplateFunction" }
/**
* Gets a compiler-generated instantiation of this function template.
@@ -1261,7 +778,7 @@ class FunctionTemplateInstantiation extends Function {
FunctionTemplateInstantiation() { tf.getAnInstantiation() = this }
- override string getCanonicalQLClass() { result = "FunctionTemplateInstantiation" }
+ override string getAPrimaryQlClass() { result = "FunctionTemplateInstantiation" }
/**
* Gets the function template from which this instantiation was instantiated.
@@ -1306,7 +823,7 @@ class FunctionTemplateInstantiation extends Function {
class FunctionTemplateSpecialization extends Function {
FunctionTemplateSpecialization() { this.isSpecialization() }
- override string getCanonicalQLClass() { result = "FunctionTemplateSpecialization" }
+ override string getAPrimaryQlClass() { result = "FunctionTemplateSpecialization" }
/**
* Gets the primary template for the specialization (the function template
diff --git a/cpp/ql/src/semmle/code/cpp/Include.qll b/cpp/ql/src/semmle/code/cpp/Include.qll
index 11702ce1bf69..f21edb2651d9 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() }
diff --git a/cpp/ql/src/semmle/code/cpp/Initializer.qll b/cpp/ql/src/semmle/code/cpp/Initializer.qll
index 80601f659da8..643a880ddf2c 100644
--- a/cpp/ql/src/semmle/code/cpp/Initializer.qll
+++ b/cpp/ql/src/semmle/code/cpp/Initializer.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides the `Initializer` class, representing C/C++ declaration initializers.
+ */
+
import semmle.code.cpp.controlflow.ControlFlowGraph
/**
@@ -18,7 +22,7 @@ import semmle.code.cpp.controlflow.ControlFlowGraph
class Initializer extends ControlFlowNode, @initialiser {
override Location getLocation() { initialisers(underlyingElement(this), _, _, result) }
- override string getCanonicalQLClass() { result = "Initializer" }
+ override string getAPrimaryQlClass() { result = "Initializer" }
/** Holds if this initializer is explicit in the source. */
override predicate fromSource() { not this.getLocation() instanceof UnknownLocation }
diff --git a/cpp/ql/src/semmle/code/cpp/Iteration.qll b/cpp/ql/src/semmle/code/cpp/Iteration.qll
index fd7ba60ea194..d87306c4babe 100644
--- a/cpp/ql/src/semmle/code/cpp/Iteration.qll
+++ b/cpp/ql/src/semmle/code/cpp/Iteration.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes for loop iteration variables.
+ */
+
import semmle.code.cpp.Variable
/**
@@ -7,14 +11,18 @@ import semmle.code.cpp.Variable
class LoopCounter extends Variable {
LoopCounter() { exists(ForStmt f | f.getAnIterationVariable() = this) }
- // Gets an access of this variable within loop `f`.
+ /**
+ * Gets an access of this variable within loop `f`.
+ */
VariableAccess getVariableAccessInLoop(ForStmt f) {
this.getALoop() = f and
result.getEnclosingStmt().getParent*() = f and
this = result.getTarget()
}
- // Gets a loop which uses this variable as its counter.
+ /**
+ * Gets a loop which uses this variable as its counter.
+ */
ForStmt getALoop() { result.getAnIterationVariable() = this }
}
@@ -25,14 +33,18 @@ class LoopCounter extends Variable {
class LoopControlVariable extends Variable {
LoopControlVariable() { this = loopControlVariable(_) }
- // Gets an access of this variable within loop `f`.
+ /**
+ * Gets an access of this variable within loop `f`.
+ */
VariableAccess getVariableAccessInLoop(ForStmt f) {
this.getALoop() = f and
result.getEnclosingStmt().getParent*() = f and
this = result.getTarget()
}
- // Gets a loop which uses this variable as its control variable.
+ /**
+ * Gets a loop which uses this variable as its control variable.
+ */
ForStmt getALoop() { this = loopControlVariable(result) }
}
diff --git a/cpp/ql/src/semmle/code/cpp/Linkage.qll b/cpp/ql/src/semmle/code/cpp/Linkage.qll
index 7912c9e25e1b..54a6099eaef2 100644
--- a/cpp/ql/src/semmle/code/cpp/Linkage.qll
+++ b/cpp/ql/src/semmle/code/cpp/Linkage.qll
@@ -1,3 +1,7 @@
+/**
+ * Proivdes the `LinkTarget` class representing linker invocations during the build process.
+ */
+
import semmle.code.cpp.Class
import semmle.code.cpp.File
import semmle.code.cpp.Function
diff --git a/cpp/ql/src/semmle/code/cpp/Location.qll b/cpp/ql/src/semmle/code/cpp/Location.qll
index 129ffba0f748..dc37d4009c05 100644
--- a/cpp/ql/src/semmle/code/cpp/Location.qll
+++ b/cpp/ql/src/semmle/code/cpp/Location.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes and predicates for locations in the source code.
+ */
+
import semmle.code.cpp.Element
import semmle.code.cpp.File
@@ -11,16 +15,16 @@ class Location extends @location {
/** Gets the file corresponding to this location, if any. */
File getFile() { result = this.getContainer() }
- /** Gets the start line of this location. */
+ /** Gets the 1-based line number (inclusive) where this location starts. */
int getStartLine() { this.fullLocationInfo(_, result, _, _, _) }
- /** Gets the start column of this location. */
+ /** Gets the 1-based column number (inclusive) where this location starts. */
int getStartColumn() { this.fullLocationInfo(_, _, result, _, _) }
- /** Gets the end line of this location. */
+ /** Gets the 1-based line number (inclusive) where this location ends. */
int getEndLine() { this.fullLocationInfo(_, _, _, result, _) }
- /** Gets the end column of this location. */
+ /** Gets the 1-based column number (inclusive) where this location ends. */
int getEndColumn() { this.fullLocationInfo(_, _, _, _, result) }
/**
@@ -101,16 +105,23 @@ class Location extends @location {
}
/**
+ * DEPRECATED: Use `Location` instead.
* A location of an element. Not used for expressions or statements, which
* instead use LocationExpr and LocationStmt respectively.
*/
-library class LocationDefault extends Location, @location_default { }
+deprecated library class LocationDefault extends Location, @location_default { }
-/** A location of a statement. */
-library class LocationStmt extends Location, @location_stmt { }
+/**
+ * DEPRECATED: Use `Location` instead.
+ * A location of a statement.
+ */
+deprecated library class LocationStmt extends Location, @location_stmt { }
-/** A location of an expression. */
-library class LocationExpr extends Location, @location_expr { }
+/**
+ * DEPRECATED: Use `Location` instead.
+ * A location of an expression.
+ */
+deprecated library class LocationExpr extends Location, @location_expr { }
/**
* Gets the length of the longest line in file `f`.
diff --git a/cpp/ql/src/semmle/code/cpp/Macro.qll b/cpp/ql/src/semmle/code/cpp/Macro.qll
index 389634912b7d..aa4b8d419990 100644
--- a/cpp/ql/src/semmle/code/cpp/Macro.qll
+++ b/cpp/ql/src/semmle/code/cpp/Macro.qll
@@ -13,7 +13,7 @@ class Macro extends PreprocessorDirective, @ppd_define {
*/
override string getHead() { preproctext(underlyingElement(this), result, _) }
- override string getCanonicalQLClass() { result = "Macro" }
+ override string getAPrimaryQlClass() { result = "Macro" }
/**
* Gets the body of this macro. For example, `(((x)>(y))?(x):(y))` in
@@ -74,7 +74,7 @@ class MacroAccess extends Locatable, @macroinvocation {
*/
override Location getLocation() { result = this.getOutermostMacroAccess().getActualLocation() }
- override string getCanonicalQLClass() { result = "MacroAccess" }
+ override string getAPrimaryQlClass() { result = "MacroAccess" }
/**
* Gets the location of this macro access. For a nested access, where
@@ -147,7 +147,7 @@ class MacroAccess extends Locatable, @macroinvocation {
class MacroInvocation extends MacroAccess {
MacroInvocation() { macroinvocations(underlyingElement(this), _, _, 1) }
- override string getCanonicalQLClass() { result = "MacroInvocation" }
+ override string getAPrimaryQlClass() { result = "MacroInvocation" }
/**
* Gets an element that occurs in this macro invocation or a nested macro
@@ -179,6 +179,11 @@ class MacroInvocation extends MacroAccess {
result.(Stmt).getGeneratingMacro() = this
}
+ /**
+ * Gets a function that includes an expression that is affected by this macro
+ * invocation. If the macro expansion includes the end of one function and
+ * the beginning of another, this predicate will get both.
+ */
Function getEnclosingFunction() {
result = this.getAnAffectedElement().(Expr).getEnclosingFunction()
}
diff --git a/cpp/ql/src/semmle/code/cpp/Member.qll b/cpp/ql/src/semmle/code/cpp/Member.qll
index 92769486ae98..f47edbddeba0 100644
--- a/cpp/ql/src/semmle/code/cpp/Member.qll
+++ b/cpp/ql/src/semmle/code/cpp/Member.qll
@@ -1,2 +1,6 @@
+/**
+ * DEPRECATED: import `semmle.code.cpp.Element` and/or `semmle.code.cpp.Type` directly as required.
+ */
+
import semmle.code.cpp.Element
import semmle.code.cpp.Type
diff --git a/cpp/ql/src/semmle/code/cpp/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll
new file mode 100644
index 000000000000..0a692f755ed7
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/MemberFunction.qll
@@ -0,0 +1,499 @@
+/**
+ * Provides classes for working with C++ member functions, constructors, destructors,
+ * and user-defined operators.
+ */
+
+import cpp
+
+/**
+ * A C++ function declared as a member of a class [N4140 9.3]. This includes
+ * static member functions. For example the functions `MyStaticMemberFunction`
+ * and `MyMemberFunction` in:
+ * ```
+ * class MyClass {
+ * public:
+ * void MyMemberFunction() {
+ * DoSomething();
+ * }
+ *
+ * static void MyStaticMemberFunction() {
+ * DoSomething();
+ * }
+ * };
+ * ```
+ */
+class MemberFunction extends Function {
+ MemberFunction() { this.isMember() }
+
+ override string getAPrimaryQlClass() {
+ not this instanceof CopyAssignmentOperator and
+ not this instanceof MoveAssignmentOperator and
+ result = "MemberFunction"
+ }
+
+ /**
+ * Gets the number of parameters of this function, including any implicit
+ * `this` parameter.
+ */
+ override int getEffectiveNumberOfParameters() {
+ if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1
+ }
+
+ /** Holds if this member is private. */
+ predicate isPrivate() { this.hasSpecifier("private") }
+
+ /** Holds if this member is protected. */
+ predicate isProtected() { this.hasSpecifier("protected") }
+
+ /** Holds if this member is public. */
+ predicate isPublic() { this.hasSpecifier("public") }
+
+ /** Holds if this function overrides that function. */
+ predicate overrides(MemberFunction that) {
+ overrides(underlyingElement(this), unresolveElement(that))
+ }
+
+ /** Gets a directly overridden function. */
+ MemberFunction getAnOverriddenFunction() { this.overrides(result) }
+
+ /** Gets a directly overriding function. */
+ MemberFunction getAnOverridingFunction() { result.overrides(this) }
+
+ /**
+ * Gets the declaration entry for this member function that is within the
+ * class body.
+ */
+ FunctionDeclarationEntry getClassBodyDeclarationEntry() {
+ if strictcount(getADeclarationEntry()) = 1
+ then result = getDefinition()
+ else (
+ result = getADeclarationEntry() and result != getDefinition()
+ )
+ }
+
+ /**
+ * Gets the type of the `this` parameter associated with this member function, if any. The type
+ * may have `const` and/or `volatile` qualifiers, matching the function declaration.
+ */
+ PointerType getTypeOfThis() {
+ member_function_this_type(underlyingElement(this), unresolveElement(result))
+ }
+}
+
+/**
+ * A C++ virtual function. For example the two functions called
+ * `myVirtualFunction` in the following code are each a
+ * `VirtualFunction`:
+ * ```
+ * class A {
+ * public:
+ * virtual void myVirtualFunction() = 0;
+ * };
+ *
+ * class B: public A {
+ * public:
+ * virtual void myVirtualFunction() {
+ * doSomething();
+ * }
+ * };
+ * ```
+ */
+class VirtualFunction extends MemberFunction {
+ VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) }
+
+ override string getAPrimaryQlClass() { result = "VirtualFunction" }
+
+ /** Holds if this virtual function is pure. */
+ predicate isPure() { this instanceof PureVirtualFunction }
+
+ /**
+ * Holds if this function was declared with the `override` specifier
+ * [N4140 10.3].
+ */
+ predicate isOverrideExplicit() { this.hasSpecifier("override") }
+}
+
+/**
+ * A C++ pure virtual function [N4140 10.4]. For example the first function
+ * called `myVirtualFunction` in the following code:
+ * ```
+ * class A {
+ * public:
+ * virtual void myVirtualFunction() = 0;
+ * };
+ *
+ * class B: public A {
+ * public:
+ * virtual void myVirtualFunction() {
+ * doSomething();
+ * }
+ * };
+ * ```
+ */
+class PureVirtualFunction extends VirtualFunction {
+ PureVirtualFunction() { purefunctions(underlyingElement(this)) }
+
+ override string getAPrimaryQlClass() { result = "PureVirtualFunction" }
+}
+
+/**
+ * A const C++ member function [N4140 9.3.1/4]. A const function has the
+ * `const` specifier and does not modify the state of its class. For example
+ * the member function `day` in the following code:
+ * ```
+ * class MyClass {
+ * ...
+ *
+ * int day() const {
+ * return d;
+ * }
+ *
+ * ...
+ * };
+ * ```
+ */
+class ConstMemberFunction extends MemberFunction {
+ ConstMemberFunction() { this.hasSpecifier("const") }
+
+ override string getAPrimaryQlClass() { result = "ConstMemberFunction" }
+}
+
+/**
+ * A C++ constructor [N4140 12.1]. For example the function `MyClass` in the
+ * following code is a constructor:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass() {
+ * ...
+ * }
+ * };
+ * ```
+ */
+class Constructor extends MemberFunction {
+ Constructor() { functions(underlyingElement(this), _, 2) }
+
+ override string getAPrimaryQlClass() { result = "Constructor" }
+
+ /**
+ * Holds if this constructor serves as a default constructor.
+ *
+ * This holds for constructors with zero formal parameters. It also holds
+ * for constructors which have a non-zero number of formal parameters,
+ * provided that every parameter has a default value.
+ */
+ predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) }
+
+ /**
+ * Gets an entry in the constructor's initializer list, or a
+ * compiler-generated action which initializes a base class or member
+ * variable.
+ */
+ ConstructorInit getAnInitializer() { result = getInitializer(_) }
+
+ /**
+ * Gets an entry in the constructor's initializer list, or a
+ * compiler-generated action which initializes a base class or member
+ * variable. The index specifies the order in which the initializer is
+ * to be evaluated.
+ */
+ ConstructorInit getInitializer(int i) {
+ exprparents(unresolveElement(result), i, underlyingElement(this))
+ }
+}
+
+/**
+ * A function that defines an implicit conversion.
+ */
+abstract class ImplicitConversionFunction extends MemberFunction {
+ /** Gets the type this `ImplicitConversionFunction` takes as input. */
+ abstract Type getSourceType();
+
+ /** Gets the type this `ImplicitConversionFunction` converts to. */
+ abstract Type getDestType();
+}
+
+/**
+ * DEPRECATED: as of C++11 this class does not correspond perfectly with the
+ * language definition of a converting constructor.
+ *
+ * A C++ constructor that also defines an implicit conversion. For example the
+ * function `MyClass` in the following code is a `ConversionConstructor`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass(const MyOtherClass &from) {
+ * ...
+ * }
+ * };
+ * ```
+ */
+deprecated class ConversionConstructor extends Constructor, ImplicitConversionFunction {
+ ConversionConstructor() {
+ strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
+ not hasSpecifier("explicit")
+ }
+
+ override string getAPrimaryQlClass() {
+ not this instanceof CopyConstructor and
+ not this instanceof MoveConstructor and
+ result = "ConversionConstructor"
+ }
+
+ /** Gets the type this `ConversionConstructor` takes as input. */
+ override Type getSourceType() { result = this.getParameter(0).getType() }
+
+ /** Gets the type this `ConversionConstructor` is a constructor of. */
+ override Type getDestType() { result = this.getDeclaringType() }
+}
+
+private predicate hasCopySignature(MemberFunction f) {
+ f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType()
+}
+
+private predicate hasMoveSignature(MemberFunction f) {
+ f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType()
+}
+
+/**
+ * A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in
+ * the following code is a `CopyConstructor`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass(const MyClass &from) {
+ * ...
+ * }
+ * };
+ * ```
+ *
+ * As per the standard, a copy constructor of class `T` is a non-template
+ * constructor whose first parameter has type `T&`, `const T&`, `volatile
+ * T&`, or `const volatile T&`, and either there are no other parameters,
+ * or the rest of the parameters all have default values.
+ *
+ * For template classes, it can generally not be determined until instantiation
+ * whether a constructor is a copy constructor. For such classes, `CopyConstructor`
+ * over-approximates the set of copy constructors; if an under-approximation is
+ * desired instead, see the member predicate
+ * `mayNotBeCopyConstructorInInstantiation`.
+ */
+class CopyConstructor extends Constructor {
+ CopyConstructor() {
+ hasCopySignature(this) and
+ (
+ // The rest of the parameters all have default values
+ forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
+ or
+ // or this is a template class, in which case the default values have
+ // not been extracted even if they exist. In that case, we assume that
+ // there are default values present since that is the most common case
+ // in real-world code.
+ getDeclaringType() instanceof TemplateClass
+ ) and
+ not exists(getATemplateArgument())
+ }
+
+ override string getAPrimaryQlClass() { result = "CopyConstructor" }
+
+ /**
+ * Holds if we cannot determine that this constructor will become a copy
+ * constructor in all instantiations. Depending on template parameters of the
+ * enclosing class, this may become an ordinary constructor or a copy
+ * constructor.
+ */
+ predicate mayNotBeCopyConstructorInInstantiation() {
+ // In general, default arguments of template classes can only be
+ // type-checked for each template instantiation; if an argument in an
+ // instantiation fails to type-check then the corresponding parameter has
+ // no default argument in the instantiation.
+ getDeclaringType() instanceof TemplateClass and
+ getNumberOfParameters() > 1
+ }
+}
+
+/**
+ * A C++ move constructor [N4140 12.8]. For example the function `MyClass` in
+ * the following code is a `MoveConstructor`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass(MyClass &&from) {
+ * ...
+ * }
+ * };
+ * ```
+ *
+ * As per the standard, a move constructor of class `T` is a non-template
+ * constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`,
+ * or `const volatile T&&`, and either there are no other parameters, or
+ * the rest of the parameters all have default values.
+ *
+ * For template classes, it can generally not be determined until instantiation
+ * whether a constructor is a move constructor. For such classes, `MoveConstructor`
+ * over-approximates the set of move constructors; if an under-approximation is
+ * desired instead, see the member predicate
+ * `mayNotBeMoveConstructorInInstantiation`.
+ */
+class MoveConstructor extends Constructor {
+ MoveConstructor() {
+ hasMoveSignature(this) and
+ (
+ // The rest of the parameters all have default values
+ forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
+ or
+ // or this is a template class, in which case the default values have
+ // not been extracted even if they exist. In that case, we assume that
+ // there are default values present since that is the most common case
+ // in real-world code.
+ getDeclaringType() instanceof TemplateClass
+ ) and
+ not exists(getATemplateArgument())
+ }
+
+ override string getAPrimaryQlClass() { result = "MoveConstructor" }
+
+ /**
+ * Holds if we cannot determine that this constructor will become a move
+ * constructor in all instantiations. Depending on template parameters of the
+ * enclosing class, this may become an ordinary constructor or a move
+ * constructor.
+ */
+ predicate mayNotBeMoveConstructorInInstantiation() {
+ // In general, default arguments of template classes can only be
+ // type-checked for each template instantiation; if an argument in an
+ // instantiation fails to type-check then the corresponding parameter has
+ // no default argument in the instantiation.
+ getDeclaringType() instanceof TemplateClass and
+ getNumberOfParameters() > 1
+ }
+}
+
+/**
+ * A C++ constructor that takes no arguments ('default' constructor). This
+ * is the constructor that is invoked when no initializer is given. For
+ * example the function `MyClass` in the following code is a
+ * `NoArgConstructor`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass() {
+ * ...
+ * }
+ * };
+ * ```
+ */
+class NoArgConstructor extends Constructor {
+ NoArgConstructor() { this.getNumberOfParameters() = 0 }
+}
+
+/**
+ * A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the
+ * following code is a destructor:
+ * ```
+ * class MyClass {
+ * public:
+ * ~MyClass() {
+ * ...
+ * }
+ * };
+ * ```
+ */
+class Destructor extends MemberFunction {
+ Destructor() { functions(underlyingElement(this), _, 3) }
+
+ override string getAPrimaryQlClass() { result = "Destructor" }
+
+ /**
+ * Gets a compiler-generated action which destructs a base class or member
+ * variable.
+ */
+ DestructorDestruction getADestruction() { result = getDestruction(_) }
+
+ /**
+ * Gets a compiler-generated action which destructs a base class or member
+ * variable. The index specifies the order in which the destruction should
+ * be evaluated.
+ */
+ DestructorDestruction getDestruction(int i) {
+ exprparents(unresolveElement(result), i, underlyingElement(this))
+ }
+}
+
+/**
+ * A C++ conversion operator [N4140 12.3.2]. For example the function
+ * `operator int` in the following code is a `ConversionOperator`:
+ * ```
+ * class MyClass {
+ * public:
+ * operator int();
+ * };
+ * ```
+ */
+class ConversionOperator extends MemberFunction, ImplicitConversionFunction {
+ ConversionOperator() { functions(underlyingElement(this), _, 4) }
+
+ override string getAPrimaryQlClass() { result = "ConversionOperator" }
+
+ override Type getSourceType() { result = this.getDeclaringType() }
+
+ override Type getDestType() { result = this.getType() }
+}
+
+/**
+ * A C++ copy assignment operator [N4140 12.8]. For example the function
+ * `operator=` in the following code is a `CopyAssignmentOperator`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass &operator=(const MyClass &other);
+ * };
+ * ```
+ *
+ * As per the standard, a copy assignment operator of class `T` is a
+ * non-template non-static member function with the name `operator=` that
+ * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile
+ * T&`, or `const volatile T&`.
+ */
+class CopyAssignmentOperator extends Operator {
+ CopyAssignmentOperator() {
+ hasName("operator=") and
+ (
+ hasCopySignature(this)
+ or
+ // Unlike CopyConstructor, this member allows a non-reference
+ // parameter.
+ getParameter(0).getUnspecifiedType() = getDeclaringType()
+ ) and
+ not exists(this.getParameter(1)) and
+ not exists(getATemplateArgument())
+ }
+
+ override string getAPrimaryQlClass() { result = "CopyAssignmentOperator" }
+}
+
+/**
+ * A C++ move assignment operator [N4140 12.8]. For example the function
+ * `operator=` in the following code is a `MoveAssignmentOperator`:
+ * ```
+ * class MyClass {
+ * public:
+ * MyClass &operator=(MyClass &&other);
+ * };
+ * ```
+ *
+ * As per the standard, a move assignment operator of class `T` is a
+ * non-template non-static member function with the name `operator=` that
+ * takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`,
+ * or `const volatile T&&`.
+ */
+class MoveAssignmentOperator extends Operator {
+ MoveAssignmentOperator() {
+ hasName("operator=") and
+ hasMoveSignature(this) and
+ not exists(this.getParameter(1)) and
+ not exists(getATemplateArgument())
+ }
+
+ override string getAPrimaryQlClass() { result = "MoveAssignmentOperator" }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll b/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll
index eff2c9205bf9..042ee10700ad 100644
--- a/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll
+++ b/cpp/ql/src/semmle/code/cpp/NameQualifiers.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides classes for working with name qualifiers such as the `N::` in
+ * `N::f()`.
+ */
+
import cpp
/**
diff --git a/cpp/ql/src/semmle/code/cpp/Namespace.qll b/cpp/ql/src/semmle/code/cpp/Namespace.qll
index 9b9b12aaef06..6172c3af50ca 100644
--- a/cpp/ql/src/semmle/code/cpp/Namespace.qll
+++ b/cpp/ql/src/semmle/code/cpp/Namespace.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes for modeling namespaces, `using` directives and `using` declarations.
+ */
+
import semmle.code.cpp.Element
import semmle.code.cpp.Type
import semmle.code.cpp.metrics.MetricNamespace
@@ -127,7 +131,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
*/
Location getBodyLocation() { namespace_decls(underlyingElement(this), _, _, result) }
- override string getCanonicalQLClass() { result = "NamespaceDeclarationEntry" }
+ override string getAPrimaryQlClass() { result = "NamespaceDeclarationEntry" }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/NestedFields.qll b/cpp/ql/src/semmle/code/cpp/NestedFields.qll
index c4be8b8b9ffd..ce67719a7e21 100644
--- a/cpp/ql/src/semmle/code/cpp/NestedFields.qll
+++ b/cpp/ql/src/semmle/code/cpp/NestedFields.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides a class for reasoning about nested field accesses, for example
+ * the access `myLine.start.x`.
+ */
+
import cpp
/**
@@ -25,7 +30,7 @@ private Expr getUltimateQualifier(FieldAccess fa) {
}
/**
- * Accesses to nested fields.
+ * A nested field access, for example the access `myLine.start.x`.
*/
class NestedFieldAccess extends FieldAccess {
Expr ultimateQualifier;
@@ -35,6 +40,30 @@ class NestedFieldAccess extends FieldAccess {
getTarget() = getANestedField(ultimateQualifier.getType().stripType())
}
- /** Gets the ultimate qualifier of this nested field access. */
+ /**
+ * Gets the outermost qualifier of this nested field access. In the
+ * following example, the access to `myLine.start.x` has outermost qualifier
+ * `myLine`:
+ * ```
+ * struct Point
+ * {
+ * float x, y;
+ * };
+ *
+ * struct Line
+ * {
+ * Point start, end;
+ * };
+ *
+ * void init()
+ * {
+ * Line myLine;
+ *
+ * myLine.start.x = 0.0f;
+ *
+ * // ...
+ * }
+ * ```
+ */
Expr getUltimateQualifier() { result = ultimateQualifier }
}
diff --git a/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll b/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll
index 5c99a47e6747..17da273d5a73 100644
--- a/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll
+++ b/cpp/ql/src/semmle/code/cpp/ObjectiveC.qll
@@ -1,3 +1,7 @@
+/**
+ * DEPRECATED: Objective-C is no longer supported.
+ */
+
import semmle.code.cpp.Class
private import semmle.code.cpp.internal.ResolveClass
@@ -132,7 +136,7 @@ deprecated class ObjcTryStmt extends TryStmt {
* DEPRECATED: Objective-C is no longer supported.
* An Objective C `@finally` block.
*/
-deprecated class FinallyBlock extends Block {
+deprecated class FinallyBlock extends BlockStmt {
FinallyBlock() { none() }
/** Gets the try statement corresponding to this finally block. */
diff --git a/cpp/ql/src/semmle/code/cpp/Parameter.qll b/cpp/ql/src/semmle/code/cpp/Parameter.qll
index 1fbd8b0f071c..040b738591c4 100644
--- a/cpp/ql/src/semmle/code/cpp/Parameter.qll
+++ b/cpp/ql/src/semmle/code/cpp/Parameter.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides a class that models parameters to functions.
+ */
+
import semmle.code.cpp.Location
import semmle.code.cpp.Declaration
private import semmle.code.cpp.internal.ResolveClass
@@ -45,7 +49,7 @@ class Parameter extends LocalScopeVariable, @parameter {
result = "p#" + this.getIndex().toString()
}
- override string getCanonicalQLClass() { result = "Parameter" }
+ override string getAPrimaryQlClass() { result = "Parameter" }
/**
* Gets the name of this parameter, including it's type.
@@ -94,7 +98,7 @@ class Parameter extends LocalScopeVariable, @parameter {
* DEPRECATED: this method was used in a previous implementation of
* getName, but is no longer in use.
*/
- deprecated string getNameInBlock(Block b) {
+ deprecated string getNameInBlock(BlockStmt b) {
exists(ParameterDeclarationEntry pde |
pde.getFunctionDeclarationEntry().getBlock() = b and
this.getFunction().getBlock() = b and
@@ -123,7 +127,7 @@ class Parameter extends LocalScopeVariable, @parameter {
* Gets the catch block to which this parameter belongs, if it is a catch
* block parameter.
*/
- Block getCatchBlock() { params(underlyingElement(this), unresolveElement(result), _, _) }
+ BlockStmt getCatchBlock() { params(underlyingElement(this), unresolveElement(result), _, _) }
/**
* Gets the zero-based index of this parameter.
@@ -165,6 +169,7 @@ class Parameter extends LocalScopeVariable, @parameter {
class ParameterIndex extends int {
ParameterIndex() {
exists(Parameter p | this = p.getIndex()) or
- exists(Call c | exists(c.getArgument(this))) // permit indexing varargs
+ exists(Call c | exists(c.getArgument(this))) or // permit indexing varargs
+ this = -1 // used for `this`
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/Preprocessor.qll b/cpp/ql/src/semmle/code/cpp/Preprocessor.qll
index d0104900d605..a9609922b74d 100644
--- a/cpp/ql/src/semmle/code/cpp/Preprocessor.qll
+++ b/cpp/ql/src/semmle/code/cpp/Preprocessor.qll
@@ -33,11 +33,13 @@ class PreprocessorDirective extends Locatable, @preprocdirect {
}
}
+private class TPreprocessorBranchDirective = @ppd_branch or @ppd_else or @ppd_endif;
+
/**
* A C/C++ preprocessor branch related directive: `#if`, `#ifdef`,
* `#ifndef`, `#elif`, `#else` or `#endif`.
*/
-abstract class PreprocessorBranchDirective extends PreprocessorDirective {
+class PreprocessorBranchDirective extends PreprocessorDirective, TPreprocessorBranchDirective {
/**
* Gets the `#if`, `#ifdef` or `#ifndef` directive which matches this
* branching directive.
@@ -152,7 +154,7 @@ class PreprocessorIf extends PreprocessorBranch, @ppd_if {
class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef {
override string toString() { result = "#ifdef " + this.getHead() }
- override string getCanonicalQLClass() { result = "PreprocessorIfdef" }
+ override string getAPrimaryQlClass() { result = "PreprocessorIfdef" }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/PrintAST.qll b/cpp/ql/src/semmle/code/cpp/PrintAST.qll
index 9f69d5644575..fb5232b3008a 100644
--- a/cpp/ql/src/semmle/code/cpp/PrintAST.qll
+++ b/cpp/ql/src/semmle/code/cpp/PrintAST.qll
@@ -1,3 +1,11 @@
+/**
+ * Provides queries to pretty-print a C++ AST as a graph.
+ *
+ * By default, this will print the AST for all functions in the database. To change this behavior,
+ * extend `PrintASTConfiguration` and override `shouldPrintFunction` to hold for only the functions
+ * you wish to view the AST for.
+ */
+
import cpp
private import semmle.code.cpp.Print
@@ -7,6 +15,9 @@ private newtype TPrintASTConfiguration = MkPrintASTConfiguration()
* The query can extend this class to control which functions are printed.
*/
class PrintASTConfiguration extends TPrintASTConfiguration {
+ /**
+ * Gets a textual representation of this `PrintASTConfiguration`.
+ */
string toString() { result = "PrintASTConfiguration" }
/**
@@ -96,6 +107,9 @@ private newtype TPrintASTNode =
* A node in the output tree.
*/
class PrintASTNode extends TPrintASTNode {
+ /**
+ * Gets a textual representation of this node in the PrintAST output tree.
+ */
abstract string toString();
/**
@@ -155,7 +169,7 @@ class PrintASTNode extends TPrintASTNode {
* Retrieves the canonical QL class(es) for entity `el`
*/
private string qlClass(ElementBase el) {
- result = "[" + concat(el.getCanonicalQLClass(), ",") + "] "
+ result = "[" + concat(el.getAPrimaryQlClass(), ",") + "] "
// Alternative implementation -- do not delete. It is useful for QL class discovery.
//result = "["+ concat(el.getAQlClass(), ",") + "] "
}
@@ -208,6 +222,9 @@ class ExprNode extends ASTNode {
result = expr.getValueCategoryString()
}
+ /**
+ * Gets the value of this expression, if it is a constant.
+ */
string getValue() { result = expr.getValue() }
}
@@ -373,6 +390,9 @@ class ParametersNode extends PrintASTNode, TParametersNode {
override ASTNode getChild(int childIndex) { result.getAST() = func.getParameter(childIndex) }
+ /**
+ * Gets the `Function` for which this node represents the parameters.
+ */
final Function getFunction() { result = func }
}
@@ -392,6 +412,9 @@ class ConstructorInitializersNode extends PrintASTNode, TConstructorInitializers
result.getAST() = ctor.getInitializer(childIndex)
}
+ /**
+ * Gets the `Constructor` for which this node represents the initializer list.
+ */
final Constructor getConstructor() { result = ctor }
}
@@ -411,6 +434,9 @@ class DestructorDestructionsNode extends PrintASTNode, TDestructorDestructionsNo
result.getAST() = dtor.getDestruction(childIndex)
}
+ /**
+ * Gets the `Destructor` for which this node represents the destruction list.
+ */
final Destructor getDestructor() { result = dtor }
}
@@ -464,6 +490,9 @@ class FunctionNode extends ASTNode {
key = "semmle.order" and result = getOrder().toString()
}
+ /**
+ * Gets the `Function` this node represents.
+ */
final Function getFunction() { result = func }
}
@@ -499,11 +528,16 @@ class ArrayAggregateLiteralNode extends ExprNode {
}
}
+/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */
query predicate nodes(PrintASTNode node, string key, string value) {
node.shouldPrint() and
value = node.getProperty(key)
}
+/**
+ * Holds if `target` is a child of `source` in the AST, and property `key` of the edge has the
+ * given `value`.
+ */
query predicate edges(PrintASTNode source, PrintASTNode target, string key, string value) {
exists(int childIndex |
source.shouldPrint() and
@@ -517,6 +551,7 @@ query predicate edges(PrintASTNode source, PrintASTNode target, string key, stri
)
}
+/** Holds if property `key` of the graph has the given `value`. */
query predicate graphProperties(string key, string value) {
key = "semmle.graphKind" and value = "tree"
}
diff --git a/cpp/ql/src/semmle/code/cpp/Specifier.qll b/cpp/ql/src/semmle/code/cpp/Specifier.qll
index f58f060623dd..3d68fb374f12 100644
--- a/cpp/ql/src/semmle/code/cpp/Specifier.qll
+++ b/cpp/ql/src/semmle/code/cpp/Specifier.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes for modeling specifiers and attributes.
+ */
+
import semmle.code.cpp.Element
private import semmle.code.cpp.internal.ResolveClass
@@ -12,7 +16,7 @@ class Specifier extends Element, @specifier {
result instanceof UnknownDefaultLocation
}
- override string getCanonicalQLClass() { result = "Specifier" }
+ override string getAPrimaryQlClass() { result = "Specifier" }
/** Gets the name of this specifier. */
string getName() { specifiers(underlyingElement(this), result) }
@@ -33,7 +37,7 @@ class FunctionSpecifier extends Specifier {
this.hasName("explicit")
}
- override string getCanonicalQLClass() { result = "FunctionSpecifier)" }
+ override string getAPrimaryQlClass() { result = "FunctionSpecifier)" }
}
/**
@@ -49,7 +53,7 @@ class StorageClassSpecifier extends Specifier {
this.hasName("mutable")
}
- override string getCanonicalQLClass() { result = "StorageClassSpecifier" }
+ override string getAPrimaryQlClass() { result = "StorageClassSpecifier" }
}
/**
@@ -104,7 +108,7 @@ class AccessSpecifier extends Specifier {
)
}
- override string getCanonicalQLClass() { result = "AccessSpecifier" }
+ override string getAPrimaryQlClass() { result = "AccessSpecifier" }
}
/**
@@ -234,7 +238,7 @@ class FormatAttribute extends GnuAttribute {
)
}
- override string getCanonicalQLClass() { result = "FormatAttribute" }
+ override string getAPrimaryQlClass() { result = "FormatAttribute" }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/Struct.qll b/cpp/ql/src/semmle/code/cpp/Struct.qll
index 6b95cdba1bd5..50a208894b43 100644
--- a/cpp/ql/src/semmle/code/cpp/Struct.qll
+++ b/cpp/ql/src/semmle/code/cpp/Struct.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes for modeling `struct`s.
+ */
+
import semmle.code.cpp.Type
import semmle.code.cpp.Class
@@ -18,7 +22,7 @@ import semmle.code.cpp.Class
class Struct extends Class {
Struct() { usertypes(underlyingElement(this), _, 1) or usertypes(underlyingElement(this), _, 3) }
- override string getCanonicalQLClass() { result = "Struct" }
+ override string getAPrimaryQlClass() { result = "Struct" }
override string explain() { result = "struct " + this.getName() }
@@ -39,9 +43,7 @@ class Struct extends Class {
class LocalStruct extends Struct {
LocalStruct() { isLocal() }
- override string getCanonicalQLClass() {
- not this instanceof LocalUnion and result = "LocalStruct"
- }
+ override string getAPrimaryQlClass() { not this instanceof LocalUnion and result = "LocalStruct" }
}
/**
@@ -58,7 +60,7 @@ class LocalStruct extends Struct {
class NestedStruct extends Struct {
NestedStruct() { this.isMember() }
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not this instanceof NestedUnion and result = "NestedStruct"
}
diff --git a/cpp/ql/src/semmle/code/cpp/TestFile.qll b/cpp/ql/src/semmle/code/cpp/TestFile.qll
index 4348bddf59c0..b9e3fe3a614f 100644
--- a/cpp/ql/src/semmle/code/cpp/TestFile.qll
+++ b/cpp/ql/src/semmle/code/cpp/TestFile.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides classes for identifying files that contain test cases. It is often
+ * desirable to exclude these files from analysis.
+ */
+
import semmle.code.cpp.File
/**
diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll
index 55eb4f27d3dc..8273d0440d5c 100644
--- a/cpp/ql/src/semmle/code/cpp/Type.qll
+++ b/cpp/ql/src/semmle/code/cpp/Type.qll
@@ -1,5 +1,8 @@
+/**
+ * Provides a hierarchy of classes for modeling C/C++ types.
+ */
+
import semmle.code.cpp.Element
-import semmle.code.cpp.Member
import semmle.code.cpp.Function
private import semmle.code.cpp.internal.ResolveClass
@@ -322,7 +325,7 @@ class BuiltInType extends Type, @builtintype {
class ErroneousType extends BuiltInType {
ErroneousType() { builtintypes(underlyingElement(this), _, 1, _, _, _) }
- override string getCanonicalQLClass() { result = "ErroneousType" }
+ override string getAPrimaryQlClass() { result = "ErroneousType" }
}
/**
@@ -342,7 +345,7 @@ class ErroneousType extends BuiltInType {
class UnknownType extends BuiltInType {
UnknownType() { builtintypes(underlyingElement(this), _, 2, _, _, _) }
- override string getCanonicalQLClass() { result = "UnknownType" }
+ override string getAPrimaryQlClass() { result = "UnknownType" }
}
private predicate isArithmeticType(@builtintype type, int kind) {
@@ -361,7 +364,7 @@ private predicate isArithmeticType(@builtintype type, int kind) {
class ArithmeticType extends BuiltInType {
ArithmeticType() { isArithmeticType(underlyingElement(this), _) }
- override string getCanonicalQLClass() { result = "ArithmeticType" }
+ override string getAPrimaryQlClass() { result = "ArithmeticType" }
}
private predicate isIntegralType(@builtintype type, int kind) {
@@ -561,7 +564,7 @@ class IntegralType extends ArithmeticType, IntegralOrEnumType {
class BoolType extends IntegralType {
BoolType() { builtintypes(underlyingElement(this), _, 4, _, _, _) }
- override string getCanonicalQLClass() { result = "BoolType" }
+ override string getAPrimaryQlClass() { result = "BoolType" }
}
/**
@@ -586,7 +589,7 @@ abstract class CharType extends IntegralType { }
class PlainCharType extends CharType {
PlainCharType() { builtintypes(underlyingElement(this), _, 5, _, _, _) }
- override string getCanonicalQLClass() { result = "PlainCharType" }
+ override string getAPrimaryQlClass() { result = "PlainCharType" }
}
/**
@@ -599,7 +602,7 @@ class PlainCharType extends CharType {
class UnsignedCharType extends CharType {
UnsignedCharType() { builtintypes(underlyingElement(this), _, 6, _, _, _) }
- override string getCanonicalQLClass() { result = "UnsignedCharType" }
+ override string getAPrimaryQlClass() { result = "UnsignedCharType" }
}
/**
@@ -612,7 +615,7 @@ class UnsignedCharType extends CharType {
class SignedCharType extends CharType {
SignedCharType() { builtintypes(underlyingElement(this), _, 7, _, _, _) }
- override string getCanonicalQLClass() { result = "SignedCharType" }
+ override string getAPrimaryQlClass() { result = "SignedCharType" }
}
/**
@@ -629,7 +632,7 @@ class ShortType extends IntegralType {
builtintypes(underlyingElement(this), _, 10, _, _, _)
}
- override string getCanonicalQLClass() { result = "ShortType" }
+ override string getAPrimaryQlClass() { result = "ShortType" }
}
/**
@@ -646,7 +649,7 @@ class IntType extends IntegralType {
builtintypes(underlyingElement(this), _, 13, _, _, _)
}
- override string getCanonicalQLClass() { result = "IntType" }
+ override string getAPrimaryQlClass() { result = "IntType" }
}
/**
@@ -663,7 +666,7 @@ class LongType extends IntegralType {
builtintypes(underlyingElement(this), _, 16, _, _, _)
}
- override string getCanonicalQLClass() { result = "LongType" }
+ override string getAPrimaryQlClass() { result = "LongType" }
}
/**
@@ -680,7 +683,7 @@ class LongLongType extends IntegralType {
builtintypes(underlyingElement(this), _, 19, _, _, _)
}
- override string getCanonicalQLClass() { result = "LongLongType" }
+ override string getAPrimaryQlClass() { result = "LongLongType" }
}
/**
@@ -698,7 +701,7 @@ class Int128Type extends IntegralType {
builtintypes(underlyingElement(this), _, 37, _, _, _)
}
- override string getCanonicalQLClass() { result = "Int128Type" }
+ override string getAPrimaryQlClass() { result = "Int128Type" }
}
private newtype TTypeDomain =
@@ -894,7 +897,7 @@ class DecimalFloatingPointType extends FloatingPointType {
class FloatType extends RealNumberType, BinaryFloatingPointType {
FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) }
- override string getCanonicalQLClass() { result = "FloatType" }
+ override string getAPrimaryQlClass() { result = "FloatType" }
}
/**
@@ -906,7 +909,7 @@ class FloatType extends RealNumberType, BinaryFloatingPointType {
class DoubleType extends RealNumberType, BinaryFloatingPointType {
DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) }
- override string getCanonicalQLClass() { result = "DoubleType" }
+ override string getAPrimaryQlClass() { result = "DoubleType" }
}
/**
@@ -918,7 +921,7 @@ class DoubleType extends RealNumberType, BinaryFloatingPointType {
class LongDoubleType extends RealNumberType, BinaryFloatingPointType {
LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) }
- override string getCanonicalQLClass() { result = "LongDoubleType" }
+ override string getAPrimaryQlClass() { result = "LongDoubleType" }
}
/**
@@ -930,7 +933,7 @@ class LongDoubleType extends RealNumberType, BinaryFloatingPointType {
class Float128Type extends RealNumberType, BinaryFloatingPointType {
Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) }
- override string getCanonicalQLClass() { result = "Float128Type" }
+ override string getAPrimaryQlClass() { result = "Float128Type" }
}
/**
@@ -942,7 +945,7 @@ class Float128Type extends RealNumberType, BinaryFloatingPointType {
class Decimal32Type extends RealNumberType, DecimalFloatingPointType {
Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) }
- override string getCanonicalQLClass() { result = "Decimal32Type" }
+ override string getAPrimaryQlClass() { result = "Decimal32Type" }
}
/**
@@ -954,7 +957,7 @@ class Decimal32Type extends RealNumberType, DecimalFloatingPointType {
class Decimal64Type extends RealNumberType, DecimalFloatingPointType {
Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) }
- override string getCanonicalQLClass() { result = "Decimal64Type" }
+ override string getAPrimaryQlClass() { result = "Decimal64Type" }
}
/**
@@ -966,7 +969,7 @@ class Decimal64Type extends RealNumberType, DecimalFloatingPointType {
class Decimal128Type extends RealNumberType, DecimalFloatingPointType {
Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) }
- override string getCanonicalQLClass() { result = "Decimal128Type" }
+ override string getAPrimaryQlClass() { result = "Decimal128Type" }
}
/**
@@ -978,7 +981,7 @@ class Decimal128Type extends RealNumberType, DecimalFloatingPointType {
class VoidType extends BuiltInType {
VoidType() { builtintypes(underlyingElement(this), _, 3, _, _, _) }
- override string getCanonicalQLClass() { result = "VoidType" }
+ override string getAPrimaryQlClass() { result = "VoidType" }
}
/**
@@ -994,7 +997,7 @@ class VoidType extends BuiltInType {
class WideCharType extends IntegralType {
WideCharType() { builtintypes(underlyingElement(this), _, 33, _, _, _) }
- override string getCanonicalQLClass() { result = "WideCharType" }
+ override string getAPrimaryQlClass() { result = "WideCharType" }
}
/**
@@ -1006,7 +1009,7 @@ class WideCharType extends IntegralType {
class Char8Type extends IntegralType {
Char8Type() { builtintypes(underlyingElement(this), _, 51, _, _, _) }
- override string getCanonicalQLClass() { result = "Char8Type" }
+ override string getAPrimaryQlClass() { result = "Char8Type" }
}
/**
@@ -1018,7 +1021,7 @@ class Char8Type extends IntegralType {
class Char16Type extends IntegralType {
Char16Type() { builtintypes(underlyingElement(this), _, 43, _, _, _) }
- override string getCanonicalQLClass() { result = "Char16Type" }
+ override string getAPrimaryQlClass() { result = "Char16Type" }
}
/**
@@ -1030,7 +1033,7 @@ class Char16Type extends IntegralType {
class Char32Type extends IntegralType {
Char32Type() { builtintypes(underlyingElement(this), _, 44, _, _, _) }
- override string getCanonicalQLClass() { result = "Char32Type" }
+ override string getAPrimaryQlClass() { result = "Char32Type" }
}
/**
@@ -1045,7 +1048,7 @@ class Char32Type extends IntegralType {
class NullPointerType extends BuiltInType {
NullPointerType() { builtintypes(underlyingElement(this), _, 34, _, _, _) }
- override string getCanonicalQLClass() { result = "NullPointerType" }
+ override string getAPrimaryQlClass() { result = "NullPointerType" }
}
/**
@@ -1080,22 +1083,46 @@ class DerivedType extends Type, @derivedtype {
override Type stripType() { result = getBaseType().stripType() }
- predicate isAutoReleasing() {
+ /**
+ * Holds if this type has the `__autoreleasing` specifier or if it points to
+ * a type with the `__autoreleasing` specifier.
+ *
+ * DEPRECATED: use `hasSpecifier` directly instead.
+ */
+ deprecated predicate isAutoReleasing() {
this.hasSpecifier("__autoreleasing") or
this.(PointerType).getBaseType().hasSpecifier("__autoreleasing")
}
- predicate isStrong() {
+ /**
+ * Holds if this type has the `__strong` specifier or if it points to
+ * a type with the `__strong` specifier.
+ *
+ * DEPRECATED: use `hasSpecifier` directly instead.
+ */
+ deprecated predicate isStrong() {
this.hasSpecifier("__strong") or
this.(PointerType).getBaseType().hasSpecifier("__strong")
}
- predicate isUnsafeRetained() {
+ /**
+ * Holds if this type has the `__unsafe_unretained` specifier or if it points
+ * to a type with the `__unsafe_unretained` specifier.
+ *
+ * DEPRECATED: use `hasSpecifier` directly instead.
+ */
+ deprecated predicate isUnsafeRetained() {
this.hasSpecifier("__unsafe_unretained") or
this.(PointerType).getBaseType().hasSpecifier("__unsafe_unretained")
}
- predicate isWeak() {
+ /**
+ * Holds if this type has the `__weak` specifier or if it points to
+ * a type with the `__weak` specifier.
+ *
+ * DEPRECATED: use `hasSpecifier` directly instead.
+ */
+ deprecated predicate isWeak() {
this.hasSpecifier("__weak") or
this.(PointerType).getBaseType().hasSpecifier("__weak")
}
@@ -1109,7 +1136,7 @@ class DerivedType extends Type, @derivedtype {
* ```
*/
class Decltype extends Type, @decltype {
- override string getCanonicalQLClass() { result = "Decltype" }
+ override string getAPrimaryQlClass() { result = "Decltype" }
/**
* The expression whose type is being obtained by this decltype.
@@ -1182,7 +1209,7 @@ class Decltype extends Type, @decltype {
class PointerType extends DerivedType {
PointerType() { derivedtypes(underlyingElement(this), _, 1, _) }
- override string getCanonicalQLClass() { result = "PointerType" }
+ override string getAPrimaryQlClass() { result = "PointerType" }
override int getPointerIndirectionLevel() {
result = 1 + this.getBaseType().getPointerIndirectionLevel()
@@ -1208,7 +1235,7 @@ class ReferenceType extends DerivedType {
derivedtypes(underlyingElement(this), _, 2, _) or derivedtypes(underlyingElement(this), _, 8, _)
}
- override string getCanonicalQLClass() { result = "ReferenceType" }
+ override string getAPrimaryQlClass() { result = "ReferenceType" }
override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() }
@@ -1235,7 +1262,7 @@ class ReferenceType extends DerivedType {
class LValueReferenceType extends ReferenceType {
LValueReferenceType() { derivedtypes(underlyingElement(this), _, 2, _) }
- override string getCanonicalQLClass() { result = "LValueReferenceType" }
+ override string getAPrimaryQlClass() { result = "LValueReferenceType" }
}
/**
@@ -1251,7 +1278,7 @@ class LValueReferenceType extends ReferenceType {
class RValueReferenceType extends ReferenceType {
RValueReferenceType() { derivedtypes(underlyingElement(this), _, 8, _) }
- override string getCanonicalQLClass() { result = "RValueReferenceType" }
+ override string getAPrimaryQlClass() { result = "RValueReferenceType" }
override string explain() { result = "rvalue " + super.explain() }
}
@@ -1266,7 +1293,7 @@ class RValueReferenceType extends ReferenceType {
class SpecifiedType extends DerivedType {
SpecifiedType() { derivedtypes(underlyingElement(this), _, 3, _) }
- override string getCanonicalQLClass() { result = "SpecifiedType" }
+ override string getAPrimaryQlClass() { result = "SpecifiedType" }
override int getSize() { result = this.getBaseType().getSize() }
@@ -1314,8 +1341,12 @@ class SpecifiedType extends DerivedType {
class ArrayType extends DerivedType {
ArrayType() { derivedtypes(underlyingElement(this), _, 4, _) }
- override string getCanonicalQLClass() { result = "ArrayType" }
+ override string getAPrimaryQlClass() { result = "ArrayType" }
+ /**
+ * Holds if this array is declared to be of a constant size. See
+ * `getArraySize` and `getByteSize` to get the size of the array.
+ */
predicate hasArraySize() { arraysizes(underlyingElement(this), _, _, _) }
/**
@@ -1381,7 +1412,7 @@ class GNUVectorType extends DerivedType {
*/
int getNumElements() { arraysizes(underlyingElement(this), result, _, _) }
- override string getCanonicalQLClass() { result = "GNUVectorType" }
+ override string getAPrimaryQlClass() { result = "GNUVectorType" }
/**
* Gets the size, in bytes, of this vector type.
@@ -1412,7 +1443,7 @@ class GNUVectorType extends DerivedType {
class FunctionPointerType extends FunctionPointerIshType {
FunctionPointerType() { derivedtypes(underlyingElement(this), _, 6, _) }
- override string getCanonicalQLClass() { result = "FunctionPointerType" }
+ override string getAPrimaryQlClass() { result = "FunctionPointerType" }
override int getPointerIndirectionLevel() { result = 1 }
@@ -1430,7 +1461,7 @@ class FunctionPointerType extends FunctionPointerIshType {
class FunctionReferenceType extends FunctionPointerIshType {
FunctionReferenceType() { derivedtypes(underlyingElement(this), _, 7, _) }
- override string getCanonicalQLClass() { result = "FunctionReferenceType" }
+ override string getAPrimaryQlClass() { result = "FunctionReferenceType" }
override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() }
@@ -1519,7 +1550,7 @@ class PointerToMemberType extends Type, @ptrtomember {
/** a printable representation of this named element */
override string toString() { result = this.getName() }
- override string getCanonicalQLClass() { result = "PointerToMemberType" }
+ override string getAPrimaryQlClass() { result = "PointerToMemberType" }
/** the name of this type */
override string getName() { result = "..:: *" }
@@ -1564,16 +1595,25 @@ class RoutineType extends Type, @routinetype {
/** a printable representation of this named element */
override string toString() { result = this.getName() }
- override string getCanonicalQLClass() { result = "RoutineType" }
+ override string getAPrimaryQlClass() { result = "RoutineType" }
override string getName() { result = "..()(..)" }
+ /**
+ * Gets the type of the `n`th parameter to this routine.
+ */
Type getParameterType(int n) {
routinetypeargs(underlyingElement(this), n, unresolveElement(result))
}
+ /**
+ * Gets the type of a parameter to this routine.
+ */
Type getAParameterType() { routinetypeargs(underlyingElement(this), _, unresolveElement(result)) }
+ /**
+ * Gets the return type of this routine.
+ */
Type getReturnType() { routinetypes(underlyingElement(this), unresolveElement(result)) }
override string explain() {
@@ -1632,7 +1672,7 @@ class TemplateParameter extends UserType {
usertypes(underlyingElement(this), _, 7) or usertypes(underlyingElement(this), _, 8)
}
- override string getCanonicalQLClass() { result = "TemplateParameter" }
+ override string getAPrimaryQlClass() { result = "TemplateParameter" }
override predicate involvesTemplateParameter() { any() }
}
@@ -1650,7 +1690,7 @@ class TemplateParameter extends UserType {
class TemplateTemplateParameter extends TemplateParameter {
TemplateTemplateParameter() { usertypes(underlyingElement(this), _, 8) }
- override string getCanonicalQLClass() { result = "TemplateTemplateParameter" }
+ override string getAPrimaryQlClass() { result = "TemplateTemplateParameter" }
}
/**
@@ -1662,7 +1702,7 @@ class TemplateTemplateParameter extends TemplateParameter {
class AutoType extends TemplateParameter {
AutoType() { usertypes(underlyingElement(this), "auto", 7) }
- override string getCanonicalQLClass() { result = "AutoType" }
+ override string getAPrimaryQlClass() { result = "AutoType" }
override Location getLocation() {
suppressUnusedThis(this) and
@@ -1698,7 +1738,7 @@ private predicate suppressUnusedThis(Type t) { any() }
class TypeMention extends Locatable, @type_mention {
override string toString() { result = "type mention" }
- override string getCanonicalQLClass() { result = "TypeMention" }
+ override string getAPrimaryQlClass() { result = "TypeMention" }
/**
* Gets the type being referenced by this type mention.
diff --git a/cpp/ql/src/semmle/code/cpp/TypedefType.qll b/cpp/ql/src/semmle/code/cpp/TypedefType.qll
index 504333aeedcc..aaf452ce4bb2 100644
--- a/cpp/ql/src/semmle/code/cpp/TypedefType.qll
+++ b/cpp/ql/src/semmle/code/cpp/TypedefType.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes for modeling typedefs and type aliases.
+ */
+
import semmle.code.cpp.Type
private import semmle.code.cpp.internal.ResolveClass
@@ -55,7 +59,7 @@ class TypedefType extends UserType {
class CTypedefType extends TypedefType {
CTypedefType() { usertypes(underlyingElement(this), _, 5) }
- override string getCanonicalQLClass() { result = "CTypedefType" }
+ override string getAPrimaryQlClass() { result = "CTypedefType" }
override string explain() {
result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
@@ -71,7 +75,7 @@ class CTypedefType extends TypedefType {
class UsingAliasTypedefType extends TypedefType {
UsingAliasTypedefType() { usertypes(underlyingElement(this), _, 14) }
- override string getCanonicalQLClass() { result = "UsingAliasTypedefType" }
+ override string getAPrimaryQlClass() { result = "UsingAliasTypedefType" }
override string explain() {
result = "using {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
@@ -88,7 +92,7 @@ class UsingAliasTypedefType extends TypedefType {
class LocalTypedefType extends TypedefType {
LocalTypedefType() { isLocal() }
- override string getCanonicalQLClass() { result = "LocalTypedefType" }
+ override string getAPrimaryQlClass() { result = "LocalTypedefType" }
}
/**
@@ -101,7 +105,7 @@ class LocalTypedefType extends TypedefType {
class NestedTypedefType extends TypedefType {
NestedTypedefType() { this.isMember() }
- override string getCanonicalQLClass() { result = "NestedTypedefType" }
+ override string getAPrimaryQlClass() { result = "NestedTypedefType" }
/**
* DEPRECATED: use `.hasSpecifier("private")` instead.
diff --git a/cpp/ql/src/semmle/code/cpp/Union.qll b/cpp/ql/src/semmle/code/cpp/Union.qll
index f1c033438ba5..6dcb2f0796c8 100644
--- a/cpp/ql/src/semmle/code/cpp/Union.qll
+++ b/cpp/ql/src/semmle/code/cpp/Union.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes for modeling `union`s.
+ */
+
import semmle.code.cpp.Type
import semmle.code.cpp.Struct
@@ -13,7 +17,7 @@ import semmle.code.cpp.Struct
class Union extends Struct {
Union() { usertypes(underlyingElement(this), _, 3) }
- override string getCanonicalQLClass() { result = "Union" }
+ override string getAPrimaryQlClass() { result = "Union" }
override string explain() { result = "union " + this.getName() }
@@ -35,7 +39,7 @@ class Union extends Struct {
class LocalUnion extends Union {
LocalUnion() { isLocal() }
- override string getCanonicalQLClass() { result = "LocalUnion" }
+ override string getAPrimaryQlClass() { result = "LocalUnion" }
}
/**
@@ -53,7 +57,7 @@ class LocalUnion extends Union {
class NestedUnion extends Union {
NestedUnion() { this.isMember() }
- override string getCanonicalQLClass() { result = "NestedUnion" }
+ override string getAPrimaryQlClass() { result = "NestedUnion" }
/** Holds if this member is private. */
predicate isPrivate() { this.hasSpecifier("private") }
diff --git a/cpp/ql/src/semmle/code/cpp/UserType.qll b/cpp/ql/src/semmle/code/cpp/UserType.qll
index 4484cde84deb..2ab0603f06cc 100644
--- a/cpp/ql/src/semmle/code/cpp/UserType.qll
+++ b/cpp/ql/src/semmle/code/cpp/UserType.qll
@@ -1,6 +1,10 @@
+/**
+ * Provides classes for modeling user-defined types such as classes, typedefs
+ * and enums.
+ */
+
import semmle.code.cpp.Declaration
import semmle.code.cpp.Type
-import semmle.code.cpp.Member
import semmle.code.cpp.Function
private import semmle.code.cpp.internal.ResolveClass
@@ -20,7 +24,7 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
*/
override string getName() { usertypes(underlyingElement(this), result, _) }
- override string getCanonicalQLClass() { result = "UserType" }
+ override string getAPrimaryQlClass() { result = "UserType" }
/**
* Gets the simple name of this type, without any template parameters. For example
@@ -84,6 +88,9 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
* type exactly - but this is not apparent from its subclasses
*/
+ /**
+ * Gets a child declaration within this user-defined type.
+ */
Declaration getADeclaration() { none() }
override string explain() { result = this.getName() }
@@ -104,7 +111,7 @@ class TypeDeclarationEntry extends DeclarationEntry, @type_decl {
override string getName() { result = getType().getName() }
- override string getCanonicalQLClass() { result = "TypeDeclarationEntry" }
+ override string getAPrimaryQlClass() { result = "TypeDeclarationEntry" }
/**
* The type which is being declared or defined.
diff --git a/cpp/ql/src/semmle/code/cpp/Variable.qll b/cpp/ql/src/semmle/code/cpp/Variable.qll
index 54f39ab2bb6f..527d8879b96d 100644
--- a/cpp/ql/src/semmle/code/cpp/Variable.qll
+++ b/cpp/ql/src/semmle/code/cpp/Variable.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes for modeling variables and their declarations.
+ */
+
import semmle.code.cpp.Element
import semmle.code.cpp.exprs.Access
import semmle.code.cpp.Initializer
@@ -28,7 +32,7 @@ private import semmle.code.cpp.internal.ResolveClass
* can have multiple declarations.
*/
class Variable extends Declaration, @variable {
- override string getCanonicalQLClass() { result = "Variable" }
+ override string getAPrimaryQlClass() { result = "Variable" }
/** Gets the initializer of this variable, if any. */
Initializer getInitializer() { result.getDeclaration() = this }
@@ -140,6 +144,11 @@ class Variable extends Declaration, @variable {
*/
predicate isConstexpr() { this.hasSpecifier("is_constexpr") }
+ /**
+ * Holds if this variable is declared `constinit`.
+ */
+ predicate isConstinit() { this.hasSpecifier("declared_constinit") }
+
/**
* Holds if this variable is `thread_local`.
*/
@@ -186,7 +195,7 @@ class Variable extends Declaration, @variable {
class VariableDeclarationEntry extends DeclarationEntry, @var_decl {
override Variable getDeclaration() { result = getVariable() }
- override string getCanonicalQLClass() { result = "VariableDeclarationEntry" }
+ override string getAPrimaryQlClass() { result = "VariableDeclarationEntry" }
/**
* Gets the variable which is being declared or defined.
@@ -245,7 +254,7 @@ class VariableDeclarationEntry extends DeclarationEntry, @var_decl {
class ParameterDeclarationEntry extends VariableDeclarationEntry {
ParameterDeclarationEntry() { param_decl_bind(underlyingElement(this), _, _) }
- override string getCanonicalQLClass() { result = "ParameterDeclarationEntry" }
+ override string getAPrimaryQlClass() { result = "ParameterDeclarationEntry" }
/**
* Gets the function declaration or definition which this parameter
@@ -321,7 +330,7 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
*/
class LocalScopeVariable extends Variable, @localscopevariable {
/** Gets the function to which this variable belongs. */
- /*abstract*/ Function getFunction() { none() }
+ Function getFunction() { none() } // overridden in subclasses
}
/**
@@ -359,7 +368,7 @@ class StackVariable extends LocalScopeVariable {
* A local variable can be declared by a `DeclStmt` or a `ConditionDeclExpr`.
*/
class LocalVariable extends LocalScopeVariable, @localvariable {
- override string getCanonicalQLClass() { result = "LocalVariable" }
+ override string getAPrimaryQlClass() { result = "LocalVariable" }
override string getName() { localvariables(underlyingElement(this), _, result) }
@@ -460,7 +469,7 @@ class NamespaceVariable extends GlobalOrNamespaceVariable {
exists(Namespace n | namespacembrs(unresolveElement(n), underlyingElement(this)))
}
- override string getCanonicalQLClass() { result = "NamespaceVariable" }
+ override string getAPrimaryQlClass() { result = "NamespaceVariable" }
}
/**
@@ -481,7 +490,7 @@ class NamespaceVariable extends GlobalOrNamespaceVariable {
class GlobalVariable extends GlobalOrNamespaceVariable {
GlobalVariable() { not this instanceof NamespaceVariable }
- override string getCanonicalQLClass() { result = "GlobalVariable" }
+ override string getAPrimaryQlClass() { result = "GlobalVariable" }
}
/**
@@ -501,7 +510,7 @@ class GlobalVariable extends GlobalOrNamespaceVariable {
class MemberVariable extends Variable, @membervariable {
MemberVariable() { this.isMember() }
- override string getCanonicalQLClass() { result = "MemberVariable" }
+ override string getAPrimaryQlClass() { result = "MemberVariable" }
/** Holds if this member is private. */
predicate isPrivate() { this.hasSpecifier("private") }
@@ -578,7 +587,7 @@ class TemplateVariable extends Variable {
* float a;
* }
*
- * template
+ * template
* void myTemplateFunction() {
* T b;
* }
diff --git a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll
index f584a8e48027..253a27670774 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll
@@ -6,7 +6,7 @@ import semmle.code.cpp.Type
class CharPointerType extends PointerType {
CharPointerType() { this.getBaseType() instanceof CharType }
- override string getCanonicalQLClass() { result = "CharPointerType" }
+ override string getAPrimaryQlClass() { result = "CharPointerType" }
}
/**
@@ -15,7 +15,7 @@ class CharPointerType extends PointerType {
class IntPointerType extends PointerType {
IntPointerType() { this.getBaseType() instanceof IntType }
- override string getCanonicalQLClass() { result = "IntPointerType" }
+ override string getAPrimaryQlClass() { result = "IntPointerType" }
}
/**
@@ -24,7 +24,7 @@ class IntPointerType extends PointerType {
class VoidPointerType extends PointerType {
VoidPointerType() { this.getBaseType() instanceof VoidType }
- override string getCanonicalQLClass() { result = "VoidPointerType" }
+ override string getAPrimaryQlClass() { result = "VoidPointerType" }
}
/**
@@ -36,7 +36,7 @@ class Size_t extends Type {
this.hasName("size_t")
}
- override string getCanonicalQLClass() { result = "Size_t" }
+ override string getAPrimaryQlClass() { result = "Size_t" }
}
/**
@@ -48,7 +48,7 @@ class Ssize_t extends Type {
this.hasName("ssize_t")
}
- override string getCanonicalQLClass() { result = "Ssize_t" }
+ override string getAPrimaryQlClass() { result = "Ssize_t" }
}
/**
@@ -60,7 +60,7 @@ class Ptrdiff_t extends Type {
this.hasName("ptrdiff_t")
}
- override string getCanonicalQLClass() { result = "Ptrdiff_t" }
+ override string getAPrimaryQlClass() { result = "Ptrdiff_t" }
}
/**
@@ -72,7 +72,7 @@ class Intmax_t extends Type {
this.hasName("intmax_t")
}
- override string getCanonicalQLClass() { result = "Intmax_t" }
+ override string getAPrimaryQlClass() { result = "Intmax_t" }
}
/**
@@ -84,7 +84,7 @@ class Uintmax_t extends Type {
this.hasName("uintmax_t")
}
- override string getCanonicalQLClass() { result = "Uintmax_t" }
+ override string getAPrimaryQlClass() { result = "Uintmax_t" }
}
/**
@@ -100,7 +100,7 @@ class Wchar_t extends Type {
this.hasName("wchar_t")
}
- override string getCanonicalQLClass() { result = "Wchar_t" }
+ override string getAPrimaryQlClass() { result = "Wchar_t" }
}
/**
@@ -176,5 +176,5 @@ class MicrosoftInt64Type extends IntegralType {
class BuiltInVarArgsList extends Type {
BuiltInVarArgsList() { this.hasName("__builtin_va_list") }
- override string getCanonicalQLClass() { result = "BuiltInVarArgsList" }
+ override string getAPrimaryQlClass() { result = "BuiltInVarArgsList" }
}
diff --git a/cpp/ql/src/semmle/code/cpp/commons/Environment.qll b/cpp/ql/src/semmle/code/cpp/commons/Environment.qll
index f3f1759dd5c0..3da19977f338 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Environment.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Environment.qll
@@ -29,7 +29,7 @@ private predicate readsEnvironment(Expr read, string sourceDescription) {
exists(FunctionCall call, string name |
read = call and
call.getTarget().hasGlobalOrStdName(name) and
- (name = "getenv" or name = "secure_getenv" or name = "_wgetenv") and
+ name = ["getenv", "secure_getenv", "_wgetenv"] and
sourceDescription = name
)
}
diff --git a/cpp/ql/src/semmle/code/cpp/commons/File.qll b/cpp/ql/src/semmle/code/cpp/commons/File.qll
index 5808d704e385..acc5893d8106 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/File.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/File.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides predicates for identifying function calls that open or close a file.
+ */
+
import cpp
/**
diff --git a/cpp/ql/src/semmle/code/cpp/commons/Printf.qll b/cpp/ql/src/semmle/code/cpp/commons/Printf.qll
index 32cea249214f..e441dd66adcc 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Printf.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Printf.qll
@@ -9,10 +9,7 @@ import semmle.code.cpp.models.interfaces.FormattingFunction
import semmle.code.cpp.models.implementations.Printf
class PrintfFormatAttribute extends FormatAttribute {
- PrintfFormatAttribute() {
- getArchetype() = "printf" or
- getArchetype() = "__printf__"
- }
+ PrintfFormatAttribute() { getArchetype() = ["printf", "__printf__"] }
}
/**
@@ -20,7 +17,7 @@ class PrintfFormatAttribute extends FormatAttribute {
* function by its use of the GNU `format` attribute.
*/
class AttributeFormattingFunction extends FormattingFunction {
- override string getCanonicalQLClass() { result = "AttributeFormattingFunction" }
+ override string getAPrimaryQlClass() { result = "AttributeFormattingFunction" }
AttributeFormattingFunction() {
exists(PrintfFormatAttribute printf_attrib |
@@ -49,6 +46,18 @@ predicate primitiveVariadicFormatter(TopLevelFunction f, int formatParamIndex) {
)
}
+/**
+ * A standard function such as `vsprintf` that has an output parameter
+ * and a variable argument list of type `va_arg`.
+ */
+private predicate primitiveVariadicFormatterOutput(TopLevelFunction f, int outputParamIndex) {
+ // note: this might look like the regular expression in `primitiveVariadicFormatter`, but
+ // there is one important difference: the [fs] part is not optional, as these classify
+ // the `printf` variants that write to a buffer.
+ // Conveniently, these buffer parameters are all at index 0.
+ f.getName().regexpMatch("_?_?va?[fs]n?w?printf(_s)?(_p)?(_l)?") and outputParamIndex = 0
+}
+
private predicate callsVariadicFormatter(Function f, int formatParamIndex) {
exists(FunctionCall fc, int i |
variadicFormatter(fc.getTarget(), i) and
@@ -57,6 +66,26 @@ private predicate callsVariadicFormatter(Function f, int formatParamIndex) {
)
}
+private predicate callsVariadicFormatterOutput(Function f, int outputParamIndex) {
+ exists(FunctionCall fc, int i |
+ fc.getEnclosingFunction() = f and
+ variadicFormatterOutput(fc.getTarget(), i) and
+ fc.getArgument(i) = f.getParameter(outputParamIndex).getAnAccess()
+ )
+}
+
+/**
+ * Holds if `f` is a function such as `vprintf` that takes variable argument list
+ * of type `va_arg` and writes formatted output to a buffer given as a parameter at
+ * index `outputParamIndex`, if any.
+ */
+private predicate variadicFormatterOutput(Function f, int outputParamIndex) {
+ primitiveVariadicFormatterOutput(f, outputParamIndex)
+ or
+ not f.isVarargs() and
+ callsVariadicFormatterOutput(f, outputParamIndex)
+}
+
/**
* Holds if `f` is a function such as `vprintf` that has a format parameter
* (at `formatParamIndex`) and a variable argument list of type `va_arg`.
@@ -73,11 +102,13 @@ predicate variadicFormatter(Function f, int formatParamIndex) {
* string and a variable number of arguments.
*/
class UserDefinedFormattingFunction extends FormattingFunction {
- override string getCanonicalQLClass() { result = "UserDefinedFormattingFunction" }
+ override string getAPrimaryQlClass() { result = "UserDefinedFormattingFunction" }
UserDefinedFormattingFunction() { isVarargs() and callsVariadicFormatter(this, _) }
override int getFormatParameterIndex() { callsVariadicFormatter(this, result) }
+
+ override int getOutputParameterIndex() { callsVariadicFormatterOutput(this, result) }
}
/**
@@ -86,7 +117,7 @@ class UserDefinedFormattingFunction extends FormattingFunction {
class FormattingFunctionCall extends Expr {
FormattingFunctionCall() { this.(Call).getTarget() instanceof FormattingFunction }
- override string getCanonicalQLClass() { result = "FormattingFunctionCall" }
+ override string getAPrimaryQlClass() { result = "FormattingFunctionCall" }
/**
* Gets the formatting function being called.
@@ -567,12 +598,12 @@ class FormatLiteral extends Literal {
or
len = "l" and result = this.getLongType()
or
- (len = "ll" or len = "L" or len = "q") and
+ len = ["ll", "L", "q"] and
result instanceof LongLongType
or
len = "j" and result = this.getIntmax_t()
or
- (len = "z" or len = "Z") and
+ len = ["z", "Z"] and
(result = this.getSize_t() or result = this.getSsize_t())
or
len = "t" and result = this.getPtrdiff_t()
@@ -605,12 +636,12 @@ class FormatLiteral extends Literal {
or
len = "l" and result = this.getLongType()
or
- (len = "ll" or len = "L" or len = "q") and
+ len = ["ll", "L", "q"] and
result instanceof LongLongType
or
len = "j" and result = this.getIntmax_t()
or
- (len = "z" or len = "Z") and
+ len = ["z", "Z"] and
(result = this.getSize_t() or result = this.getSsize_t())
or
len = "t" and result = this.getPtrdiff_t()
@@ -636,9 +667,7 @@ class FormatLiteral extends Literal {
FloatingPointType getFloatingPointConversion(int n) {
exists(string len |
len = this.getLength(n) and
- if len = "L" or len = "ll"
- then result instanceof LongDoubleType
- else result instanceof DoubleType
+ if len = ["L", "ll"] then result instanceof LongDoubleType else result instanceof DoubleType
)
}
@@ -655,7 +684,7 @@ class FormatLiteral extends Literal {
or
len = "l" and base = this.getLongType()
or
- (len = "ll" or len = "L") and
+ len = ["ll", "L"] and
base instanceof LongLongType
or
len = "q" and base instanceof LongLongType
@@ -702,12 +731,12 @@ class FormatLiteral extends Literal {
exists(string len, string conv |
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
(
- (conv = "c" or conv = "C") and
+ conv = ["c", "C"] and
len = "h" and
result instanceof PlainCharType
or
- (conv = "c" or conv = "C") and
- (len = "l" or len = "w") and
+ conv = ["c", "C"] and
+ len = ["l", "w"] and
result = getWideCharType()
or
conv = "c" and
@@ -747,12 +776,12 @@ class FormatLiteral extends Literal {
exists(string len, string conv |
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
(
- (conv = "s" or conv = "S") and
+ conv = ["s", "S"] and
len = "h" and
result.(PointerType).getBaseType() instanceof PlainCharType
or
- (conv = "s" or conv = "S") and
- (len = "l" or len = "w") and
+ conv = ["s", "S"] and
+ len = ["l", "w"] and
result.(PointerType).getBaseType() = getWideCharType()
or
conv = "s" and
@@ -789,10 +818,7 @@ class FormatLiteral extends Literal {
private Type getConversionType9(int n) {
this.getConversionChar(n) = "Z" and
- (
- this.getLength(n) = "l" or
- this.getLength(n) = "w"
- ) and
+ this.getLength(n) = ["l", "w"] and
exists(Type t |
t.getName() = "UNICODE_STRING" and
result.(PointerType).getBaseType() = t
@@ -945,10 +971,7 @@ class FormatLiteral extends Literal {
len = (afterdot.maximum(1) + 6).maximum(1 + 1 + dot + afterdot + 1 + 1 + 3)
) // (e.g. "-1.59203e-319")
or
- (
- this.getConversionChar(n).toLowerCase() = "d" or
- this.getConversionChar(n).toLowerCase() = "i"
- ) and
+ this.getConversionChar(n).toLowerCase() = ["d", "i"] and
// e.g. -2^31 = "-2147483648"
exists(int sizeBits |
sizeBits =
diff --git a/cpp/ql/src/semmle/code/cpp/commons/Strcat.qll b/cpp/ql/src/semmle/code/cpp/commons/Strcat.qll
index dce086bdd4ba..28408b82ffa6 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Strcat.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Strcat.qll
@@ -8,14 +8,13 @@ import cpp
*/
class StrcatFunction extends Function {
StrcatFunction() {
- exists(string name | name = getName() |
- name = "strcat" or // strcat(dst, src)
- name = "strncat" or // strncat(dst, src, max_amount)
- name = "wcscat" or // wcscat(dst, src)
- name = "_mbscat" or // _mbscat(dst, src)
- name = "wcsncat" or // wcsncat(dst, src, max_amount)
- name = "_mbsncat" or // _mbsncat(dst, src, max_amount)
- name = "_mbsncat_l" // _mbsncat_l(dst, src, max_amount, locale)
- )
+ getName() =
+ ["strcat", // strcat(dst, src)
+ "strncat", // strncat(dst, src, max_amount)
+ "wcscat", // wcscat(dst, src)
+ "_mbscat", // _mbscat(dst, src)
+ "wcsncat", // wcsncat(dst, src, max_amount)
+ "_mbsncat", // _mbsncat(dst, src, max_amount)
+ "_mbsncat_l"] // _mbsncat_l(dst, src, max_amount, locale)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll b/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll
index 92c09a3e6667..b54ff6d66e30 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides a class for calculating the possible length of string expressions.
+ */
+
import semmle.code.cpp.exprs.Expr
import semmle.code.cpp.controlflow.SSA
diff --git a/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll b/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll
index c680cfb073e8..c76413853939 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Synchronization.qll
@@ -23,8 +23,7 @@ abstract class MutexType extends Type {
abstract predicate trylockAccess(FunctionCall fc, Expr arg);
/**
- * Holds if `fc` is a call that unlocks mutex `arg`
- * of this type.
+ * Holds if `fc` is a call that unlocks mutex `arg` of this type.
*/
abstract predicate unlockAccess(FunctionCall fc, Expr arg);
@@ -38,8 +37,7 @@ abstract class MutexType extends Type {
}
/**
- * Holds if `fc` is a call that locks or tries to lock any
- * mutex of this type.
+ * Gets a call that locks or tries to lock any mutex of this type.
*/
FunctionCall getLockAccess() {
result = getMustlockAccess() or
@@ -47,44 +45,44 @@ abstract class MutexType extends Type {
}
/**
- * Holds if `fc` is a call that always locks any mutex of this type.
+ * Gets a call that always locks any mutex of this type.
*/
FunctionCall getMustlockAccess() { this.mustlockAccess(result, _) }
/**
- * Holds if `fc` is a call that tries to lock any mutex of this type,
+ * Gets a call that tries to lock any mutex of this type,
* by may return without success.
*/
FunctionCall getTrylockAccess() { this.trylockAccess(result, _) }
/**
- * Holds if `fc` is a call that unlocks any mutex of this type.
+ * Gets a call that unlocks any mutex of this type.
*/
FunctionCall getUnlockAccess() { this.unlockAccess(result, _) }
/**
- * DEPRECATED: use mustlockAccess(fc, arg) instead
+ * DEPRECATED: use mustlockAccess(fc, arg) instead.
*/
deprecated Function getMustlockFunction() { result = getMustlockAccess().getTarget() }
/**
- * DEPRECATED: use trylockAccess(fc, arg) instead
+ * DEPRECATED: use trylockAccess(fc, arg) instead.
*/
deprecated Function getTrylockFunction() { result = getTrylockAccess().getTarget() }
/**
- * DEPRECATED: use lockAccess(fc, arg) instead
+ * DEPRECATED: use lockAccess(fc, arg) instead.
*/
deprecated Function getLockFunction() { result = getLockAccess().getTarget() }
/**
- * DEPRECATED: use unlockAccess(fc, arg) instead
+ * DEPRECATED: use unlockAccess(fc, arg) instead.
*/
deprecated Function getUnlockFunction() { result = getUnlockAccess().getTarget() }
}
/**
- * A function that looks like a lock function.
+ * Gets a function that looks like a lock function.
*/
private Function mustlockCandidate() {
exists(string name | name = result.getName() |
@@ -94,7 +92,7 @@ private Function mustlockCandidate() {
}
/**
- * A function that looks like a try-lock function.
+ * Gets a function that looks like a try-lock function.
*/
private Function trylockCandidate() {
exists(string name | name = result.getName() |
@@ -104,7 +102,7 @@ private Function trylockCandidate() {
}
/**
- * A function that looks like an unlock function.
+ * Gets a function that looks like an unlock function.
*/
private Function unlockCandidate() {
exists(string name | name = result.getName() |
@@ -171,7 +169,10 @@ class DefaultMutexType extends MutexType {
}
}
-/** Get the mutex argument of a call to lock or unlock. */
+/**
+ * Holds if `arg` is the mutex argument of a call to lock or unlock and
+ * `argType` is the type of the mutex.
+ */
private predicate lockArg(Expr arg, MutexType argType, FunctionCall call) {
argType = arg.getUnderlyingType().stripType() and
(
@@ -184,18 +185,31 @@ private predicate lockArg(Expr arg, MutexType argType, FunctionCall call) {
// `MutexType.mustlockAccess`.
}
+/**
+ * Holds if `call` is a call that locks or tries to lock its argument `arg`.
+ */
predicate lockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getLockAccess())
}
+/**
+ * Holds if `call` is a call that always locks its argument `arg`.
+ */
predicate mustlockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getMustlockAccess())
}
+/**
+ * Holds if `call` is a call that tries to lock its argument `arg`, but may
+ * return without success.
+ */
predicate trylockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getTrylockAccess())
}
+/**
+ * Holds if `call` is a call that unlocks its argument `arg`.
+ */
predicate unlockCall(Expr arg, FunctionCall call) {
exists(MutexType t | lockArg(arg, t, call) and call = t.getUnlockAccess())
}
diff --git a/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll b/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll
index 4b0b1299fd86..0a325442cf5a 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll
@@ -25,7 +25,7 @@ private predicate exprInVoidContext(Expr e) {
(
exists(ExprStmt s |
s = e.getParent() and
- not exists(StmtExpr se | s = se.getStmt().(Block).getLastStmt())
+ not exists(StmtExpr se | s = se.getStmt().(BlockStmt).getLastStmt())
)
or
exists(ConditionalExpr c | c.getThen() = e and c instanceof ExprInVoidContext)
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll
index 1f79d6c5bc20..bac051f64746 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll
@@ -65,7 +65,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
* taken when this expression is true.
*/
ControlFlowNode getATrueSuccessor() {
- truecond_base(this, result) and
+ qlCFGTrueSuccessor(this, result) and
result = getASuccessor()
}
@@ -74,7 +74,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
* taken when this expression is false.
*/
ControlFlowNode getAFalseSuccessor() {
- falsecond_base(this, result) and
+ qlCFGFalseSuccessor(this, result) and
result = getASuccessor()
}
@@ -95,18 +95,20 @@ import ControlFlowGraphPublic
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
/**
+ * DEPRECATED: Use `ControlFlowNode.getATrueSuccessor()` instead.
* Holds when `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is true.
*/
-predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
+deprecated predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGTrueSuccessor(n1, n2)
}
/**
+ * DEPRECATED: Use `ControlFlowNode.getAFalseSuccessor()` instead.
* Holds when `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is false.
*/
-predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
+deprecated predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGFalseSuccessor(n1, n2)
}
@@ -134,7 +136,7 @@ abstract class AdditionalControlFlowEdge extends ControlFlowNodeBase {
/**
* Holds if there is a control-flow edge from `source` to `target` in either
* the extractor-generated control-flow graph or in a subclass of
- * `AdditionalControlFlowEdge`. Use this relation instead of `successors`.
+ * `AdditionalControlFlowEdge`. Use this relation instead of `qlCFGSuccessor`.
*/
predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) {
qlCFGSuccessor(source, target)
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll
index 75211c631feb..656496325af2 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/IRGuards.qll
@@ -13,6 +13,7 @@ import semmle.code.cpp.ir.IR
* has the AST for the `Function` itself, which tends to confuse mapping between the AST `BasicBlock`
* and the `IRBlock`.
*/
+pragma[noinline]
private predicate isUnreachedBlock(IRBlock block) {
block.getFirstInstruction() instanceof UnreachedInstruction
}
@@ -304,13 +305,13 @@ class IRGuardCondition extends Instruction {
pred.getASuccessor() = succ and
controls(pred, testIsTrue)
or
- hasBranchEdge(succ, testIsTrue) and
+ succ = getBranchSuccessor(testIsTrue) and
branch.getCondition() = this and
branch.getBlock() = pred
}
/**
- * Holds if `branch` jumps directly to `succ` when this condition is `testIsTrue`.
+ * Gets the block to which `branch` jumps directly when this condition is `testIsTrue`.
*
* This predicate is intended to help with situations in which an inference can only be made
* based on an edge between a block with multiple successors and a block with multiple
@@ -324,14 +325,14 @@ class IRGuardCondition extends Instruction {
* return x;
* ```
*/
- private predicate hasBranchEdge(IRBlock succ, boolean testIsTrue) {
+ private IRBlock getBranchSuccessor(boolean testIsTrue) {
branch.getCondition() = this and
(
testIsTrue = true and
- succ.getFirstInstruction() = branch.getTrueSuccessor()
+ result.getFirstInstruction() = branch.getTrueSuccessor()
or
testIsTrue = false and
- succ.getFirstInstruction() = branch.getFalseSuccessor()
+ result.getFirstInstruction() = branch.getFalseSuccessor()
)
}
@@ -405,20 +406,78 @@ class IRGuardCondition extends Instruction {
*/
private predicate controlsBlock(IRBlock controlled, boolean testIsTrue) {
not isUnreachedBlock(controlled) and
- exists(IRBlock branchBlock | branchBlock.getAnInstruction() = branch |
- exists(IRBlock succ |
- testIsTrue = true and succ.getFirstInstruction() = branch.getTrueSuccessor()
+ //
+ // For this block to control the block `controlled` with `testIsTrue` the
+ // following must hold: Execution must have passed through the test; that
+ // is, `this` must strictly dominate `controlled`. Execution must have
+ // passed through the `testIsTrue` edge leaving `this`.
+ //
+ // Although "passed through the true edge" implies that
+ // `getBranchSuccessor(true)` dominates `controlled`, the reverse is not
+ // true, as flow may have passed through another edge to get to
+ // `getBranchSuccessor(true)`, so we need to assert that
+ // `getBranchSuccessor(true)` dominates `controlled` *and* that all
+ // predecessors of `getBranchSuccessor(true)` are either `this` or
+ // dominated by `getBranchSuccessor(true)`.
+ //
+ // For example, in the following snippet:
+ //
+ // if (x)
+ // controlled;
+ // false_successor;
+ // uncontrolled;
+ //
+ // `false_successor` dominates `uncontrolled`, but not all of its
+ // predecessors are `this` (`if (x)`) or dominated by itself. Whereas in
+ // the following code:
+ //
+ // if (x)
+ // while (controlled)
+ // also_controlled;
+ // false_successor;
+ // uncontrolled;
+ //
+ // the block `while (controlled)` is controlled because all of its
+ // predecessors are `this` (`if (x)`) or (in the case of `also_controlled`)
+ // dominated by itself.
+ //
+ // The additional constraint on the predecessors of the test successor implies
+ // that `this` strictly dominates `controlled` so that isn't necessary to check
+ // directly.
+ exists(IRBlock succ |
+ succ = this.getBranchSuccessor(testIsTrue) and
+ this.hasDominatingEdgeTo(succ) and
+ succ.dominates(controlled)
+ )
+ }
+
+ /**
+ * Holds if `(this, succ)` is an edge that dominates `succ`, that is, all other
+ * predecessors of `succ` are dominated by `succ`. This implies that `this` is the
+ * immediate dominator of `succ`.
+ *
+ * This is a necessary and sufficient condition for an edge to dominate anything,
+ * and in particular `bb1.hasDominatingEdgeTo(bb2) and bb2.dominates(bb3)` means
+ * that the edge `(bb1, bb2)` dominates `bb3`.
+ */
+ private predicate hasDominatingEdgeTo(IRBlock succ) {
+ exists(IRBlock branchBlock | branchBlock = this.getBranchBlock() |
+ branchBlock.immediatelyDominates(succ) and
+ branchBlock.getASuccessor() = succ and
+ forall(IRBlock pred | pred = succ.getAPredecessor() and pred != branchBlock |
+ succ.dominates(pred)
or
- testIsTrue = false and succ.getFirstInstruction() = branch.getFalseSuccessor()
- |
- branch.getCondition() = this and
- succ.dominates(controlled) and
- forall(IRBlock pred | pred.getASuccessor() = succ |
- pred = branchBlock or succ.dominates(pred) or not pred.isReachableFromFunctionEntry()
- )
+ // An unreachable `pred` is vacuously dominated by `succ` since all
+ // paths from the entry to `pred` go through `succ`. Such vacuous
+ // dominance is not included in the `dominates` predicate since that
+ // could cause quadratic blow-up.
+ not pred.isReachableFromFunctionEntry()
)
)
}
+
+ pragma[noinline]
+ private IRBlock getBranchBlock() { result = branch.getBlock() }
}
private ConditionalBranchInstruction get_branch_for_condition(Instruction guard) {
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll b/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll
index 3ae1ed11e6d9..caae23c3bbd9 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll
@@ -73,8 +73,20 @@ private predicate addressTakenVariable(StackVariable var) {
)
}
+/**
+ * Holds if `v` is a stack-allocated reference-typed local variable. We don't
+ * build SSA for such variables since they are likely to change values even
+ * when not syntactically mentioned. For the same reason,
+ * `addressTakenVariable` is used to prevent tracking variables that may be
+ * aliased by such a reference.
+ *
+ * Reference-typed parameters are treated as if they weren't references.
+ * That's because it's in practice highly unlikely that they alias other data
+ * accessible from the function body.
+ */
private predicate isReferenceVar(StackVariable v) {
- v.getUnspecifiedType() instanceof ReferenceType
+ v.getUnspecifiedType() instanceof ReferenceType and
+ not v instanceof Parameter
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll
index b2cfc903a662..b47618de2e92 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll
@@ -118,7 +118,7 @@ private predicate excludeNodeAndNodesBelow(Expr e) {
or
// Constructor init lists should be evaluated, and we can change this in
// the future, but it would mean that a `Function` entry point is not
- // always a `Block` or `FunctionTryStmt`.
+ // always a `BlockStmt` or `FunctionTryStmt`.
e instanceof ConstructorInit
or
// Destructor field destructions should also be hooked into the CFG
@@ -408,10 +408,10 @@ private Node getControlOrderChildSparse(Node n, int i) {
// in-line in the block containing their corresponding DeclStmt but should
// not be evaluated in the order implied by their position in the block. We
// do the following.
- // - Block skips all the VlaDeclStmt and VlaDimensionStmt children.
+ // - BlockStmt skips all the VlaDeclStmt and VlaDimensionStmt children.
// - VlaDeclStmt is inserted as a child of DeclStmt
// - VlaDimensionStmt is inserted as a child of VlaDeclStmt
- result = n.(Block).getChild(i) and
+ result = n.(BlockStmt).getChild(i) and
not result instanceof VlaDeclStmt and
not result instanceof VlaDimensionStmt
or
@@ -557,7 +557,7 @@ private class Spec extends Pos {
*/
private predicate straightLineSparse(Node scope, int i, Node ni, Spec spec) {
scope =
- any(Block b |
+ any(BlockStmt b |
i = -1 and ni = b and spec.isAt()
or
if exists(getLastControlOrderChild(b))
@@ -734,7 +734,7 @@ private predicate straightLineSparse(Node scope, int i, Node ni, Spec spec) {
or
// If the switch body is not a block then this step is skipped, and the
// expression jumps directly to the cases.
- i = 1 and ni = s.getStmt().(Block) and spec.isAt()
+ i = 1 and ni = s.getStmt().(BlockStmt) and spec.isAt()
or
i = 2 and ni = s.getASwitchCase() and spec.isBefore()
or
@@ -1010,7 +1010,7 @@ private predicate subEdgeIncludingDestructors(Pos p1, Node n1, Node n2, Pos p2)
* The exact placement of that call in the CFG depends on the type of
* `node` as follows:
*
- * - `Block`: after ordinary control flow falls off the end of the block
+ * - `BlockStmt`: after ordinary control flow falls off the end of the block
* without jumps or exceptions.
* - `ReturnStmt`: After the statement itself or after its operand (if
* present).
@@ -1376,8 +1376,6 @@ private module Cached {
/**
* Holds if `n2` is a successor of `n1` in the CFG. This includes also
* true-successors and false-successors.
- *
- * This corresponds to the old `successors` dbscheme relation.
*/
cached
predicate qlCFGSuccessor(Node n1, Node n2) {
@@ -1390,9 +1388,8 @@ private module Cached {
}
/**
- * Holds if `n2` is a true-successor of `n1` in the CFG.
- *
- * This corresponds to the old `truecond` dbscheme relation.
+ * Holds if `n2` is a control-flow node such that the control-flow
+ * edge `(n1, n2)` may be taken when `n1` is an expression that is true.
*/
cached
predicate qlCFGTrueSuccessor(Node n1, Node n2) {
@@ -1401,9 +1398,8 @@ private module Cached {
}
/**
- * Holds if `n2` is a false-successor of `n1` in the CFG.
- *
- * This corresponds to the old `falsecond` dbscheme relation.
+ * Holds if `n2` is a control-flow node such that the control-flow
+ * edge `(n1, n2)` may be taken when `n1` is an expression that is false.
*/
cached
predicate qlCFGFalseSuccessor(Node n1, Node n2) {
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
index c473f1560882..77e0f05ed020 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/ConstantExprs.qll
@@ -1,5 +1,6 @@
import cpp
private import PrimitiveBasicBlocks
+private import semmle.code.cpp.controlflow.internal.CFG
private class Node = ControlFlowNodeBase;
@@ -153,8 +154,8 @@ private predicate nonAnalyzableFunction(Function f) {
*/
private predicate impossibleFalseEdge(Expr condition, Node succ) {
conditionAlwaysTrue(condition) and
- falsecond_base(condition, succ) and
- not truecond_base(condition, succ)
+ qlCFGFalseSuccessor(condition, succ) and
+ not qlCFGTrueSuccessor(condition, succ)
}
/**
@@ -162,8 +163,8 @@ private predicate impossibleFalseEdge(Expr condition, Node succ) {
*/
private predicate impossibleTrueEdge(Expr condition, Node succ) {
conditionAlwaysFalse(condition) and
- truecond_base(condition, succ) and
- not falsecond_base(condition, succ)
+ qlCFGTrueSuccessor(condition, succ) and
+ not qlCFGFalseSuccessor(condition, succ)
}
/**
@@ -181,7 +182,7 @@ private int switchCaseRangeEnd(SwitchCase sc) {
* body `switchBlock`. There may be several such expressions: for example, if
* the condition is `(x ? y : z)` then the result is {`y`, `z`}.
*/
-private Node getASwitchExpr(SwitchStmt switch, Block switchBlock) {
+private Node getASwitchExpr(SwitchStmt switch, BlockStmt switchBlock) {
switch.getStmt() = switchBlock and
successors_extended(result, switchBlock)
}
@@ -191,7 +192,7 @@ private Node getASwitchExpr(SwitchStmt switch, Block switchBlock) {
* from `switchBlock` to `sc` is impossible. This considers only non-`default`
* switch cases.
*/
-private predicate impossibleSwitchEdge(Block switchBlock, SwitchCase sc) {
+private predicate impossibleSwitchEdge(BlockStmt switchBlock, SwitchCase sc) {
not sc instanceof DefaultCase and
exists(SwitchStmt switch |
switch = sc.getSwitchStmt() and
@@ -214,7 +215,7 @@ private predicate impossibleSwitchEdge(Block switchBlock, SwitchCase sc) {
* If a switch provably always chooses a non-default case, then the edge to
* the default case is impossible.
*/
-private predicate impossibleDefaultSwitchEdge(Block switchBlock, DefaultCase dc) {
+private predicate impossibleDefaultSwitchEdge(BlockStmt switchBlock, DefaultCase dc) {
exists(SwitchStmt switch |
switch = dc.getSwitchStmt() and
switch.getStmt() = switchBlock and
@@ -278,20 +279,62 @@ private predicate reachableRecursive(ControlFlowNode n) {
reachableRecursive(n.getAPredecessor())
}
+/** Holds if `e` is a compile time constant with integer value `val`. */
private predicate compileTimeConstantInt(Expr e, int val) {
- val = e.getFullyConverted().getValue().toInt() and
- not e instanceof StringLiteral and
- not exists(Expr e1 | e1.getConversion() = e) // only values for fully converted expressions
+ (
+ // If we have an integer value then we are done.
+ if exists(e.getValue().toInt())
+ then val = e.getValue().toInt()
+ else
+ // Otherwise, if we are a conversion of another expression with an
+ // integer value, and that value can be converted into our type,
+ // then we have that value.
+ exists(Expr x, int valx |
+ x.getConversion() = e and
+ compileTimeConstantInt(x, valx) and
+ val = convertIntToType(valx, e.getType().getUnspecifiedType())
+ )
+ ) and
+ // If our unconverted expression is a string literal `"123"`, then we
+ // do not have integer value `123`.
+ not e.getUnconverted() instanceof StringLiteral
}
-library class CompileTimeConstantInt extends Expr {
- CompileTimeConstantInt() { compileTimeConstantInt(this, _) }
+/**
+ * Get `val` represented as type `t`, if that is possible without
+ * overflow or underflows.
+ */
+bindingset[val, t]
+private int convertIntToType(int val, IntegralType t) {
+ if t instanceof BoolType
+ then if val = 0 then result = 0 else result = 1
+ else
+ if t.isUnsigned()
+ then if val >= 0 and val.bitShiftRight(t.getSize() * 8) = 0 then result = val else none()
+ else
+ if val >= 0 and val.bitShiftRight(t.getSize() * 8 - 1) = 0
+ then result = val
+ else
+ if (-(val + 1)).bitShiftRight(t.getSize() * 8 - 1) = 0
+ then result = val
+ else none()
+}
+
+/**
+ * INTERNAL: Do not use.
+ * An expression that has been found to have an integer value at compile
+ * time.
+ */
+class CompileTimeConstantInt extends Expr {
+ int val;
+
+ CompileTimeConstantInt() { compileTimeConstantInt(this.getFullyConverted(), val) }
- int getIntValue() { compileTimeConstantInt(this, result) }
+ int getIntValue() { result = val }
}
library class CompileTimeVariableExpr extends Expr {
- CompileTimeVariableExpr() { not compileTimeConstantInt(this, _) }
+ CompileTimeVariableExpr() { not this instanceof CompileTimeConstantInt }
}
/** A helper class for evaluation of expressions. */
@@ -863,9 +906,9 @@ library class ConditionEvaluator extends ExprEvaluator {
ConditionEvaluator() { this = 0 }
override predicate interesting(Expr e) {
- falsecond_base(e, _)
+ qlCFGFalseSuccessor(e, _)
or
- truecond_base(e, _)
+ qlCFGTrueSuccessor(e, _)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll
index b3f8cd02828b..f9677a0654b7 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/AddressFlow.qll
@@ -29,7 +29,7 @@ private predicate stdIdentityFunction(Function f) { f.hasQualifiedName("std", ["
*/
private predicate stdAddressOf(Function f) { f.hasQualifiedName("std", "addressof") }
-private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) {
+private predicate lvalueToLvalueStepPure(Expr lvalueIn, Expr lvalueOut) {
lvalueIn.getConversion() = lvalueOut.(ParenthesisExpr)
or
// When an object is implicitly converted to a reference to one of its base
@@ -42,6 +42,10 @@ private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) {
// such casts.
lvalueIn.getConversion() = lvalueOut and
lvalueOut.(CStyleCast).isImplicit()
+}
+
+private predicate lvalueToLvalueStep(Expr lvalueIn, Expr lvalueOut) {
+ lvalueToLvalueStepPure(lvalueIn, lvalueOut)
or
// C++ only
lvalueIn = lvalueOut.(PrefixCrementOperation).getOperand().getFullyConverted()
@@ -214,6 +218,69 @@ private predicate referenceToUpdate(Expr reference, Expr outer, ControlFlowNode
)
}
+private predicate lvalueFromVariableAccess(VariableAccess va, Expr lvalue) {
+ // Base case for non-reference types.
+ lvalue = va and
+ not va.getConversion() instanceof ReferenceDereferenceExpr
+ or
+ // Base case for reference types where we pretend that they are
+ // non-reference types. The type of the target of `va` can be `ReferenceType`
+ // or `FunctionReferenceType`.
+ lvalue = va.getConversion().(ReferenceDereferenceExpr)
+ or
+ // lvalue -> lvalue
+ exists(Expr prev |
+ lvalueFromVariableAccess(va, prev) and
+ lvalueToLvalueStep(prev, lvalue)
+ )
+ or
+ // pointer -> lvalue
+ exists(Expr prev |
+ pointerFromVariableAccess(va, prev) and
+ pointerToLvalueStep(prev, lvalue)
+ )
+ or
+ // reference -> lvalue
+ exists(Expr prev |
+ referenceFromVariableAccess(va, prev) and
+ referenceToLvalueStep(prev, lvalue)
+ )
+}
+
+private predicate pointerFromVariableAccess(VariableAccess va, Expr pointer) {
+ // pointer -> pointer
+ exists(Expr prev |
+ pointerFromVariableAccess(va, prev) and
+ pointerToPointerStep(prev, pointer)
+ )
+ or
+ // reference -> pointer
+ exists(Expr prev |
+ referenceFromVariableAccess(va, prev) and
+ referenceToPointerStep(prev, pointer)
+ )
+ or
+ // lvalue -> pointer
+ exists(Expr prev |
+ lvalueFromVariableAccess(va, prev) and
+ lvalueToPointerStep(prev, pointer)
+ )
+}
+
+private predicate referenceFromVariableAccess(VariableAccess va, Expr reference) {
+ // reference -> reference
+ exists(Expr prev |
+ referenceFromVariableAccess(va, prev) and
+ referenceToReferenceStep(prev, reference)
+ )
+ or
+ // lvalue -> reference
+ exists(Expr prev |
+ lvalueFromVariableAccess(va, prev) and
+ lvalueToReferenceStep(prev, reference)
+ )
+}
+
/**
* Holds if `node` is a control-flow node that may modify `inner` (or what it
* points to) through `outer`. The two expressions may be `Conversion`s. Plain
@@ -236,7 +303,7 @@ predicate valueToUpdate(Expr inner, Expr outer, ControlFlowNode node) {
(
inner instanceof VariableAccess and
// Don't track non-field assignments
- (assignmentTo(outer, _) implies inner instanceof FieldAccess)
+ not (assignmentTo(outer, _) and outer.(VariableAccess).getTarget() instanceof StackVariable)
or
inner instanceof ThisExpr
or
@@ -245,3 +312,27 @@ predicate valueToUpdate(Expr inner, Expr outer, ControlFlowNode node) {
// can't do anything useful with those at the moment.
)
}
+
+/**
+ * Holds if `e` is a fully-converted expression that evaluates to an lvalue
+ * derived from `va` and is used for reading from or assigning to. This is in
+ * contrast with a variable access that is used for taking an address (`&x`)
+ * or simply discarding its value (`x;`).
+ *
+ * This analysis does not propagate across assignments or calls, and unlike
+ * `variableAccessedAsValue` in `semmle.code.cpp.dataflow.EscapesTree` it
+ * propagates through array accesses but not field accesses. The analysis is
+ * also not concerned with whether the lvalue `e` is converted to an rvalue --
+ * to examine that, use the relevant member predicates on `Expr`.
+ *
+ * If `va` has reference type, the analysis concerns the value pointed to by
+ * the reference rather than the reference itself. The expression `e` may be a
+ * `Conversion`.
+ */
+predicate variablePartiallyAccessed(VariableAccess va, Expr e) {
+ lvalueFromVariableAccess(va, e) and
+ not lvalueToLvalueStepPure(e, _) and
+ not lvalueToPointerStep(e, _) and
+ not lvalueToReferenceStep(e, _) and
+ not e = any(ExprInVoidContext eivc | e = eivc.getConversion*())
+}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll
index 154430794d4a..e57af3b8d317 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll
@@ -53,26 +53,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
}
/**
- * Holds if the call context `ctx` reduces the set of viable dispatch
- * targets of `ma` in `c`.
+ * Holds if the set of viable implementations that can be called by `call`
+ * might be improved by knowing the call context.
*/
-predicate reducedViableImplInCallContext(Call call, Function f, Call ctx) { none() }
+predicate mayBenefitFromCallContext(Call call, Function f) { none() }
/**
- * Gets a viable dispatch target of `ma` in the context `ctx`. This is
- * restricted to those `ma`s for which the context makes a difference.
+ * Gets a viable dispatch target of `call` in the context `ctx`. This is
+ * restricted to those `call`s for which a context might make a difference.
*/
-Function prunedViableImplInCallContext(Call call, Call ctx) { none() }
-
-/**
- * Holds if flow returning from `m` to `ma` might return further and if
- * this path restricts the set of call sites that can be returned to.
- */
-predicate reducedViableImplInReturn(Function f, Call call) { none() }
-
-/**
- * Gets a viable dispatch target of `ma` in the context `ctx`. This is
- * restricted to those `ma`s and results for which the return flow from the
- * result to `ma` restricts the possible context `ctx`.
- */
-Function prunedViableImplInCallContextReverse(Call call, Call ctx) { none() }
+Function viableImplInCallContext(Call call, Call ctx) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
index f876c04d6c66..8bc3d75ff86e 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- store(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- nodeCandFwd1IsStored(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
exists(Node mid |
nodeCandFwd1(mid, fromArg, config) and
- read(mid, f, node)
+ read(mid, c, node)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
- exists(Node mid, Node node |
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
nodeCandFwd1(mid, config) and
- store(mid, f, node)
+ store(mid, tc, node, _) and
+ c = tc.getContent()
)
}
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- nodeCand1IsRead(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate nodeCand1IsRead(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- nodeCandFwd1IsStored(f, unbind(config)) and
- store(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
- nodeCand1IsRead(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
- nodeCand1(n2, unbind(config)) and
- store(n1, f, n2)
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
+ )
}
pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- read(n1, f, n2)
+ read(n1, c, n2)
}
pragma[noinline]
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
)
or
// store
- exists(Node mid, Content f |
+ exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, _, config) and
- store(mid, f, node, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, argStored, config) and
- nodeCandFwd2IsStored(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
// flow into a callable
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCand1(node, unbind(config)) and
nodeCandFwd2(mid, _, _, stored, config) and
- store(mid, f, node, config)
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
private predicate nodeCandFwd2Read(
- Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
) {
exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, true, config) and
- read(mid, f, node, config)
+ read(mid, c, node, config)
)
}
@@ -896,15 +901,15 @@ private predicate nodeCand2(
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, returnRead, read, config) and
- nodeCand2IsRead(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(Node mid, Content f, boolean read0 |
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
@@ -930,51 +935,51 @@ private predicate nodeCand2(
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd2(node, _, _, true, unbind(config)) and
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
Configuration config
) {
exists(Node mid |
- store(node, f, mid, config) and
+ storeCand1(node, c, mid, config) and
nodeCand2(mid, toReturn, returnRead, true, config) and
nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
pragma[nomagic]
-private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
exists(Node node |
- nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2Store(c, node, _, _, stored, conf) and
nodeCand2(node, _, _, stored, conf)
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
+ * Holds if `c` is the target of both a store and a read in the path graph
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
exists(boolean apNonEmpty |
- nodeCand2IsStored(f, apNonEmpty, conf) and
- nodeCand2IsRead(f, apNonEmpty, conf)
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
)
}
@@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2(
}
private module LocalFlowBigStep {
+ /**
+ * A node where some checking is required, and hence the big-step relation
+ * is not allowed to step over.
+ */
+ private class FlowCheckNode extends Node {
+ FlowCheckNode() {
+ this instanceof CastNode or
+ clearsContent(this, _)
+ }
+ }
+
/**
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1058,9 +1074,9 @@ private module LocalFlowBigStep {
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
node instanceof OutNodeExt or
- store(_, _, node) or
+ store(_, _, node, _) or
read(_, _, node) or
- node instanceof CastNode
+ node instanceof FlowCheckNode
)
}
@@ -1074,11 +1090,11 @@ private module LocalFlowBigStep {
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next) or
+ store(node, _, next, _) or
read(node, _, next)
)
or
- node instanceof CastNode
+ node instanceof FlowCheckNode
or
config.isSink(node)
}
@@ -1108,11 +1124,11 @@ private module LocalFlowBigStep {
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getErasedNodeTypeBound(node1)
+ t = getNodeType(node1)
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getErasedNodeTypeBound(node2)
+ t = getNodeType(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
@@ -1122,16 +1138,16 @@ private module LocalFlowBigStep {
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
localFlowStepNodeCand1(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getErasedNodeTypeBound(node2) and
+ t = getNodeType(node2) and
nodeCand2(node2, unbind(config))
)
)
@@ -1154,19 +1170,21 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2, config) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
nodeCand2(node2, _, _, true, unbind(config)) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
@@ -1183,9 +1201,8 @@ private predicate flowCandFwd(
Configuration config
) {
flowCandFwd0(node, fromArg, argApf, apf, config) and
- if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
- else any()
+ not apf.isClearedAt(node) and
+ if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any()
}
pragma[nomagic]
@@ -1197,7 +1214,7 @@ private predicate flowCandFwd0(
config.isSource(node) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
or
exists(Node mid |
flowCandFwd(mid, fromArg, argApf, apf, config) and
@@ -1223,21 +1240,22 @@ private predicate flowCandFwd0(
additionalJumpStep(mid, node, config) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
)
or
// store
- exists(Node mid, Content f |
- flowCandFwd(mid, fromArg, argApf, _, config) and
- storeCand2(mid, f, node, config) and
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
nodeCand2(node, _, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
)
or
// read
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, argApf, config) and
- flowCandFwdConsCand(f, apf, config) and
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
)
or
@@ -1261,24 +1279,30 @@ private predicate flowCandFwd0(
}
pragma[nomagic]
-private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
- exists(Node mid, Node n |
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
flowCandFwd(mid, _, _, apf, config) and
- storeCand2(mid, f, n, config) and
+ storeCand2(mid, tc, n, contentType, config) and
nodeCand2(n, _, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+ compatibleTypes(apf.getType(), contentType)
)
}
+pragma[nomagic]
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
pragma[nomagic]
private predicate flowCandFwdRead(
- Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, argApf, apf0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
- )
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
}
pragma[nomagic]
@@ -1385,17 +1409,15 @@ private predicate flowCand0(
)
or
// store
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, returnApf, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandConsCand(f, apf, config)
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
)
or
// read
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, returnApf, apf0, config) and
- flowCandFwdConsCand(f, apf0, config) and
- apf.headUsesContent(f)
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
)
or
// flow into a callable
@@ -1417,36 +1439,40 @@ private predicate flowCand0(
else returnApf = TAccessPathFrontNone()
}
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
+}
+
pragma[nomagic]
private predicate flowCandRead(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
exists(Node mid |
- readCand2(node, f, mid, config) and
+ readCandFwd(node, tc, apf, mid, config) and
flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
exists(Node mid |
- storeCand2(node, f, mid, config) and
- flowCand(mid, toReturn, returnApf, apf0, config)
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
- flowCandFwdConsCand(f, apf, config) and
- exists(Node n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, _, apf, config)
- )
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
}
pragma[nomagic]
@@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned(
)
}
-private newtype TAccessPath =
+private newtype TAccessPathApprox =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
- * 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 Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1522,20 +1548,18 @@ 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(Content 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)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content 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 Content f;
+private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil {
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathApproxConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) }
}
-private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons {
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override AccessPathApprox pop(TypedContent head) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ result = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ result = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private 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 `apa` from a source
+ * in the configuration `config`.
*
- * The Boolean `fromArg` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * The call context `cc` records whether the node is reached through an
+ * argument in a call, and if so, `argApa` records the approximate access path
+ * of that argument.
*/
private predicate flowFwd(
- Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
- Configuration config
+ Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, Configuration config
) {
- flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowFwd0(node, cc, argApa, apf, apa, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- Node node, boolean fromArg, 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
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
- exists(Node mid |
- flowFwd(mid, fromArg, argAp, apf, ap, config) and
- localFlowBigStep(mid, node, true, _, config, _)
+ exists(Node mid, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and
+ localFlowBigStep(mid, node, true, _, config, localCC)
)
or
- exists(Node mid, AccessPathNil nil |
- flowFwd(mid, fromArg, argAp, _, nil, config) and
- localFlowBigStep(mid, node, false, apf, config, _) and
- apf = ap.(AccessPathNil).getFront()
+ exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and
+ localFlowBigStep(mid, node, false, apf, config, localCC) and
+ 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
- fromArg = false and
- argAp = TAccessPathNone()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone()
)
or
- exists(Node mid, AccessPathNil nil |
+ exists(Node mid, AccessPathApproxNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
)
)
or
// store
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
- ap = push(f, ap0)
- )
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config))
or
// read
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
- flowFwdConsCand(f, apf, ap, config)
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and
+ flowFwdConsCand(tc, apf, apa, config)
)
or
// flow into a callable
- flowFwdIn(_, node, _, _, apf, ap, config) and
- fromArg = true 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 |
- flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
- fromArg = false
+ exists(DataFlowCallable c |
+ 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, fromArg, 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, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, LocalCallContext localCC, Configuration config
+) {
+ flowFwd(node, cc, argApa, apf, apa, config) and
+ localFlowEntry(node, config) and
+ localCC = getLocalCallContext(cc, node.getEnclosingCallable())
+}
+
pragma[nomagic]
private predicate flowFwdStore(
- Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- 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, fromArg, argAp, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ flowFwd(mid, cc, argApa, apf0, apa0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeCand2(mid, f, node, config) and
- flowCand(mid, _, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- flowCandConsCand(f, apf0, config) and
- apf.headUsesContent(f) and
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
flowCand(node, _, _, apf, unbind(config))
}
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2,
+ CallContext cc, AccessPathApproxOption argApa, Configuration config
+) {
+ flowFwd(node1, cc, argApa, apf0, apa0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
+}
+
pragma[nomagic]
private predicate flowFwdRead(
- Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
- Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc,
+ AccessPathApproxOption argApa, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
private predicate flowFwdConsCand(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+ TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(Node n |
- flowFwd(n, _, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ flowFwd(n, _, _, apf, apa, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
)
}
pragma[nomagic]
private predicate flowFwdIn(
- DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
- flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
+ flowFwd(arg, outercc, argApa, apf, apa, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
- flowCand(p, _, _, _, unbind(config))
+ 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, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
- flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowFwd(ret, innercc, argApa, apf, apa, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
- flowCand(node, _, _, _, unbind(config))
+ innerc = ret.getEnclosingCallable() and
+ flowCand(node, _, _, _, unbind(config)) and
+ (
+ resolveReturn(innercc, innerc, call)
+ or
+ 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, true, TAccessPathSome(argAp), apf, ap, config)
+ flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa,
+ config)
}
/**
@@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
- DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+ DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa,
+ Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
- flowFwdIn(call, p, fromArg, 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 `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
- * 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(Content f |
- flowStore(f, node, toReturn, returnAp, ap, config) and
- flowConsCand(f, ap, config)
+ exists(TypedContent tc |
+ 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, true, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- storeCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, apa, _, _, _, config) and
+ apa0 = push(tc, apa)
}
pragma[nomagic]
private predicate flowStore(
- Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- readCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, _, config) and
- ap0 = pop(f, ap) and
- flowFwdConsCand(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, apa, _, _, _, config) and
+ apa0 = pop(tc, apa) and
+ flowFwdConsCand(tc, _, apa0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, 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, f, 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)
}
/**
@@ -1969,12 +2028,13 @@ 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 |
- flowOut(call, ret, toReturn, returnAp, ap, config) and
- flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
+ exists(ReturnNodeExt ret, CallContextCall ccc |
+ flowOut(call, ret, toReturn, returnApa, apa, config) and
+ flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and
+ ccc.matchesCall(call)
)
}
@@ -1985,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, true, TAccessPathSome(ap), _, ap0, config)
- )
- }
+ TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) }
/**
* A context for generating flow summaries. This represents flow entry through
@@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
}
}
+private newtype TAccessPath =
+ TAccessPathNil(DataFlowType t) or
+ TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) }
+
private newtype TPathNode =
TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
@@ -2041,13 +2107,13 @@ private newtype TPathNode =
config.isSource(node) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TNil(getErasedNodeTypeBound(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) and
config = mid.getConfiguration() and
- flow(node, _, _, ap, unbind(config))
+ flow(node, _, _, ap.getApprox(), unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2059,12 +2125,99 @@ 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, _, _, TAccessPathNil(_)) 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 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) }
+
+ 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 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() {
+ 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() }
+}
+
/**
* 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.
@@ -2098,14 +2251,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TNil(getErasedNodeTypeBound(node))
+ ap = TAccessPathNil(getNodeType(node))
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
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
- not innercc instanceof CallContextCall and
- ap = mid.getAp() and
+ innercc instanceof CallContextNoCall and
+ apa = mid.getAp().getApprox() 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)
@@ -2321,10 +2494,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)
}
/**
@@ -2333,10 +2506,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)
)
}
@@ -2345,22 +2517,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 = ap.getApprox()
)
}
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)
)
}
@@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0(
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
AccessPath ap
) {
- pathIntoArg(mid, i, outercc, call, ap) and
- callable = resolveCall(call, outercc) and
- parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration())
+ exists(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())
+ )
}
/**
@@ -2403,7 +2578,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, AccessPath ap, AccessPathApprox apa,
+ Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
@@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough(
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp() and
+ apa = ap.getApprox() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
@@ -2419,11 +2596,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()))
)
}
@@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0(
*/
pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
- exists(DataFlowCall call, ReturnKindExt kind |
- pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
+ exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
+ pathThroughCallable0(call, mid, kind, cc, ap, apa) and
+ out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration()))
)
}
@@ -2521,10 +2699,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2533,7 +2711,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2544,7 +2722,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2562,15 +2740,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2591,7 +2769,7 @@ private module FlowExploration {
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
@@ -2608,7 +2786,7 @@ private module FlowExploration {
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), ap.getType())
+ then compatibleTypes(getNodeType(node), ap.getType())
else any()
)
}
@@ -2721,7 +2899,7 @@ private module FlowExploration {
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
)
or
@@ -2737,7 +2915,7 @@ private module FlowExploration {
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -2746,11 +2924,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getNodeType(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2769,35 +2948,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- store(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
@@ -2806,7 +2992,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
- not innercc instanceof CallContextCall and
+ innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = 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 f876c04d6c66..8bc3d75ff86e 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- store(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- nodeCandFwd1IsStored(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
exists(Node mid |
nodeCandFwd1(mid, fromArg, config) and
- read(mid, f, node)
+ read(mid, c, node)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
- exists(Node mid, Node node |
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
nodeCandFwd1(mid, config) and
- store(mid, f, node)
+ store(mid, tc, node, _) and
+ c = tc.getContent()
)
}
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- nodeCand1IsRead(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate nodeCand1IsRead(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- nodeCandFwd1IsStored(f, unbind(config)) and
- store(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
- nodeCand1IsRead(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
- nodeCand1(n2, unbind(config)) and
- store(n1, f, n2)
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
+ )
}
pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- read(n1, f, n2)
+ read(n1, c, n2)
}
pragma[noinline]
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
)
or
// store
- exists(Node mid, Content f |
+ exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, _, config) and
- store(mid, f, node, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, argStored, config) and
- nodeCandFwd2IsStored(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
// flow into a callable
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCand1(node, unbind(config)) and
nodeCandFwd2(mid, _, _, stored, config) and
- store(mid, f, node, config)
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
private predicate nodeCandFwd2Read(
- Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
) {
exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, true, config) and
- read(mid, f, node, config)
+ read(mid, c, node, config)
)
}
@@ -896,15 +901,15 @@ private predicate nodeCand2(
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, returnRead, read, config) and
- nodeCand2IsRead(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(Node mid, Content f, boolean read0 |
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
@@ -930,51 +935,51 @@ private predicate nodeCand2(
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd2(node, _, _, true, unbind(config)) and
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
Configuration config
) {
exists(Node mid |
- store(node, f, mid, config) and
+ storeCand1(node, c, mid, config) and
nodeCand2(mid, toReturn, returnRead, true, config) and
nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
pragma[nomagic]
-private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
exists(Node node |
- nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2Store(c, node, _, _, stored, conf) and
nodeCand2(node, _, _, stored, conf)
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
+ * Holds if `c` is the target of both a store and a read in the path graph
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
exists(boolean apNonEmpty |
- nodeCand2IsStored(f, apNonEmpty, conf) and
- nodeCand2IsRead(f, apNonEmpty, conf)
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
)
}
@@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2(
}
private module LocalFlowBigStep {
+ /**
+ * A node where some checking is required, and hence the big-step relation
+ * is not allowed to step over.
+ */
+ private class FlowCheckNode extends Node {
+ FlowCheckNode() {
+ this instanceof CastNode or
+ clearsContent(this, _)
+ }
+ }
+
/**
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1058,9 +1074,9 @@ private module LocalFlowBigStep {
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
node instanceof OutNodeExt or
- store(_, _, node) or
+ store(_, _, node, _) or
read(_, _, node) or
- node instanceof CastNode
+ node instanceof FlowCheckNode
)
}
@@ -1074,11 +1090,11 @@ private module LocalFlowBigStep {
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next) or
+ store(node, _, next, _) or
read(node, _, next)
)
or
- node instanceof CastNode
+ node instanceof FlowCheckNode
or
config.isSink(node)
}
@@ -1108,11 +1124,11 @@ private module LocalFlowBigStep {
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getErasedNodeTypeBound(node1)
+ t = getNodeType(node1)
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getErasedNodeTypeBound(node2)
+ t = getNodeType(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
@@ -1122,16 +1138,16 @@ private module LocalFlowBigStep {
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
localFlowStepNodeCand1(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getErasedNodeTypeBound(node2) and
+ t = getNodeType(node2) and
nodeCand2(node2, unbind(config))
)
)
@@ -1154,19 +1170,21 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2, config) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
nodeCand2(node2, _, _, true, unbind(config)) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
@@ -1183,9 +1201,8 @@ private predicate flowCandFwd(
Configuration config
) {
flowCandFwd0(node, fromArg, argApf, apf, config) and
- if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
- else any()
+ not apf.isClearedAt(node) and
+ if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any()
}
pragma[nomagic]
@@ -1197,7 +1214,7 @@ private predicate flowCandFwd0(
config.isSource(node) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
or
exists(Node mid |
flowCandFwd(mid, fromArg, argApf, apf, config) and
@@ -1223,21 +1240,22 @@ private predicate flowCandFwd0(
additionalJumpStep(mid, node, config) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
)
or
// store
- exists(Node mid, Content f |
- flowCandFwd(mid, fromArg, argApf, _, config) and
- storeCand2(mid, f, node, config) and
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
nodeCand2(node, _, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
)
or
// read
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, argApf, config) and
- flowCandFwdConsCand(f, apf, config) and
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
)
or
@@ -1261,24 +1279,30 @@ private predicate flowCandFwd0(
}
pragma[nomagic]
-private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
- exists(Node mid, Node n |
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
flowCandFwd(mid, _, _, apf, config) and
- storeCand2(mid, f, n, config) and
+ storeCand2(mid, tc, n, contentType, config) and
nodeCand2(n, _, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+ compatibleTypes(apf.getType(), contentType)
)
}
+pragma[nomagic]
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
pragma[nomagic]
private predicate flowCandFwdRead(
- Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, argApf, apf0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
- )
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
}
pragma[nomagic]
@@ -1385,17 +1409,15 @@ private predicate flowCand0(
)
or
// store
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, returnApf, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandConsCand(f, apf, config)
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
)
or
// read
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, returnApf, apf0, config) and
- flowCandFwdConsCand(f, apf0, config) and
- apf.headUsesContent(f)
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
)
or
// flow into a callable
@@ -1417,36 +1439,40 @@ private predicate flowCand0(
else returnApf = TAccessPathFrontNone()
}
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
+}
+
pragma[nomagic]
private predicate flowCandRead(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
exists(Node mid |
- readCand2(node, f, mid, config) and
+ readCandFwd(node, tc, apf, mid, config) and
flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
exists(Node mid |
- storeCand2(node, f, mid, config) and
- flowCand(mid, toReturn, returnApf, apf0, config)
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
- flowCandFwdConsCand(f, apf, config) and
- exists(Node n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, _, apf, config)
- )
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
}
pragma[nomagic]
@@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned(
)
}
-private newtype TAccessPath =
+private newtype TAccessPathApprox =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
- * 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 Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1522,20 +1548,18 @@ 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(Content 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)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content 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 Content f;
+private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil {
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathApproxConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) }
}
-private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons {
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override AccessPathApprox pop(TypedContent head) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ result = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ result = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private 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 `apa` from a source
+ * in the configuration `config`.
*
- * The Boolean `fromArg` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * The call context `cc` records whether the node is reached through an
+ * argument in a call, and if so, `argApa` records the approximate access path
+ * of that argument.
*/
private predicate flowFwd(
- Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
- Configuration config
+ Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, Configuration config
) {
- flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowFwd0(node, cc, argApa, apf, apa, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- Node node, boolean fromArg, 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
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
- exists(Node mid |
- flowFwd(mid, fromArg, argAp, apf, ap, config) and
- localFlowBigStep(mid, node, true, _, config, _)
+ exists(Node mid, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and
+ localFlowBigStep(mid, node, true, _, config, localCC)
)
or
- exists(Node mid, AccessPathNil nil |
- flowFwd(mid, fromArg, argAp, _, nil, config) and
- localFlowBigStep(mid, node, false, apf, config, _) and
- apf = ap.(AccessPathNil).getFront()
+ exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and
+ localFlowBigStep(mid, node, false, apf, config, localCC) and
+ 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
- fromArg = false and
- argAp = TAccessPathNone()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone()
)
or
- exists(Node mid, AccessPathNil nil |
+ exists(Node mid, AccessPathApproxNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
)
)
or
// store
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
- ap = push(f, ap0)
- )
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config))
or
// read
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
- flowFwdConsCand(f, apf, ap, config)
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and
+ flowFwdConsCand(tc, apf, apa, config)
)
or
// flow into a callable
- flowFwdIn(_, node, _, _, apf, ap, config) and
- fromArg = true 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 |
- flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
- fromArg = false
+ exists(DataFlowCallable c |
+ 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, fromArg, 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, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, LocalCallContext localCC, Configuration config
+) {
+ flowFwd(node, cc, argApa, apf, apa, config) and
+ localFlowEntry(node, config) and
+ localCC = getLocalCallContext(cc, node.getEnclosingCallable())
+}
+
pragma[nomagic]
private predicate flowFwdStore(
- Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- 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, fromArg, argAp, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ flowFwd(mid, cc, argApa, apf0, apa0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeCand2(mid, f, node, config) and
- flowCand(mid, _, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- flowCandConsCand(f, apf0, config) and
- apf.headUsesContent(f) and
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
flowCand(node, _, _, apf, unbind(config))
}
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2,
+ CallContext cc, AccessPathApproxOption argApa, Configuration config
+) {
+ flowFwd(node1, cc, argApa, apf0, apa0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
+}
+
pragma[nomagic]
private predicate flowFwdRead(
- Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
- Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc,
+ AccessPathApproxOption argApa, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
private predicate flowFwdConsCand(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+ TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(Node n |
- flowFwd(n, _, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ flowFwd(n, _, _, apf, apa, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
)
}
pragma[nomagic]
private predicate flowFwdIn(
- DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
- flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
+ flowFwd(arg, outercc, argApa, apf, apa, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
- flowCand(p, _, _, _, unbind(config))
+ 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, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
- flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowFwd(ret, innercc, argApa, apf, apa, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
- flowCand(node, _, _, _, unbind(config))
+ innerc = ret.getEnclosingCallable() and
+ flowCand(node, _, _, _, unbind(config)) and
+ (
+ resolveReturn(innercc, innerc, call)
+ or
+ 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, true, TAccessPathSome(argAp), apf, ap, config)
+ flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa,
+ config)
}
/**
@@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
- DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+ DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa,
+ Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
- flowFwdIn(call, p, fromArg, 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 `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
- * 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(Content f |
- flowStore(f, node, toReturn, returnAp, ap, config) and
- flowConsCand(f, ap, config)
+ exists(TypedContent tc |
+ 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, true, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- storeCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, apa, _, _, _, config) and
+ apa0 = push(tc, apa)
}
pragma[nomagic]
private predicate flowStore(
- Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- readCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, _, config) and
- ap0 = pop(f, ap) and
- flowFwdConsCand(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, apa, _, _, _, config) and
+ apa0 = pop(tc, apa) and
+ flowFwdConsCand(tc, _, apa0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, 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, f, 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)
}
/**
@@ -1969,12 +2028,13 @@ 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 |
- flowOut(call, ret, toReturn, returnAp, ap, config) and
- flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
+ exists(ReturnNodeExt ret, CallContextCall ccc |
+ flowOut(call, ret, toReturn, returnApa, apa, config) and
+ flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and
+ ccc.matchesCall(call)
)
}
@@ -1985,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, true, TAccessPathSome(ap), _, ap0, config)
- )
- }
+ TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) }
/**
* A context for generating flow summaries. This represents flow entry through
@@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
}
}
+private newtype TAccessPath =
+ TAccessPathNil(DataFlowType t) or
+ TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) }
+
private newtype TPathNode =
TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
@@ -2041,13 +2107,13 @@ private newtype TPathNode =
config.isSource(node) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TNil(getErasedNodeTypeBound(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) and
config = mid.getConfiguration() and
- flow(node, _, _, ap, unbind(config))
+ flow(node, _, _, ap.getApprox(), unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2059,12 +2125,99 @@ 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, _, _, TAccessPathNil(_)) 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 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) }
+
+ 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 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() {
+ 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() }
+}
+
/**
* 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.
@@ -2098,14 +2251,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TNil(getErasedNodeTypeBound(node))
+ ap = TAccessPathNil(getNodeType(node))
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
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
- not innercc instanceof CallContextCall and
- ap = mid.getAp() and
+ innercc instanceof CallContextNoCall and
+ apa = mid.getAp().getApprox() 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)
@@ -2321,10 +2494,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)
}
/**
@@ -2333,10 +2506,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)
)
}
@@ -2345,22 +2517,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 = ap.getApprox()
)
}
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)
)
}
@@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0(
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
AccessPath ap
) {
- pathIntoArg(mid, i, outercc, call, ap) and
- callable = resolveCall(call, outercc) and
- parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration())
+ exists(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())
+ )
}
/**
@@ -2403,7 +2578,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, AccessPath ap, AccessPathApprox apa,
+ Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
@@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough(
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp() and
+ apa = ap.getApprox() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
@@ -2419,11 +2596,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()))
)
}
@@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0(
*/
pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
- exists(DataFlowCall call, ReturnKindExt kind |
- pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
+ exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
+ pathThroughCallable0(call, mid, kind, cc, ap, apa) and
+ out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration()))
)
}
@@ -2521,10 +2699,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2533,7 +2711,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2544,7 +2722,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2562,15 +2740,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2591,7 +2769,7 @@ private module FlowExploration {
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
@@ -2608,7 +2786,7 @@ private module FlowExploration {
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), ap.getType())
+ then compatibleTypes(getNodeType(node), ap.getType())
else any()
)
}
@@ -2721,7 +2899,7 @@ private module FlowExploration {
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
)
or
@@ -2737,7 +2915,7 @@ private module FlowExploration {
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -2746,11 +2924,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getNodeType(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2769,35 +2948,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- store(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
@@ -2806,7 +2992,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
- not innercc instanceof CallContextCall and
+ innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = 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 f876c04d6c66..8bc3d75ff86e 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- store(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- nodeCandFwd1IsStored(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
exists(Node mid |
nodeCandFwd1(mid, fromArg, config) and
- read(mid, f, node)
+ read(mid, c, node)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
- exists(Node mid, Node node |
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
nodeCandFwd1(mid, config) and
- store(mid, f, node)
+ store(mid, tc, node, _) and
+ c = tc.getContent()
)
}
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- nodeCand1IsRead(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate nodeCand1IsRead(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- nodeCandFwd1IsStored(f, unbind(config)) and
- store(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
- nodeCand1IsRead(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
- nodeCand1(n2, unbind(config)) and
- store(n1, f, n2)
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
+ )
}
pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- read(n1, f, n2)
+ read(n1, c, n2)
}
pragma[noinline]
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
)
or
// store
- exists(Node mid, Content f |
+ exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, _, config) and
- store(mid, f, node, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, argStored, config) and
- nodeCandFwd2IsStored(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
// flow into a callable
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCand1(node, unbind(config)) and
nodeCandFwd2(mid, _, _, stored, config) and
- store(mid, f, node, config)
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
private predicate nodeCandFwd2Read(
- Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
) {
exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, true, config) and
- read(mid, f, node, config)
+ read(mid, c, node, config)
)
}
@@ -896,15 +901,15 @@ private predicate nodeCand2(
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, returnRead, read, config) and
- nodeCand2IsRead(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(Node mid, Content f, boolean read0 |
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
@@ -930,51 +935,51 @@ private predicate nodeCand2(
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd2(node, _, _, true, unbind(config)) and
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
Configuration config
) {
exists(Node mid |
- store(node, f, mid, config) and
+ storeCand1(node, c, mid, config) and
nodeCand2(mid, toReturn, returnRead, true, config) and
nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
pragma[nomagic]
-private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
exists(Node node |
- nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2Store(c, node, _, _, stored, conf) and
nodeCand2(node, _, _, stored, conf)
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
+ * Holds if `c` is the target of both a store and a read in the path graph
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
exists(boolean apNonEmpty |
- nodeCand2IsStored(f, apNonEmpty, conf) and
- nodeCand2IsRead(f, apNonEmpty, conf)
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
)
}
@@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2(
}
private module LocalFlowBigStep {
+ /**
+ * A node where some checking is required, and hence the big-step relation
+ * is not allowed to step over.
+ */
+ private class FlowCheckNode extends Node {
+ FlowCheckNode() {
+ this instanceof CastNode or
+ clearsContent(this, _)
+ }
+ }
+
/**
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1058,9 +1074,9 @@ private module LocalFlowBigStep {
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
node instanceof OutNodeExt or
- store(_, _, node) or
+ store(_, _, node, _) or
read(_, _, node) or
- node instanceof CastNode
+ node instanceof FlowCheckNode
)
}
@@ -1074,11 +1090,11 @@ private module LocalFlowBigStep {
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next) or
+ store(node, _, next, _) or
read(node, _, next)
)
or
- node instanceof CastNode
+ node instanceof FlowCheckNode
or
config.isSink(node)
}
@@ -1108,11 +1124,11 @@ private module LocalFlowBigStep {
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getErasedNodeTypeBound(node1)
+ t = getNodeType(node1)
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getErasedNodeTypeBound(node2)
+ t = getNodeType(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
@@ -1122,16 +1138,16 @@ private module LocalFlowBigStep {
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
localFlowStepNodeCand1(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getErasedNodeTypeBound(node2) and
+ t = getNodeType(node2) and
nodeCand2(node2, unbind(config))
)
)
@@ -1154,19 +1170,21 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2, config) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
nodeCand2(node2, _, _, true, unbind(config)) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
@@ -1183,9 +1201,8 @@ private predicate flowCandFwd(
Configuration config
) {
flowCandFwd0(node, fromArg, argApf, apf, config) and
- if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
- else any()
+ not apf.isClearedAt(node) and
+ if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any()
}
pragma[nomagic]
@@ -1197,7 +1214,7 @@ private predicate flowCandFwd0(
config.isSource(node) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
or
exists(Node mid |
flowCandFwd(mid, fromArg, argApf, apf, config) and
@@ -1223,21 +1240,22 @@ private predicate flowCandFwd0(
additionalJumpStep(mid, node, config) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
)
or
// store
- exists(Node mid, Content f |
- flowCandFwd(mid, fromArg, argApf, _, config) and
- storeCand2(mid, f, node, config) and
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
nodeCand2(node, _, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
)
or
// read
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, argApf, config) and
- flowCandFwdConsCand(f, apf, config) and
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
)
or
@@ -1261,24 +1279,30 @@ private predicate flowCandFwd0(
}
pragma[nomagic]
-private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
- exists(Node mid, Node n |
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
flowCandFwd(mid, _, _, apf, config) and
- storeCand2(mid, f, n, config) and
+ storeCand2(mid, tc, n, contentType, config) and
nodeCand2(n, _, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+ compatibleTypes(apf.getType(), contentType)
)
}
+pragma[nomagic]
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
pragma[nomagic]
private predicate flowCandFwdRead(
- Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, argApf, apf0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
- )
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
}
pragma[nomagic]
@@ -1385,17 +1409,15 @@ private predicate flowCand0(
)
or
// store
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, returnApf, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandConsCand(f, apf, config)
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
)
or
// read
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, returnApf, apf0, config) and
- flowCandFwdConsCand(f, apf0, config) and
- apf.headUsesContent(f)
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
)
or
// flow into a callable
@@ -1417,36 +1439,40 @@ private predicate flowCand0(
else returnApf = TAccessPathFrontNone()
}
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
+}
+
pragma[nomagic]
private predicate flowCandRead(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
exists(Node mid |
- readCand2(node, f, mid, config) and
+ readCandFwd(node, tc, apf, mid, config) and
flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
exists(Node mid |
- storeCand2(node, f, mid, config) and
- flowCand(mid, toReturn, returnApf, apf0, config)
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
- flowCandFwdConsCand(f, apf, config) and
- exists(Node n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, _, apf, config)
- )
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
}
pragma[nomagic]
@@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned(
)
}
-private newtype TAccessPath =
+private newtype TAccessPathApprox =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
- * 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 Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1522,20 +1548,18 @@ 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(Content 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)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content 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 Content f;
+private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil {
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathApproxConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) }
}
-private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons {
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override AccessPathApprox pop(TypedContent head) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ result = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ result = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private 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 `apa` from a source
+ * in the configuration `config`.
*
- * The Boolean `fromArg` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * The call context `cc` records whether the node is reached through an
+ * argument in a call, and if so, `argApa` records the approximate access path
+ * of that argument.
*/
private predicate flowFwd(
- Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
- Configuration config
+ Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, Configuration config
) {
- flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowFwd0(node, cc, argApa, apf, apa, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- Node node, boolean fromArg, 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
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
- exists(Node mid |
- flowFwd(mid, fromArg, argAp, apf, ap, config) and
- localFlowBigStep(mid, node, true, _, config, _)
+ exists(Node mid, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and
+ localFlowBigStep(mid, node, true, _, config, localCC)
)
or
- exists(Node mid, AccessPathNil nil |
- flowFwd(mid, fromArg, argAp, _, nil, config) and
- localFlowBigStep(mid, node, false, apf, config, _) and
- apf = ap.(AccessPathNil).getFront()
+ exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and
+ localFlowBigStep(mid, node, false, apf, config, localCC) and
+ 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
- fromArg = false and
- argAp = TAccessPathNone()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone()
)
or
- exists(Node mid, AccessPathNil nil |
+ exists(Node mid, AccessPathApproxNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
)
)
or
// store
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
- ap = push(f, ap0)
- )
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config))
or
// read
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
- flowFwdConsCand(f, apf, ap, config)
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and
+ flowFwdConsCand(tc, apf, apa, config)
)
or
// flow into a callable
- flowFwdIn(_, node, _, _, apf, ap, config) and
- fromArg = true 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 |
- flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
- fromArg = false
+ exists(DataFlowCallable c |
+ 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, fromArg, 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, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, LocalCallContext localCC, Configuration config
+) {
+ flowFwd(node, cc, argApa, apf, apa, config) and
+ localFlowEntry(node, config) and
+ localCC = getLocalCallContext(cc, node.getEnclosingCallable())
+}
+
pragma[nomagic]
private predicate flowFwdStore(
- Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- 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, fromArg, argAp, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ flowFwd(mid, cc, argApa, apf0, apa0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeCand2(mid, f, node, config) and
- flowCand(mid, _, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- flowCandConsCand(f, apf0, config) and
- apf.headUsesContent(f) and
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
flowCand(node, _, _, apf, unbind(config))
}
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2,
+ CallContext cc, AccessPathApproxOption argApa, Configuration config
+) {
+ flowFwd(node1, cc, argApa, apf0, apa0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
+}
+
pragma[nomagic]
private predicate flowFwdRead(
- Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
- Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc,
+ AccessPathApproxOption argApa, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
private predicate flowFwdConsCand(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+ TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(Node n |
- flowFwd(n, _, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ flowFwd(n, _, _, apf, apa, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
)
}
pragma[nomagic]
private predicate flowFwdIn(
- DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
- flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
+ flowFwd(arg, outercc, argApa, apf, apa, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
- flowCand(p, _, _, _, unbind(config))
+ 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, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
- flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowFwd(ret, innercc, argApa, apf, apa, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
- flowCand(node, _, _, _, unbind(config))
+ innerc = ret.getEnclosingCallable() and
+ flowCand(node, _, _, _, unbind(config)) and
+ (
+ resolveReturn(innercc, innerc, call)
+ or
+ 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, true, TAccessPathSome(argAp), apf, ap, config)
+ flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa,
+ config)
}
/**
@@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
- DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+ DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa,
+ Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
- flowFwdIn(call, p, fromArg, 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 `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
- * 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(Content f |
- flowStore(f, node, toReturn, returnAp, ap, config) and
- flowConsCand(f, ap, config)
+ exists(TypedContent tc |
+ 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, true, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- storeCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, apa, _, _, _, config) and
+ apa0 = push(tc, apa)
}
pragma[nomagic]
private predicate flowStore(
- Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- readCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, _, config) and
- ap0 = pop(f, ap) and
- flowFwdConsCand(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, apa, _, _, _, config) and
+ apa0 = pop(tc, apa) and
+ flowFwdConsCand(tc, _, apa0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, 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, f, 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)
}
/**
@@ -1969,12 +2028,13 @@ 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 |
- flowOut(call, ret, toReturn, returnAp, ap, config) and
- flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
+ exists(ReturnNodeExt ret, CallContextCall ccc |
+ flowOut(call, ret, toReturn, returnApa, apa, config) and
+ flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and
+ ccc.matchesCall(call)
)
}
@@ -1985,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, true, TAccessPathSome(ap), _, ap0, config)
- )
- }
+ TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) }
/**
* A context for generating flow summaries. This represents flow entry through
@@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
}
}
+private newtype TAccessPath =
+ TAccessPathNil(DataFlowType t) or
+ TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) }
+
private newtype TPathNode =
TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
@@ -2041,13 +2107,13 @@ private newtype TPathNode =
config.isSource(node) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TNil(getErasedNodeTypeBound(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) and
config = mid.getConfiguration() and
- flow(node, _, _, ap, unbind(config))
+ flow(node, _, _, ap.getApprox(), unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2059,12 +2125,99 @@ 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, _, _, TAccessPathNil(_)) 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 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) }
+
+ 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 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() {
+ 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() }
+}
+
/**
* 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.
@@ -2098,14 +2251,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TNil(getErasedNodeTypeBound(node))
+ ap = TAccessPathNil(getNodeType(node))
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
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
- not innercc instanceof CallContextCall and
- ap = mid.getAp() and
+ innercc instanceof CallContextNoCall and
+ apa = mid.getAp().getApprox() 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)
@@ -2321,10 +2494,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)
}
/**
@@ -2333,10 +2506,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)
)
}
@@ -2345,22 +2517,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 = ap.getApprox()
)
}
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)
)
}
@@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0(
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
AccessPath ap
) {
- pathIntoArg(mid, i, outercc, call, ap) and
- callable = resolveCall(call, outercc) and
- parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration())
+ exists(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())
+ )
}
/**
@@ -2403,7 +2578,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, AccessPath ap, AccessPathApprox apa,
+ Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
@@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough(
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp() and
+ apa = ap.getApprox() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
@@ -2419,11 +2596,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()))
)
}
@@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0(
*/
pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
- exists(DataFlowCall call, ReturnKindExt kind |
- pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
+ exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
+ pathThroughCallable0(call, mid, kind, cc, ap, apa) and
+ out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration()))
)
}
@@ -2521,10 +2699,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2533,7 +2711,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2544,7 +2722,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2562,15 +2740,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2591,7 +2769,7 @@ private module FlowExploration {
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
@@ -2608,7 +2786,7 @@ private module FlowExploration {
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), ap.getType())
+ then compatibleTypes(getNodeType(node), ap.getType())
else any()
)
}
@@ -2721,7 +2899,7 @@ private module FlowExploration {
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
)
or
@@ -2737,7 +2915,7 @@ private module FlowExploration {
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -2746,11 +2924,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getNodeType(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2769,35 +2948,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- store(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
@@ -2806,7 +2992,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
- not innercc instanceof CallContextCall and
+ innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = 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 f876c04d6c66..8bc3d75ff86e 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- store(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- nodeCandFwd1IsStored(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
exists(Node mid |
nodeCandFwd1(mid, fromArg, config) and
- read(mid, f, node)
+ read(mid, c, node)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
- exists(Node mid, Node node |
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
nodeCandFwd1(mid, config) and
- store(mid, f, node)
+ store(mid, tc, node, _) and
+ c = tc.getContent()
)
}
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- nodeCand1IsRead(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate nodeCand1IsRead(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- nodeCandFwd1IsStored(f, unbind(config)) and
- store(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
- nodeCand1IsRead(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
- nodeCand1(n2, unbind(config)) and
- store(n1, f, n2)
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
+ )
}
pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- read(n1, f, n2)
+ read(n1, c, n2)
}
pragma[noinline]
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
)
or
// store
- exists(Node mid, Content f |
+ exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, _, config) and
- store(mid, f, node, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, argStored, config) and
- nodeCandFwd2IsStored(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
// flow into a callable
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCand1(node, unbind(config)) and
nodeCandFwd2(mid, _, _, stored, config) and
- store(mid, f, node, config)
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
private predicate nodeCandFwd2Read(
- Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
) {
exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, true, config) and
- read(mid, f, node, config)
+ read(mid, c, node, config)
)
}
@@ -896,15 +901,15 @@ private predicate nodeCand2(
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, returnRead, read, config) and
- nodeCand2IsRead(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(Node mid, Content f, boolean read0 |
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
@@ -930,51 +935,51 @@ private predicate nodeCand2(
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd2(node, _, _, true, unbind(config)) and
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
Configuration config
) {
exists(Node mid |
- store(node, f, mid, config) and
+ storeCand1(node, c, mid, config) and
nodeCand2(mid, toReturn, returnRead, true, config) and
nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
pragma[nomagic]
-private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
exists(Node node |
- nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2Store(c, node, _, _, stored, conf) and
nodeCand2(node, _, _, stored, conf)
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
+ * Holds if `c` is the target of both a store and a read in the path graph
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
exists(boolean apNonEmpty |
- nodeCand2IsStored(f, apNonEmpty, conf) and
- nodeCand2IsRead(f, apNonEmpty, conf)
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
)
}
@@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2(
}
private module LocalFlowBigStep {
+ /**
+ * A node where some checking is required, and hence the big-step relation
+ * is not allowed to step over.
+ */
+ private class FlowCheckNode extends Node {
+ FlowCheckNode() {
+ this instanceof CastNode or
+ clearsContent(this, _)
+ }
+ }
+
/**
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1058,9 +1074,9 @@ private module LocalFlowBigStep {
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
node instanceof OutNodeExt or
- store(_, _, node) or
+ store(_, _, node, _) or
read(_, _, node) or
- node instanceof CastNode
+ node instanceof FlowCheckNode
)
}
@@ -1074,11 +1090,11 @@ private module LocalFlowBigStep {
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next) or
+ store(node, _, next, _) or
read(node, _, next)
)
or
- node instanceof CastNode
+ node instanceof FlowCheckNode
or
config.isSink(node)
}
@@ -1108,11 +1124,11 @@ private module LocalFlowBigStep {
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getErasedNodeTypeBound(node1)
+ t = getNodeType(node1)
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getErasedNodeTypeBound(node2)
+ t = getNodeType(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
@@ -1122,16 +1138,16 @@ private module LocalFlowBigStep {
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
localFlowStepNodeCand1(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getErasedNodeTypeBound(node2) and
+ t = getNodeType(node2) and
nodeCand2(node2, unbind(config))
)
)
@@ -1154,19 +1170,21 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2, config) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
nodeCand2(node2, _, _, true, unbind(config)) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
@@ -1183,9 +1201,8 @@ private predicate flowCandFwd(
Configuration config
) {
flowCandFwd0(node, fromArg, argApf, apf, config) and
- if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
- else any()
+ not apf.isClearedAt(node) and
+ if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any()
}
pragma[nomagic]
@@ -1197,7 +1214,7 @@ private predicate flowCandFwd0(
config.isSource(node) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
or
exists(Node mid |
flowCandFwd(mid, fromArg, argApf, apf, config) and
@@ -1223,21 +1240,22 @@ private predicate flowCandFwd0(
additionalJumpStep(mid, node, config) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
)
or
// store
- exists(Node mid, Content f |
- flowCandFwd(mid, fromArg, argApf, _, config) and
- storeCand2(mid, f, node, config) and
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
nodeCand2(node, _, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
)
or
// read
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, argApf, config) and
- flowCandFwdConsCand(f, apf, config) and
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
)
or
@@ -1261,24 +1279,30 @@ private predicate flowCandFwd0(
}
pragma[nomagic]
-private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
- exists(Node mid, Node n |
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
flowCandFwd(mid, _, _, apf, config) and
- storeCand2(mid, f, n, config) and
+ storeCand2(mid, tc, n, contentType, config) and
nodeCand2(n, _, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+ compatibleTypes(apf.getType(), contentType)
)
}
+pragma[nomagic]
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
pragma[nomagic]
private predicate flowCandFwdRead(
- Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, argApf, apf0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
- )
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
}
pragma[nomagic]
@@ -1385,17 +1409,15 @@ private predicate flowCand0(
)
or
// store
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, returnApf, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandConsCand(f, apf, config)
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
)
or
// read
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, returnApf, apf0, config) and
- flowCandFwdConsCand(f, apf0, config) and
- apf.headUsesContent(f)
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
)
or
// flow into a callable
@@ -1417,36 +1439,40 @@ private predicate flowCand0(
else returnApf = TAccessPathFrontNone()
}
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
+}
+
pragma[nomagic]
private predicate flowCandRead(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
exists(Node mid |
- readCand2(node, f, mid, config) and
+ readCandFwd(node, tc, apf, mid, config) and
flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
exists(Node mid |
- storeCand2(node, f, mid, config) and
- flowCand(mid, toReturn, returnApf, apf0, config)
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
- flowCandFwdConsCand(f, apf, config) and
- exists(Node n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, _, apf, config)
- )
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
}
pragma[nomagic]
@@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned(
)
}
-private newtype TAccessPath =
+private newtype TAccessPathApprox =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
- * 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 Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1522,20 +1548,18 @@ 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(Content 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)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content 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 Content f;
+private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil {
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathApproxConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) }
}
-private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons {
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override AccessPathApprox pop(TypedContent head) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ result = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ result = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private 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 `apa` from a source
+ * in the configuration `config`.
*
- * The Boolean `fromArg` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * The call context `cc` records whether the node is reached through an
+ * argument in a call, and if so, `argApa` records the approximate access path
+ * of that argument.
*/
private predicate flowFwd(
- Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
- Configuration config
+ Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, Configuration config
) {
- flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowFwd0(node, cc, argApa, apf, apa, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- Node node, boolean fromArg, 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
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
- exists(Node mid |
- flowFwd(mid, fromArg, argAp, apf, ap, config) and
- localFlowBigStep(mid, node, true, _, config, _)
+ exists(Node mid, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and
+ localFlowBigStep(mid, node, true, _, config, localCC)
)
or
- exists(Node mid, AccessPathNil nil |
- flowFwd(mid, fromArg, argAp, _, nil, config) and
- localFlowBigStep(mid, node, false, apf, config, _) and
- apf = ap.(AccessPathNil).getFront()
+ exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and
+ localFlowBigStep(mid, node, false, apf, config, localCC) and
+ 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
- fromArg = false and
- argAp = TAccessPathNone()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone()
)
or
- exists(Node mid, AccessPathNil nil |
+ exists(Node mid, AccessPathApproxNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
)
)
or
// store
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
- ap = push(f, ap0)
- )
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config))
or
// read
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
- flowFwdConsCand(f, apf, ap, config)
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and
+ flowFwdConsCand(tc, apf, apa, config)
)
or
// flow into a callable
- flowFwdIn(_, node, _, _, apf, ap, config) and
- fromArg = true 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 |
- flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
- fromArg = false
+ exists(DataFlowCallable c |
+ 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, fromArg, 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, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, LocalCallContext localCC, Configuration config
+) {
+ flowFwd(node, cc, argApa, apf, apa, config) and
+ localFlowEntry(node, config) and
+ localCC = getLocalCallContext(cc, node.getEnclosingCallable())
+}
+
pragma[nomagic]
private predicate flowFwdStore(
- Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- 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, fromArg, argAp, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ flowFwd(mid, cc, argApa, apf0, apa0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeCand2(mid, f, node, config) and
- flowCand(mid, _, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- flowCandConsCand(f, apf0, config) and
- apf.headUsesContent(f) and
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
flowCand(node, _, _, apf, unbind(config))
}
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2,
+ CallContext cc, AccessPathApproxOption argApa, Configuration config
+) {
+ flowFwd(node1, cc, argApa, apf0, apa0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
+}
+
pragma[nomagic]
private predicate flowFwdRead(
- Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
- Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc,
+ AccessPathApproxOption argApa, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
private predicate flowFwdConsCand(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+ TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(Node n |
- flowFwd(n, _, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ flowFwd(n, _, _, apf, apa, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
)
}
pragma[nomagic]
private predicate flowFwdIn(
- DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
- flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
+ flowFwd(arg, outercc, argApa, apf, apa, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
- flowCand(p, _, _, _, unbind(config))
+ 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, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
- flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowFwd(ret, innercc, argApa, apf, apa, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
- flowCand(node, _, _, _, unbind(config))
+ innerc = ret.getEnclosingCallable() and
+ flowCand(node, _, _, _, unbind(config)) and
+ (
+ resolveReturn(innercc, innerc, call)
+ or
+ 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, true, TAccessPathSome(argAp), apf, ap, config)
+ flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa,
+ config)
}
/**
@@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
- DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+ DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa,
+ Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
- flowFwdIn(call, p, fromArg, 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 `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
- * 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(Content f |
- flowStore(f, node, toReturn, returnAp, ap, config) and
- flowConsCand(f, ap, config)
+ exists(TypedContent tc |
+ 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, true, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- storeCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, apa, _, _, _, config) and
+ apa0 = push(tc, apa)
}
pragma[nomagic]
private predicate flowStore(
- Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- readCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, _, config) and
- ap0 = pop(f, ap) and
- flowFwdConsCand(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, apa, _, _, _, config) and
+ apa0 = pop(tc, apa) and
+ flowFwdConsCand(tc, _, apa0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, 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, f, 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)
}
/**
@@ -1969,12 +2028,13 @@ 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 |
- flowOut(call, ret, toReturn, returnAp, ap, config) and
- flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
+ exists(ReturnNodeExt ret, CallContextCall ccc |
+ flowOut(call, ret, toReturn, returnApa, apa, config) and
+ flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and
+ ccc.matchesCall(call)
)
}
@@ -1985,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, true, TAccessPathSome(ap), _, ap0, config)
- )
- }
+ TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) }
/**
* A context for generating flow summaries. This represents flow entry through
@@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
}
}
+private newtype TAccessPath =
+ TAccessPathNil(DataFlowType t) or
+ TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) }
+
private newtype TPathNode =
TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
@@ -2041,13 +2107,13 @@ private newtype TPathNode =
config.isSource(node) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TNil(getErasedNodeTypeBound(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) and
config = mid.getConfiguration() and
- flow(node, _, _, ap, unbind(config))
+ flow(node, _, _, ap.getApprox(), unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2059,12 +2125,99 @@ 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, _, _, TAccessPathNil(_)) 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 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) }
+
+ 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 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() {
+ 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() }
+}
+
/**
* 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.
@@ -2098,14 +2251,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TNil(getErasedNodeTypeBound(node))
+ ap = TAccessPathNil(getNodeType(node))
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
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
- not innercc instanceof CallContextCall and
- ap = mid.getAp() and
+ innercc instanceof CallContextNoCall and
+ apa = mid.getAp().getApprox() 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)
@@ -2321,10 +2494,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)
}
/**
@@ -2333,10 +2506,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)
)
}
@@ -2345,22 +2517,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 = ap.getApprox()
)
}
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)
)
}
@@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0(
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
AccessPath ap
) {
- pathIntoArg(mid, i, outercc, call, ap) and
- callable = resolveCall(call, outercc) and
- parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration())
+ exists(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())
+ )
}
/**
@@ -2403,7 +2578,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, AccessPath ap, AccessPathApprox apa,
+ Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
@@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough(
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp() and
+ apa = ap.getApprox() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
@@ -2419,11 +2596,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()))
)
}
@@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0(
*/
pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
- exists(DataFlowCall call, ReturnKindExt kind |
- pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
+ exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
+ pathThroughCallable0(call, mid, kind, cc, ap, apa) and
+ out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration()))
)
}
@@ -2521,10 +2699,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2533,7 +2711,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2544,7 +2722,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2562,15 +2740,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2591,7 +2769,7 @@ private module FlowExploration {
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
@@ -2608,7 +2786,7 @@ private module FlowExploration {
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), ap.getType())
+ then compatibleTypes(getNodeType(node), ap.getType())
else any()
)
}
@@ -2721,7 +2899,7 @@ private module FlowExploration {
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
)
or
@@ -2737,7 +2915,7 @@ private module FlowExploration {
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -2746,11 +2924,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getNodeType(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2769,35 +2948,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- store(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
@@ -2806,7 +2992,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
- not innercc instanceof CallContextCall and
+ innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
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 852f54974e24..892250f44bb8 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -22,7 +22,7 @@ private module Cached {
exists(int i |
viableParam(call, i, p) and
arg.argumentOf(call, i) and
- compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p))
+ compatibleTypes(getNodeType(arg), getNodeType(p))
)
}
@@ -147,174 +147,140 @@ private module Cached {
}
}
- private module LocalFlowBigStep {
- private predicate localFlowEntry(Node n) {
- Cand::cand(_, n) and
- (
- n instanceof ParameterNode or
- n instanceof OutNode or
- readStep(_, _, n) or
- n instanceof CastNode
- )
- }
-
- private predicate localFlowExit(Node n) {
- Cand::cand(_, n) and
- (
- n instanceof ArgumentNode
- or
- n instanceof ReturnNode
- or
- readStep(n, _, _)
- or
- n instanceof CastNode
- or
- n =
- any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun))
- .getPreUpdateNode()
- )
- }
-
- pragma[nomagic]
- private predicate localFlowStepPlus(Node node1, Node node2) {
- localFlowEntry(node1) and
- simpleLocalFlowStep(node1, node2) and
- node1 != node2
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid) and
- simpleLocalFlowStep(mid, node2) and
- not mid instanceof CastNode
- )
- }
-
- pragma[nomagic]
- predicate localFlowBigStep(Node node1, Node node2) {
- localFlowStepPlus(node1, node2) and
- localFlowExit(node2)
- }
- }
-
/**
* The final flow-through calculation:
*
- * - Input access paths are abstracted with a `ContentOption` parameter
- * that represents the head of the access path. `TContentNone()` means that
- * the access path is unrestricted.
+ * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`)
+ * or summarized as a single read step with before and after types recorded
+ * in the `ReadStepTypesOption` parameter.
* - Types are checked using the `compatibleTypes()` relation.
*/
private module Final {
/**
* Holds if `p` can flow to `node` in the same callable using only
- * value-preserving steps, not taking call contexts into account.
+ * value-preserving steps and possibly a single read step, not taking
+ * call contexts into account.
*
- * `contentIn` describes the content of `p` that can flow to `node`
- * (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
- predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) {
- parameterValueFlow0(p, node, contentIn) and
+ predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
+ parameterValueFlow0(p, node, read) and
if node instanceof CastingNode
then
// normal flow through
- contentIn = TContentNone() and
- compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
+ read = TReadStepTypesNone() and
+ compatibleTypes(getNodeType(p), getNodeType(node))
or
// getter
- exists(Content fIn |
- contentIn.getContent() = fIn and
- compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node))
- )
+ compatibleTypes(read.getContentType(), getNodeType(node))
else any()
}
pragma[nomagic]
- private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) {
+ private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
p = node and
Cand::cand(p, _) and
- contentIn = TContentNone()
+ read = TReadStepTypesNone()
or
// local flow
exists(Node mid |
- parameterValueFlow(p, mid, contentIn) and
- LocalFlowBigStep::localFlowBigStep(mid, node)
+ parameterValueFlow(p, mid, read) and
+ simpleLocalFlowStep(mid, node)
)
or
// read
- exists(Node mid, Content f |
- parameterValueFlow(p, mid, TContentNone()) and
- readStep(mid, f, node) and
- contentIn.getContent() = f and
+ exists(Node mid |
+ parameterValueFlow(p, mid, TReadStepTypesNone()) and
+ readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
+ read.getContentType()) and
Cand::parameterValueFlowReturnCand(p, _, true) and
- compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
+ compatibleTypes(getNodeType(p), read.getContainerType())
)
or
+ parameterValueFlow0_0(TReadStepTypesNone(), p, node, read)
+ }
+
+ pragma[nomagic]
+ private predicate parameterValueFlow0_0(
+ ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read
+ ) {
// flow through: no prior read
exists(ArgumentNode arg |
- parameterValueFlowArg(p, arg, TContentNone()) and
- argumentValueFlowsThrough(arg, contentIn, node)
+ parameterValueFlowArg(p, arg, mustBeNone) and
+ argumentValueFlowsThrough(arg, read, node)
)
or
// flow through: no read inside method
exists(ArgumentNode arg |
- parameterValueFlowArg(p, arg, contentIn) and
- argumentValueFlowsThrough(arg, TContentNone(), node)
+ parameterValueFlowArg(p, arg, read) and
+ argumentValueFlowsThrough(arg, mustBeNone, node)
)
}
pragma[nomagic]
private predicate parameterValueFlowArg(
- ParameterNode p, ArgumentNode arg, ContentOption contentIn
+ ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
) {
- parameterValueFlow(p, arg, contentIn) and
+ parameterValueFlow(p, arg, read) and
Cand::argumentValueFlowsThroughCand(arg, _, _)
}
pragma[nomagic]
private predicate argumentValueFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
) {
exists(ParameterNode param | viableParamArg(call, param, arg) |
- parameterValueFlowReturn(param, kind, contentIn)
+ parameterValueFlowReturn(param, kind, read)
)
}
/**
- * Holds if `arg` flows to `out` through a call using only value-preserving steps,
- * not taking call contexts into account.
+ * Holds if `arg` flows to `out` through a call using only
+ * value-preserving steps and possibly a single read step, not taking
+ * call contexts into account.
*
- * `contentIn` describes the content of `arg` that can flow to `out` (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
pragma[nomagic]
- predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) {
+ predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
exists(DataFlowCall call, ReturnKind kind |
- argumentValueFlowsThrough0(call, arg, kind, contentIn) and
+ argumentValueFlowsThrough0(call, arg, kind, read) and
out = getAnOutNode(call, kind)
|
// normal flow through
- contentIn = TContentNone() and
- compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
+ read = TReadStepTypesNone() and
+ compatibleTypes(getNodeType(arg), getNodeType(out))
or
// getter
- exists(Content fIn |
- contentIn.getContent() = fIn and
- compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
- compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out))
- )
+ compatibleTypes(getNodeType(arg), read.getContainerType()) and
+ compatibleTypes(read.getContentType(), getNodeType(out))
)
}
+ /**
+ * Holds if `arg` flows to `out` through a call using only
+ * value-preserving steps and a single read step, not taking call
+ * contexts into account, thus representing a getter-step.
+ */
+ predicate getterStep(ArgumentNode arg, Content c, Node out) {
+ argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
+ }
+
/**
* Holds if `p` can flow to a return node of kind `kind` in the same
- * callable using only value-preserving steps.
+ * callable using only value-preserving steps and possibly a single read
+ * step.
*
- * `contentIn` describes the content of `p` that can flow to the return
- * node (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
private predicate parameterValueFlowReturn(
- ParameterNode p, ReturnKind kind, ContentOption contentIn
+ ParameterNode p, ReturnKind kind, ReadStepTypesOption read
) {
exists(ReturnNode ret |
- parameterValueFlow(p, ret, contentIn) and
+ parameterValueFlow(p, ret, read) and
kind = ret.getKind()
)
}
@@ -323,37 +289,107 @@ private module Cached {
import Final
}
+ import FlowThrough
+
+ cached
+ private module DispatchWithCallContext {
+ /**
+ * Holds if the call context `ctx` reduces the set of viable run-time
+ * dispatch targets of call `call` in `c`.
+ */
+ cached
+ predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
+ exists(int tgts, int ctxtgts |
+ mayBenefitFromCallContext(call, c) and
+ c = viableCallable(ctx) and
+ ctxtgts = count(viableImplInCallContext(call, ctx)) and
+ tgts = strictcount(viableCallable(call)) and
+ ctxtgts < tgts
+ )
+ }
+
+ /**
+ * Gets a viable run-time dispatch target for the call `call` in the
+ * context `ctx`. This is restricted to those calls for which a context
+ * makes a difference.
+ */
+ cached
+ DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
+ result = viableImplInCallContext(call, ctx) and
+ reducedViableImplInCallContext(call, _, ctx)
+ }
+
+ /**
+ * Holds if flow returning from callable `c` to call `call` might return
+ * further and if this path restricts the set of call sites that can be
+ * returned to.
+ */
+ cached
+ predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
+ exists(int tgts, int ctxtgts |
+ mayBenefitFromCallContext(call, _) and
+ c = viableCallable(call) and
+ ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
+ tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
+ ctxtgts < tgts
+ )
+ }
+
+ /**
+ * Gets a viable run-time dispatch target for the call `call` in the
+ * context `ctx`. This is restricted to those calls and results for which
+ * the return flow from the result to `call` restricts the possible context
+ * `ctx`.
+ */
+ cached
+ DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
+ result = viableImplInCallContext(call, ctx) and
+ reducedViableImplInReturn(result, call)
+ }
+ }
+
+ import DispatchWithCallContext
+
/**
* Holds if `p` can flow to the pre-update node associated with post-update
* node `n`, in the same callable, using only value-preserving steps.
*/
cached
predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
- parameterValueFlow(p, n.getPreUpdateNode(), TContentNone())
+ parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
}
- /**
- * Holds if data can flow from `node1` to `node2` via a direct assignment to
- * `f`.
- *
- * This includes reverse steps through reads when the result of the read has
- * been stored into, in order to handle cases like `x.f1.f2 = y`.
- */
- cached
- predicate store(Node node1, Content f, Node node2) {
- storeStep(node1, f, node2) and readStep(_, f, _)
+ private predicate store(
+ Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
+ ) {
+ storeStep(node1, c, node2) and
+ readStep(_, c, _) and
+ contentType = getNodeType(node1) and
+ containerType = getNodeType(node2)
or
exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
- argumentValueFlowsThrough(n2, TContentSome(f), n1)
+ argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
or
- readStep(n2, f, n1)
+ readStep(n2, c, n1) and
+ contentType = getNodeType(n1) and
+ containerType = getNodeType(n2)
)
}
- import FlowThrough
+ /**
+ * Holds if data can flow from `node1` to `node2` via a direct assignment to
+ * `f`.
+ *
+ * This includes reverse steps through reads when the result of the read has
+ * been stored into, in order to handle cases like `x.f1.f2 = y`.
+ */
+ cached
+ predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
+ store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
+ }
/**
* Holds if the call context `call` either improves virtual dispatch in
@@ -397,10 +433,13 @@ private module Cached {
TBooleanNone() or
TBooleanSome(boolean b) { b = true or b = false }
+ cached
+ newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
+
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
- TFrontHead(Content f)
+ TFrontHead(TypedContent tc)
cached
newtype TAccessPathFrontOption =
@@ -415,25 +454,38 @@ class CastingNode extends Node {
CastingNode() {
this instanceof ParameterNode or
this instanceof CastNode or
- this instanceof OutNodeExt
+ this instanceof OutNodeExt or
+ // For reads, `x.f`, we want to check that the tracked type after the read (which
+ // is obtained by popping the head of the access path stack) is compatible with
+ // the type of `x.f`.
+ readStep(_, _, this)
}
}
-newtype TContentOption =
- TContentNone() or
- TContentSome(Content f)
+private predicate readStepWithTypes(
+ Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
+) {
+ readStep(n1, c, n2) and
+ container = getNodeType(n1) and
+ content = getNodeType(n2)
+}
-private class ContentOption extends TContentOption {
- Content getContent() { this = TContentSome(result) }
+private newtype TReadStepTypesOption =
+ TReadStepTypesNone() or
+ TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) {
+ readStepWithTypes(_, container, c, _, content)
+ }
- predicate hasContent() { exists(this.getContent()) }
+private class ReadStepTypesOption extends TReadStepTypesOption {
+ predicate isSome() { this instanceof TReadStepTypesSome }
- string toString() {
- result = this.getContent().toString()
- or
- not this.hasContent() and
- result = ""
- }
+ DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) }
+
+ Content getContent() { this = TReadStepTypesSome(_, result, _) }
+
+ DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) }
+
+ string toString() { if this.isSome() then result = "Some(..)" else result = "None()" }
}
/**
@@ -460,13 +512,19 @@ abstract class CallContext extends TCallContext {
abstract predicate relevantFor(DataFlowCallable callable);
}
-class CallContextAny extends CallContext, TAnyCallContext {
+abstract class CallContextNoCall extends CallContext { }
+
+class CallContextAny extends CallContextNoCall, TAnyCallContext {
override string toString() { result = "CcAny" }
override predicate relevantFor(DataFlowCallable callable) { any() }
}
-abstract class CallContextCall extends CallContext { }
+abstract class CallContextCall extends CallContext {
+ /** Holds if this call context may be `call`. */
+ bindingset[call]
+ abstract predicate matchesCall(DataFlowCall call);
+}
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
override string toString() {
@@ -477,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
recordDataFlowCallSite(getCall(), callable)
}
+ override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
+
DataFlowCall getCall() { this = TSpecificCall(result) }
}
@@ -486,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
override predicate relevantFor(DataFlowCallable callable) {
exists(ParameterNode p | p.getEnclosingCallable() = callable)
}
+
+ override predicate matchesCall(DataFlowCall call) { any() }
}
-class CallContextReturn extends CallContext, TReturn {
+class CallContextReturn extends CallContextNoCall, TReturn {
override string toString() {
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
}
@@ -678,9 +740,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
result = viableCallable(call) and cc instanceof CallContextReturn
}
-pragma[noinline]
-DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) }
-
predicate read = readStep/3;
/** An optional Boolean value. */
@@ -692,6 +751,23 @@ class BooleanOption extends TBooleanOption {
}
}
+/** Content tagged with the type of a containing object. */
+class TypedContent extends MkTypedContent {
+ private Content c;
+ private DataFlowType t;
+
+ TypedContent() { this = MkTypedContent(c, t) }
+
+ /** Gets the content. */
+ Content getContent() { result = c }
+
+ /** Gets the container type. */
+ DataFlowType getContainerType() { result = t }
+
+ /** Gets a textual representation of this content. */
+ string toString() { result = c.toString() }
+}
+
/**
* The front of an access path. This is either a head or a nil.
*/
@@ -702,25 +778,36 @@ abstract class AccessPathFront extends TAccessPathFront {
abstract boolean toBoolNonEmpty();
- predicate headUsesContent(Content f) { this = TFrontHead(f) }
+ predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) }
+
+ predicate isClearedAt(Node n) {
+ exists(TypedContent tc |
+ this.headUsesContent(tc) and
+ clearsContent(n, tc.getContent())
+ )
+ }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
- override string toString() {
- exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
- }
+ private DataFlowType t;
+
+ AccessPathFrontNil() { this = TFrontNil(t) }
+
+ override string toString() { result = ppReprType(t) }
- override DataFlowType getType() { this = TFrontNil(result) }
+ override DataFlowType getType() { result = t }
override boolean toBoolNonEmpty() { result = false }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
- override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
+ private TypedContent tc;
- override DataFlowType getType() {
- exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
- }
+ AccessPathFrontHead() { this = TFrontHead(tc) }
+
+ override string toString() { result = tc.toString() }
+
+ override DataFlowType getType() { result = tc.getContainerType() }
override boolean toBoolNonEmpty() { result = true }
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
index 0dc3b8eff450..4e1cd281488f 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll
@@ -37,21 +37,12 @@ module Consistency {
)
}
- query predicate uniqueTypeBound(Node n, string msg) {
+ query predicate uniqueType(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
- c = count(n.getTypeBound()) and
+ c = count(getNodeType(n)) and
c != 1 and
- msg = "Node should have one type bound but has " + c + "."
- )
- }
-
- query predicate uniqueTypeRepr(Node n, string msg) {
- exists(int c |
- n instanceof RelevantNode and
- c = count(getErasedRepr(n.getTypeBound())) and
- c != 1 and
- msg = "Node should have one type representation but has " + c + "."
+ msg = "Node should have one type but has " + c + "."
)
}
@@ -104,7 +95,7 @@ module Consistency {
msg = "Local flow step does not preserve enclosing callable."
}
- private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) }
+ private DataFlowType typeRepr() { result = getNodeType(_) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
t = typeRepr() and
@@ -132,8 +123,18 @@ module Consistency {
n.getEnclosingCallable() != call.getEnclosingCallable()
}
+ // This predicate helps the compiler forget that in some languages
+ // it is impossible for a result of `getPreUpdateNode` to be an
+ // instance of `PostUpdateNode`.
+ private Node getPre(PostUpdateNode n) {
+ result = n.getPreUpdateNode()
+ or
+ none()
+ }
+
query predicate postIsNotPre(PostUpdateNode n, string msg) {
- n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
+ getPre(n) = n and
+ msg = "PostUpdateNode should not equal its pre-update node."
}
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
@@ -161,15 +162,14 @@ module Consistency {
msg = "Origin of readStep is missing a PostUpdateNode."
}
- query predicate storeIsPostUpdate(Node n, string msg) {
- storeStep(_, _, n) and
- not n instanceof PostUpdateNode and
- msg = "Store targets should be PostUpdateNodes."
- }
-
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not isImmutableOrUnobservable(n) and
msg = "ArgumentNode is missing PostUpdateNode."
}
+
+ query predicate postWithInFlow(PostUpdateNode n, string msg) {
+ simpleLocalFlowStep(_, n) and
+ msg = "PostUpdateNode should not be the target of local flow."
+ }
}
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 f876c04d6c66..8bc3d75ff86e 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- store(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- nodeCandFwd1IsStored(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
exists(Node mid |
nodeCandFwd1(mid, fromArg, config) and
- read(mid, f, node)
+ read(mid, c, node)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
- exists(Node mid, Node node |
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
nodeCandFwd1(mid, config) and
- store(mid, f, node)
+ store(mid, tc, node, _) and
+ c = tc.getContent()
)
}
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- nodeCand1IsRead(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate nodeCand1IsRead(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- nodeCandFwd1IsStored(f, unbind(config)) and
- store(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
- nodeCand1IsRead(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
- nodeCand1(n2, unbind(config)) and
- store(n1, f, n2)
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
+ )
}
pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- read(n1, f, n2)
+ read(n1, c, n2)
}
pragma[noinline]
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
)
or
// store
- exists(Node mid, Content f |
+ exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, _, config) and
- store(mid, f, node, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, argStored, config) and
- nodeCandFwd2IsStored(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
// flow into a callable
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCand1(node, unbind(config)) and
nodeCandFwd2(mid, _, _, stored, config) and
- store(mid, f, node, config)
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
private predicate nodeCandFwd2Read(
- Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
) {
exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, true, config) and
- read(mid, f, node, config)
+ read(mid, c, node, config)
)
}
@@ -896,15 +901,15 @@ private predicate nodeCand2(
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, returnRead, read, config) and
- nodeCand2IsRead(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(Node mid, Content f, boolean read0 |
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
@@ -930,51 +935,51 @@ private predicate nodeCand2(
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd2(node, _, _, true, unbind(config)) and
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
Configuration config
) {
exists(Node mid |
- store(node, f, mid, config) and
+ storeCand1(node, c, mid, config) and
nodeCand2(mid, toReturn, returnRead, true, config) and
nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
pragma[nomagic]
-private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
exists(Node node |
- nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2Store(c, node, _, _, stored, conf) and
nodeCand2(node, _, _, stored, conf)
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
+ * Holds if `c` is the target of both a store and a read in the path graph
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
exists(boolean apNonEmpty |
- nodeCand2IsStored(f, apNonEmpty, conf) and
- nodeCand2IsRead(f, apNonEmpty, conf)
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
)
}
@@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2(
}
private module LocalFlowBigStep {
+ /**
+ * A node where some checking is required, and hence the big-step relation
+ * is not allowed to step over.
+ */
+ private class FlowCheckNode extends Node {
+ FlowCheckNode() {
+ this instanceof CastNode or
+ clearsContent(this, _)
+ }
+ }
+
/**
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1058,9 +1074,9 @@ private module LocalFlowBigStep {
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
node instanceof OutNodeExt or
- store(_, _, node) or
+ store(_, _, node, _) or
read(_, _, node) or
- node instanceof CastNode
+ node instanceof FlowCheckNode
)
}
@@ -1074,11 +1090,11 @@ private module LocalFlowBigStep {
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next) or
+ store(node, _, next, _) or
read(node, _, next)
)
or
- node instanceof CastNode
+ node instanceof FlowCheckNode
or
config.isSink(node)
}
@@ -1108,11 +1124,11 @@ private module LocalFlowBigStep {
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getErasedNodeTypeBound(node1)
+ t = getNodeType(node1)
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getErasedNodeTypeBound(node2)
+ t = getNodeType(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
@@ -1122,16 +1138,16 @@ private module LocalFlowBigStep {
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
localFlowStepNodeCand1(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getErasedNodeTypeBound(node2) and
+ t = getNodeType(node2) and
nodeCand2(node2, unbind(config))
)
)
@@ -1154,19 +1170,21 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2, config) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
nodeCand2(node2, _, _, true, unbind(config)) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
@@ -1183,9 +1201,8 @@ private predicate flowCandFwd(
Configuration config
) {
flowCandFwd0(node, fromArg, argApf, apf, config) and
- if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
- else any()
+ not apf.isClearedAt(node) and
+ if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any()
}
pragma[nomagic]
@@ -1197,7 +1214,7 @@ private predicate flowCandFwd0(
config.isSource(node) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
or
exists(Node mid |
flowCandFwd(mid, fromArg, argApf, apf, config) and
@@ -1223,21 +1240,22 @@ private predicate flowCandFwd0(
additionalJumpStep(mid, node, config) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
)
or
// store
- exists(Node mid, Content f |
- flowCandFwd(mid, fromArg, argApf, _, config) and
- storeCand2(mid, f, node, config) and
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
nodeCand2(node, _, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
)
or
// read
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, argApf, config) and
- flowCandFwdConsCand(f, apf, config) and
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
)
or
@@ -1261,24 +1279,30 @@ private predicate flowCandFwd0(
}
pragma[nomagic]
-private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
- exists(Node mid, Node n |
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
flowCandFwd(mid, _, _, apf, config) and
- storeCand2(mid, f, n, config) and
+ storeCand2(mid, tc, n, contentType, config) and
nodeCand2(n, _, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+ compatibleTypes(apf.getType(), contentType)
)
}
+pragma[nomagic]
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
pragma[nomagic]
private predicate flowCandFwdRead(
- Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, argApf, apf0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
- )
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
}
pragma[nomagic]
@@ -1385,17 +1409,15 @@ private predicate flowCand0(
)
or
// store
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, returnApf, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandConsCand(f, apf, config)
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
)
or
// read
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, returnApf, apf0, config) and
- flowCandFwdConsCand(f, apf0, config) and
- apf.headUsesContent(f)
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
)
or
// flow into a callable
@@ -1417,36 +1439,40 @@ private predicate flowCand0(
else returnApf = TAccessPathFrontNone()
}
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
+}
+
pragma[nomagic]
private predicate flowCandRead(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
exists(Node mid |
- readCand2(node, f, mid, config) and
+ readCandFwd(node, tc, apf, mid, config) and
flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
exists(Node mid |
- storeCand2(node, f, mid, config) and
- flowCand(mid, toReturn, returnApf, apf0, config)
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
- flowCandFwdConsCand(f, apf, config) and
- exists(Node n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, _, apf, config)
- )
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
}
pragma[nomagic]
@@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned(
)
}
-private newtype TAccessPath =
+private newtype TAccessPathApprox =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
- * 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 Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1522,20 +1548,18 @@ 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(Content 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)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content 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 Content f;
+private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil {
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathApproxConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) }
}
-private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons {
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override AccessPathApprox pop(TypedContent head) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ result = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ result = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private 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 `apa` from a source
+ * in the configuration `config`.
*
- * The Boolean `fromArg` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * The call context `cc` records whether the node is reached through an
+ * argument in a call, and if so, `argApa` records the approximate access path
+ * of that argument.
*/
private predicate flowFwd(
- Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
- Configuration config
+ Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, Configuration config
) {
- flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowFwd0(node, cc, argApa, apf, apa, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- Node node, boolean fromArg, 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
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
- exists(Node mid |
- flowFwd(mid, fromArg, argAp, apf, ap, config) and
- localFlowBigStep(mid, node, true, _, config, _)
+ exists(Node mid, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and
+ localFlowBigStep(mid, node, true, _, config, localCC)
)
or
- exists(Node mid, AccessPathNil nil |
- flowFwd(mid, fromArg, argAp, _, nil, config) and
- localFlowBigStep(mid, node, false, apf, config, _) and
- apf = ap.(AccessPathNil).getFront()
+ exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and
+ localFlowBigStep(mid, node, false, apf, config, localCC) and
+ 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
- fromArg = false and
- argAp = TAccessPathNone()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone()
)
or
- exists(Node mid, AccessPathNil nil |
+ exists(Node mid, AccessPathApproxNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
)
)
or
// store
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
- ap = push(f, ap0)
- )
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config))
or
// read
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
- flowFwdConsCand(f, apf, ap, config)
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and
+ flowFwdConsCand(tc, apf, apa, config)
)
or
// flow into a callable
- flowFwdIn(_, node, _, _, apf, ap, config) and
- fromArg = true 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 |
- flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
- fromArg = false
+ exists(DataFlowCallable c |
+ 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, fromArg, 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, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, LocalCallContext localCC, Configuration config
+) {
+ flowFwd(node, cc, argApa, apf, apa, config) and
+ localFlowEntry(node, config) and
+ localCC = getLocalCallContext(cc, node.getEnclosingCallable())
+}
+
pragma[nomagic]
private predicate flowFwdStore(
- Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- 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, fromArg, argAp, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ flowFwd(mid, cc, argApa, apf0, apa0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeCand2(mid, f, node, config) and
- flowCand(mid, _, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- flowCandConsCand(f, apf0, config) and
- apf.headUsesContent(f) and
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
flowCand(node, _, _, apf, unbind(config))
}
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2,
+ CallContext cc, AccessPathApproxOption argApa, Configuration config
+) {
+ flowFwd(node1, cc, argApa, apf0, apa0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
+}
+
pragma[nomagic]
private predicate flowFwdRead(
- Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
- Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc,
+ AccessPathApproxOption argApa, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
private predicate flowFwdConsCand(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+ TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(Node n |
- flowFwd(n, _, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ flowFwd(n, _, _, apf, apa, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
)
}
pragma[nomagic]
private predicate flowFwdIn(
- DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
- flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
+ flowFwd(arg, outercc, argApa, apf, apa, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
- flowCand(p, _, _, _, unbind(config))
+ 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, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
- flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowFwd(ret, innercc, argApa, apf, apa, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
- flowCand(node, _, _, _, unbind(config))
+ innerc = ret.getEnclosingCallable() and
+ flowCand(node, _, _, _, unbind(config)) and
+ (
+ resolveReturn(innercc, innerc, call)
+ or
+ 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, true, TAccessPathSome(argAp), apf, ap, config)
+ flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa,
+ config)
}
/**
@@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
- DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+ DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa,
+ Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
- flowFwdIn(call, p, fromArg, 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 `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
- * 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(Content f |
- flowStore(f, node, toReturn, returnAp, ap, config) and
- flowConsCand(f, ap, config)
+ exists(TypedContent tc |
+ 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, true, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- storeCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, apa, _, _, _, config) and
+ apa0 = push(tc, apa)
}
pragma[nomagic]
private predicate flowStore(
- Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- readCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, _, config) and
- ap0 = pop(f, ap) and
- flowFwdConsCand(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, apa, _, _, _, config) and
+ apa0 = pop(tc, apa) and
+ flowFwdConsCand(tc, _, apa0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, 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, f, 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)
}
/**
@@ -1969,12 +2028,13 @@ 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 |
- flowOut(call, ret, toReturn, returnAp, ap, config) and
- flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
+ exists(ReturnNodeExt ret, CallContextCall ccc |
+ flowOut(call, ret, toReturn, returnApa, apa, config) and
+ flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and
+ ccc.matchesCall(call)
)
}
@@ -1985,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, true, TAccessPathSome(ap), _, ap0, config)
- )
- }
+ TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) }
/**
* A context for generating flow summaries. This represents flow entry through
@@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
}
}
+private newtype TAccessPath =
+ TAccessPathNil(DataFlowType t) or
+ TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) }
+
private newtype TPathNode =
TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
@@ -2041,13 +2107,13 @@ private newtype TPathNode =
config.isSource(node) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TNil(getErasedNodeTypeBound(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) and
config = mid.getConfiguration() and
- flow(node, _, _, ap, unbind(config))
+ flow(node, _, _, ap.getApprox(), unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2059,12 +2125,99 @@ 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, _, _, TAccessPathNil(_)) 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 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) }
+
+ 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 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() {
+ 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() }
+}
+
/**
* 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.
@@ -2098,14 +2251,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TNil(getErasedNodeTypeBound(node))
+ ap = TAccessPathNil(getNodeType(node))
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
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
- not innercc instanceof CallContextCall and
- ap = mid.getAp() and
+ innercc instanceof CallContextNoCall and
+ apa = mid.getAp().getApprox() 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)
@@ -2321,10 +2494,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)
}
/**
@@ -2333,10 +2506,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)
)
}
@@ -2345,22 +2517,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 = ap.getApprox()
)
}
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)
)
}
@@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0(
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
AccessPath ap
) {
- pathIntoArg(mid, i, outercc, call, ap) and
- callable = resolveCall(call, outercc) and
- parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration())
+ exists(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())
+ )
}
/**
@@ -2403,7 +2578,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, AccessPath ap, AccessPathApprox apa,
+ Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
@@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough(
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp() and
+ apa = ap.getApprox() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
@@ -2419,11 +2596,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()))
)
}
@@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0(
*/
pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
- exists(DataFlowCall call, ReturnKindExt kind |
- pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
+ exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
+ pathThroughCallable0(call, mid, kind, cc, ap, apa) and
+ out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration()))
)
}
@@ -2521,10 +2699,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2533,7 +2711,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2544,7 +2722,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2562,15 +2740,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2591,7 +2769,7 @@ private module FlowExploration {
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
@@ -2608,7 +2786,7 @@ private module FlowExploration {
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), ap.getType())
+ then compatibleTypes(getNodeType(node), ap.getType())
else any()
)
}
@@ -2721,7 +2899,7 @@ private module FlowExploration {
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
)
or
@@ -2737,7 +2915,7 @@ private module FlowExploration {
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -2746,11 +2924,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getNodeType(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2769,35 +2948,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- store(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
@@ -2806,7 +2992,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
- not innercc instanceof CallContextCall and
+ innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
index 43359fb329be..4562eb3fd194 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
@@ -1,6 +1,7 @@
private import cpp
private import DataFlowUtil
private import DataFlowDispatch
+private import FlowVar
/** Gets the instance argument of a non-static call. */
private Node getInstanceArgument(Call call) {
@@ -106,7 +107,7 @@ private class ExprOutNode extends OutNode, ExprNode {
override DataFlowCall getCall() { result = this.getExpr() }
}
-private class RefOutNode extends OutNode, DefinitionByReferenceNode {
+private class RefOutNode extends OutNode, DefinitionByReferenceOrIteratorNode {
/** Gets the underlying call. */
override DataFlowCall getCall() { result = this.getArgument().getParent() }
}
@@ -120,7 +121,7 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
kind = TNormalReturnKind()
or
exists(int i |
- result.asDefiningArgument() = call.getArgument(i) and
+ result.(DefinitionByReferenceOrIteratorNode).getArgument() = call.getArgument(i) and
kind = TRefReturnKind(i)
)
}
@@ -148,12 +149,6 @@ class Content extends TContent {
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
}
-
- /** Gets the type of the object containing this content. */
- abstract Type getContainerType();
-
- /** Gets the type of this content. */
- abstract Type getType();
}
private class FieldContent extends Content, TFieldContent {
@@ -168,26 +163,14 @@ private class FieldContent extends Content, TFieldContent {
override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
}
-
- override Type getContainerType() { result = f.getDeclaringType() }
-
- override Type getType() { result = f.getType() }
}
private class CollectionContent extends Content, TCollectionContent {
override string toString() { result = "collection" }
-
- override Type getContainerType() { none() }
-
- override Type getType() { none() }
}
private class ArrayContent extends Content, TArrayContent {
override string toString() { result = "array" }
-
- override Type getContainerType() { none() }
-
- override Type getType() { none() }
}
/**
@@ -235,16 +218,19 @@ predicate readStep(Node node1, Content f, Node node2) {
}
/**
- * Gets a representative (boxed) type for `t` for the purpose of pruning
- * possible flow. A single type is used for all numeric types to account for
- * numeric conversions, and otherwise the erasure is used.
+ * Holds if values stored inside content `c` are cleared at node `n`.
*/
-Type getErasedRepr(Type t) {
- suppressUnusedType(t) and
+predicate clearsContent(Node n, Content c) {
+ none() // stub implementation
+}
+
+/** Gets the type of `n` used for type pruning. */
+Type getNodeType(Node n) {
+ suppressUnusedNode(n) and
result instanceof VoidType // stub implementation
}
-/** Gets a string representation of a type returned by `getErasedRepr`. */
+/** Gets a string representation of a type returned by `getNodeType`. */
string ppReprType(Type t) { none() } // stub implementation
/**
@@ -256,7 +242,7 @@ predicate compatibleTypes(Type t1, Type t2) {
any() // stub implementation
}
-private predicate suppressUnusedType(Type t) { any() }
+private predicate suppressUnusedNode(Node n) { any() }
//////////////////////////////////////////////////////////////////////////////
// Java QL library compatibility wrappers
@@ -314,3 +300,6 @@ predicate isImmutableOrUnobservable(Node n) {
// The above list of cases isn't exhaustive, but it narrows down the
// consistency alerts enough that most of them are interesting.
}
+
+/** Holds if `n` should be hidden from path explanations. */
+predicate nodeIsHidden(Node n) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
index 47da6ca6e540..09409eb30f2a 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -6,6 +6,7 @@ private import cpp
private import semmle.code.cpp.dataflow.internal.FlowVar
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.controlflow.Guards
+private import semmle.code.cpp.dataflow.internal.AddressFlow
cached
private newtype TNode =
@@ -50,13 +51,25 @@ class Node extends TNode {
/** Gets the type of this node. */
Type getType() { none() } // overridden in subclasses
- /** Gets the expression corresponding to this node, if any. */
+ /**
+ * Gets the expression corresponding to this node, if any. This predicate
+ * only has a result on nodes that represent the value of evaluating the
+ * expression. For data flowing _out of_ an expression, like when an
+ * argument is passed by reference, use `asDefiningArgument` instead of
+ * `asExpr`.
+ */
Expr asExpr() { result = this.(ExprNode).getExpr() }
/** Gets the parameter corresponding to this node, if any. */
Parameter asParameter() { result = this.(ExplicitParameterNode).getParameter() }
- /** Gets the argument that defines this `DefinitionByReferenceNode`, if any. */
+ /**
+ * Gets the argument that defines this `DefinitionByReferenceNode`, if any.
+ * This predicate should be used instead of `asExpr` when referring to the
+ * value of a reference argument _after_ the call has returned. For example,
+ * in `f(&x)`, this predicate will have `&x` as its result for the `Node`
+ * that represents the new value of `x`.
+ */
Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() }
/**
@@ -170,28 +183,29 @@ class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode {
}
/**
- * A node that represents the value of a variable after a function call that
- * may have changed the variable because it's passed by reference.
+ * INTERNAL: do not use.
*
- * A typical example would be a call `f(&x)`. Firstly, there will be flow into
- * `x` from previous definitions of `x`. Secondly, there will be a
- * `DefinitionByReferenceNode` to represent the value of `x` after the call has
- * returned. This node will have its `getArgument()` equal to `&x`.
+ * A node that represents the value of a variable after a function call that
+ * may have changed the variable because it's passed by reference or because an
+ * iterator for it was passed by value or by reference.
*/
-class DefinitionByReferenceNode extends PartialDefinitionNode {
+class DefinitionByReferenceOrIteratorNode extends PartialDefinitionNode {
Expr inner;
Expr argument;
- DefinitionByReferenceNode() {
- this.getPartialDefinition().(DefinitionByReference).definesExpressions(inner, argument)
+ DefinitionByReferenceOrIteratorNode() {
+ this.getPartialDefinition().definesExpressions(inner, argument) and
+ (
+ this.getPartialDefinition() instanceof DefinitionByReference
+ or
+ this.getPartialDefinition() instanceof DefinitionByIterator
+ )
}
override Function getFunction() { result = inner.getEnclosingFunction() }
override Type getType() { result = inner.getType() }
- override string toString() { result = "ref arg " + argument.toString() }
-
override Location getLocation() { result = argument.getLocation() }
override ExprNode getPreUpdateNode() { result.getExpr() = argument }
@@ -208,6 +222,21 @@ class DefinitionByReferenceNode extends PartialDefinitionNode {
}
}
+/**
+ * A node that represents the value of a variable after a function call that
+ * may have changed the variable because it's passed by reference.
+ *
+ * A typical example would be a call `f(&x)`. Firstly, there will be flow into
+ * `x` from previous definitions of `x`. Secondly, there will be a
+ * `DefinitionByReferenceNode` to represent the value of `x` after the call has
+ * returned. This node will have its `getArgument()` equal to `&x`.
+ */
+class DefinitionByReferenceNode extends DefinitionByReferenceOrIteratorNode {
+ override VariablePartialDefinition pd;
+
+ override string toString() { result = "ref arg " + argument.toString() }
+}
+
/**
* The value of an uninitialized local variable, viewed as a node in a data
* flow graph.
@@ -271,13 +300,11 @@ abstract class PostUpdateNode extends Node {
override Location getLocation() { result = getPreUpdateNode().getLocation() }
}
-private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
+abstract private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
PartialDefinition pd;
PartialDefinitionNode() { this = TPartialDefinitionNode(pd) }
- override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
-
override Location getLocation() { result = pd.getActualLocation() }
PartialDefinition getPartialDefinition() { result = pd }
@@ -285,6 +312,24 @@ private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNo
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
}
+private class VariablePartialDefinitionNode extends PartialDefinitionNode {
+ override VariablePartialDefinition pd;
+
+ override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
+}
+
+/**
+ * INTERNAL: do not use.
+ *
+ * A synthetic data flow node used for flow into a collection when an iterator
+ * write occurs in a callee.
+ */
+class IteratorPartialDefinitionNode extends PartialDefinitionNode {
+ override IteratorPartialDefinition pd;
+
+ override Node getPreUpdateNode() { pd.definesExpressions(_, result.asExpr()) }
+}
+
/**
* A post-update node on the `e->f` in `f(&e->f)` (and other forms).
*/
@@ -383,7 +428,9 @@ class PreConstructorInitThis extends Node, TPreConstructorInitThis {
}
/**
- * Gets the `Node` corresponding to `e`.
+ * Gets the `Node` corresponding to the value of evaluating `e`. For data
+ * flowing _out of_ an expression, like when an argument is passed by
+ * reference, use `definitionByReferenceNodeFromArgument` instead.
*/
ExprNode exprNode(Expr e) { result.getExpr() = e }
@@ -484,6 +531,17 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
// Expr -> Expr
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
or
+ // Assignment -> LValue post-update node
+ //
+ // This is used for assignments whose left-hand side is not a variable
+ // assignment or a storeStep but is still modeled by other means. It could be
+ // a call to `operator*` or `operator[]` where taint should flow to the
+ // post-update node of the qualifier.
+ exists(AssignExpr assign |
+ nodeFrom.asExpr() = assign and
+ nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getLValue()
+ )
+ or
// Node -> FlowVar -> VariableAccess
exists(FlowVar var |
(
@@ -514,6 +572,19 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
)
+ or
+ // Reverse flow: data that flows from the post-update node of a reference
+ // returned by a function call, back into the qualifier of that function.
+ // This allows data to flow 'in' through references returned by a modeled
+ // function such as `operator[]`.
+ exists(DataFlowFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
+ call.getTarget() = f and
+ inModel.isReturnValueDeref() and
+ outModel.isQualifierObject() and
+ f.hasDataFlow(inModel, outModel) and
+ nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() = call and
+ nodeTo.asDefiningArgument() = call.getQualifier()
+ )
}
/**
@@ -572,6 +643,15 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
or
toExpr.(AddressOfExpr).getOperand() = fromExpr
or
+ // This rule enables flow from an array to its elements. Example: `a` to
+ // `a[i]` or `*a`, where `a` is an array type. It does not enable flow from a
+ // pointer to its indirection as in `p[i]` where `p` is a pointer type.
+ exists(Expr toConverted |
+ variablePartiallyAccessed(fromExpr, toConverted) and
+ toExpr = toConverted.getUnconverted() and
+ not toExpr = fromExpr
+ )
+ or
toExpr.(BuiltInOperationBuiltInAddressOf).getOperand() = fromExpr
or
// The following case is needed to track the qualifier object for flow
@@ -591,14 +671,25 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
// `ClassAggregateLiteral` (`{ capture1, ..., captureN }`).
toExpr.(LambdaExpression).getInitializer() = fromExpr
or
+ // Data flow through a function model.
toExpr =
any(Call call |
- exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel, int iIn |
- call.getTarget() = f and
+ exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel |
f.hasDataFlow(inModel, outModel) and
- outModel.isReturnValue() and
- inModel.isParameter(iIn) and
- fromExpr = call.getArgument(iIn)
+ (
+ exists(int iIn |
+ inModel.isParameter(iIn) and
+ fromExpr = call.getArgument(iIn)
+ )
+ or
+ inModel.isQualifierObject() and
+ fromExpr = call.getQualifier()
+ or
+ inModel.isQualifierAddress() and
+ fromExpr = call.getQualifier()
+ ) and
+ call.getTarget() = f and
+ outModel.isReturnValue()
)
)
}
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
index 72033b0f72df..50395dbaafc0 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -6,6 +6,7 @@ import cpp
private import semmle.code.cpp.controlflow.SSA
private import semmle.code.cpp.dataflow.internal.SubBasicBlocks
private import semmle.code.cpp.dataflow.internal.AddressFlow
+private import semmle.code.cpp.models.implementations.Iterator
/**
* A conceptual variable that is assigned only once, like an SSA variable. This
@@ -108,27 +109,26 @@ class FlowVar extends TFlowVar {
* ```
*/
private module PartialDefinitions {
- class PartialDefinition extends Expr {
- Expr innerDefinedExpr;
+ abstract class PartialDefinition extends Expr {
ControlFlowNode node;
- PartialDefinition() {
- exists(Expr convertedInner |
- valueToUpdate(convertedInner, this.getFullyConverted(), node) and
- innerDefinedExpr = convertedInner.getUnconverted() and
- not this instanceof Conversion
- )
- }
-
- predicate partiallyDefines(Variable v) { innerDefinedExpr = v.getAnAccess() }
+ abstract deprecated predicate partiallyDefines(Variable v);
- predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e }
+ abstract deprecated predicate partiallyDefinesThis(ThisExpr e);
/**
* Gets the subBasicBlock where this `PartialDefinition` is defined.
*/
ControlFlowNode getSubBasicBlockStart() { result = node }
+ /**
+ * Holds if this `PartialDefinition` defines variable `v` at control-flow
+ * node `cfn`.
+ */
+ // does this work with a dispred?
+ pragma[noinline]
+ abstract predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn);
+
/**
* Holds if this partial definition may modify `inner` (or what it points
* to) through `outer`. These expressions will never be `Conversion`s.
@@ -137,10 +137,7 @@ private module PartialDefinitions {
* - `inner` = `... .x`, `outer` = `&...`
* - `inner` = `a`, `outer` = `*`
*/
- predicate definesExpressions(Expr inner, Expr outer) {
- inner = innerDefinedExpr and
- outer = this
- }
+ abstract predicate definesExpressions(Expr inner, Expr outer);
/**
* Gets the location of this element, adjusted to avoid unknown locations
@@ -156,10 +153,107 @@ private module PartialDefinitions {
}
}
+ class IteratorPartialDefinition extends PartialDefinition {
+ Variable collection;
+ Expr innerDefinedExpr;
+
+ IteratorPartialDefinition() {
+ exists(Expr convertedInner |
+ not this instanceof Conversion and
+ valueToUpdate(convertedInner, this.getFullyConverted(), node) and
+ innerDefinedExpr = convertedInner.getUnconverted() and
+ (
+ innerDefinedExpr.(Call).getQualifier() = getAnIteratorAccess(collection)
+ or
+ innerDefinedExpr.(Call).getQualifier() = collection.getAnAccess() and
+ collection instanceof IteratorParameter
+ ) and
+ innerDefinedExpr.(Call).getTarget() instanceof IteratorPointerDereferenceMemberOperator
+ )
+ or
+ // iterators passed by value without a copy constructor
+ exists(Call call |
+ call = node and
+ call.getAnArgument() = innerDefinedExpr and
+ innerDefinedExpr = this and
+ this = getAnIteratorAccess(collection) and
+ not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator
+ )
+ or
+ // iterators passed by value with a copy constructor
+ exists(Call call, ConstructorCall copy |
+ copy.getTarget() instanceof CopyConstructor and
+ call = node and
+ call.getAnArgument() = copy and
+ copy.getArgument(0) = getAnIteratorAccess(collection) and
+ innerDefinedExpr = this and
+ this = copy and
+ not call.getTarget() instanceof IteratorPointerDereferenceMemberOperator
+ )
+ }
+
+ deprecated override predicate partiallyDefines(Variable v) { v = collection }
+
+ deprecated override predicate partiallyDefinesThis(ThisExpr e) { none() }
+
+ override predicate definesExpressions(Expr inner, Expr outer) {
+ inner = innerDefinedExpr and
+ outer = this
+ }
+
+ override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
+ v = collection and
+ cfn = node
+ }
+ }
+
+ class VariablePartialDefinition extends PartialDefinition {
+ Expr innerDefinedExpr;
+
+ VariablePartialDefinition() {
+ not this instanceof Conversion and
+ exists(Expr convertedInner |
+ valueToUpdate(convertedInner, this.getFullyConverted(), node) and
+ innerDefinedExpr = convertedInner.getUnconverted()
+ )
+ }
+
+ deprecated override predicate partiallyDefines(Variable v) {
+ innerDefinedExpr = v.getAnAccess()
+ }
+
+ deprecated override predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e }
+
+ /**
+ * Holds if this partial definition may modify `inner` (or what it points
+ * to) through `outer`. These expressions will never be `Conversion`s.
+ *
+ * For example, in `f(& (*a).x)`, there are two results:
+ * - `inner` = `... .x`, `outer` = `&...`
+ * - `inner` = `a`, `outer` = `*`
+ */
+ override predicate definesExpressions(Expr inner, Expr outer) {
+ inner = innerDefinedExpr and
+ outer = this
+ }
+
+ override predicate partiallyDefinesVariableAt(Variable v, ControlFlowNode cfn) {
+ innerDefinedExpr = v.getAnAccess() and
+ cfn = node
+ }
+ }
+
+ /**
+ * A partial definition that's a definition via an output iterator.
+ */
+ class DefinitionByIterator extends IteratorPartialDefinition {
+ DefinitionByIterator() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) }
+ }
+
/**
* A partial definition that's a definition by reference.
*/
- class DefinitionByReference extends PartialDefinition {
+ class DefinitionByReference extends VariablePartialDefinition {
DefinitionByReference() { exists(Call c | this = c.getAnArgument() or this = c.getQualifier()) }
}
}
@@ -188,7 +282,7 @@ module FlowVar_internal {
predicate fullySupportedSsaVariable(Variable v) {
v = any(SsaDefinition def).getAVariable() and
// A partially-defined variable is handled using the partial definitions logic.
- not any(PartialDefinition p).partiallyDefines(v) and
+ not any(PartialDefinition p).partiallyDefinesVariableAt(v, _) and
// SSA variables do not exist before their first assignment, but one
// feature of this data flow library is to track where uninitialized data
// ends up.
@@ -201,7 +295,8 @@ module FlowVar_internal {
// The SSA library has a theoretically accurate treatment of reference types,
// treating them as immutable, but for data flow it gives better results in
// practice to make the variable synonymous with its contents.
- not v.getUnspecifiedType() instanceof ReferenceType
+ not v.getUnspecifiedType() instanceof ReferenceType and
+ not v instanceof IteratorParameter
}
/**
@@ -230,9 +325,9 @@ module FlowVar_internal {
(
initializer(v, sbb.getANode())
or
- assignmentLikeOperation(sbb, v, _, _)
+ assignmentLikeOperation(sbb, v, _)
or
- sbb = any(PartialDefinition p | p.partiallyDefines(v)).getSubBasicBlockStart()
+ exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, sbb))
or
blockVarDefinedByVariable(sbb, v)
)
@@ -349,7 +444,7 @@ module FlowVar_internal {
}
override predicate definedByExpr(Expr e, ControlFlowNode node) {
- assignmentLikeOperation(node, v, _, e) and
+ assignmentLikeOperation(node, v, e) and
node = sbb
or
// We pick the defining `ControlFlowNode` of an `Initializer` to be its
@@ -363,8 +458,7 @@ module FlowVar_internal {
override predicate definedPartiallyAt(Expr e) {
exists(PartialDefinition p |
- p.partiallyDefines(v) and
- sbb = p.getSubBasicBlockStart() and
+ p.partiallyDefinesVariableAt(v, sbb) and
p.definesExpressions(_, e)
)
}
@@ -427,7 +521,7 @@ module FlowVar_internal {
/**
* Gets a variable that is assigned in this loop and read outside the loop.
*/
- private Variable getARelevantVariable() {
+ Variable getARelevantVariable() {
result = this.getAVariableAssignedInLoop() and
exists(VariableAccess va |
va.getTarget() = result and
@@ -440,7 +534,7 @@ module FlowVar_internal {
pragma[noinline]
private Variable getAVariableAssignedInLoop() {
exists(BasicBlock bbAssign |
- assignmentLikeOperation(bbAssign.getANode(), result, _, _) and
+ assignmentLikeOperation(bbAssign.getANode(), result, _) and
this.bbInLoop(bbAssign)
)
}
@@ -472,10 +566,16 @@ module FlowVar_internal {
reachesWithoutAssignment(bb.getAPredecessor(), v) and
this.bbInLoop(bb)
) and
- not assignmentLikeOperation(bb.getANode(), v, _, _)
+ not assignsToVar(bb, v)
}
}
+ pragma[noinline]
+ private predicate assignsToVar(BasicBlock bb, Variable v) {
+ assignmentLikeOperation(bb.getANode(), v, _) and
+ exists(AlwaysTrueUponEntryLoop loop | v = loop.getARelevantVariable())
+ }
+
/**
* Holds if `loop` always assigns to `v` before leaving through an edge
* from `bbInside` in its condition to `bbOutside` outside the loop. Also,
@@ -509,7 +609,7 @@ module FlowVar_internal {
result = mid.getASuccessor() and
variableLiveInSBB(result, v) and
forall(AlwaysTrueUponEntryLoop loop | skipLoop(mid, result, v, loop) | loop.sbbInLoop(sbbDef)) and
- not assignmentLikeOperation(result, v, _, _)
+ not assignmentLikeOperation(result, v, _)
)
}
@@ -545,13 +645,15 @@ module FlowVar_internal {
refType = p.getUnderlyingType() and
not refType.getBaseType().isConst()
)
+ or
+ p instanceof IteratorParameter
}
/**
* Holds if liveness of `v` should stop propagating backwards from `sbb`.
*/
private predicate variableNotLiveBefore(SubBasicBlock sbb, Variable v) {
- assignmentLikeOperation(sbb, v, _, _)
+ assignmentLikeOperation(sbb, v, _)
or
// Liveness of `v` is killed when going backwards from a block that declares it
exists(DeclStmt ds | ds.getADeclaration().(LocalVariable) = v and sbb.contains(ds))
@@ -671,21 +773,17 @@ module FlowVar_internal {
* `node instanceof Initializer` is covered by `initializer` instead of this
* predicate.
*/
- predicate assignmentLikeOperation(
- ControlFlowNode node, Variable v, VariableAccess va, Expr assignedExpr
- ) {
+ predicate assignmentLikeOperation(ControlFlowNode node, Variable v, Expr assignedExpr) {
// Together, the two following cases cover `Assignment`
node =
any(AssignExpr ae |
- va = ae.getLValue() and
- v = va.getTarget() and
+ v.getAnAccess() = ae.getLValue() and
assignedExpr = ae.getRValue()
)
or
node =
any(AssignOperation ao |
- va = ao.getLValue() and
- v = va.getTarget() and
+ v.getAnAccess() = ao.getLValue() and
// Here and in the `PrefixCrementOperation` case, we say that the assigned
// expression is the operation itself. For example, we say that `x += 1`
// assigns `x += 1` to `x`. The justification is that after this operation,
@@ -697,12 +795,24 @@ module FlowVar_internal {
// `PrefixCrementOperation` is itself a source
node =
any(CrementOperation op |
- va = op.getOperand() and
- v = va.getTarget() and
+ v.getAnAccess() = op.getOperand() and
assignedExpr = op
)
}
+ Expr getAnIteratorAccess(Variable collection) {
+ exists(Call c, SsaDefinition def, Variable iterator |
+ c.getQualifier() = collection.getAnAccess() and
+ c.getTarget() instanceof BeginOrEndFunction and
+ def.getAnUltimateDefiningValue(iterator) = c and
+ result = def.getAUse(iterator)
+ )
+ }
+
+ class IteratorParameter extends Parameter {
+ IteratorParameter() { this.getUnspecifiedType() instanceof Iterator }
+ }
+
/**
* Holds if `v` is initialized to have value `assignedExpr`.
*/
@@ -734,9 +844,9 @@ module FlowVar_internal {
class DataFlowSubBasicBlockCutNode extends SubBasicBlockCutNode {
DataFlowSubBasicBlockCutNode() {
exists(Variable v | not fullySupportedSsaVariable(v) |
- assignmentLikeOperation(this, v, _, _)
+ assignmentLikeOperation(this, v, _)
or
- this = any(PartialDefinition p | p.partiallyDefines(v)).getSubBasicBlockStart()
+ exists(PartialDefinition p | p.partiallyDefinesVariableAt(v, this))
// It is not necessary to cut the basic blocks at `Initializer` nodes
// because the affected variable can have no _other_ value before its
// initializer. It is not necessary to cut basic blocks at procedure
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
index 1812e3ffe7f9..1ef340c4f213 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
@@ -10,6 +10,7 @@
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.models.interfaces.Taint
+private import semmle.code.cpp.models.interfaces.Iterator
private module DataFlow {
import semmle.code.cpp.dataflow.internal.DataFlowUtil
@@ -33,10 +34,10 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
}
/**
- * Holds if `node` should be a barrier in all global taint flow configurations
+ * Holds if `node` should be a sanitizer in all global taint flow configurations
* but not in local taint.
*/
-predicate defaultTaintBarrier(DataFlow::Node node) { none() }
+predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
/**
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
@@ -65,6 +66,15 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
// tracking. The flow from expression `x` into `x++` etc. is handled in the
// case above.
exprTo = DataFlow::getAnAccessToAssignedVariable(exprFrom.(PostfixCrementOperation))
+ or
+ // In `for (char c : s) { ... c ... }`, this rule propagates taint from `s`
+ // to `c`.
+ exists(RangeBasedForStmt rbf |
+ exprFrom = rbf.getRange() and
+ // It's guaranteed up to at least C++20 that the range-based for loop
+ // desugars to a variable with an initializer.
+ exprTo = rbf.getVariable().getInitializer().getExpr()
+ )
)
or
// Taint can flow through modeled functions
@@ -73,6 +83,26 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
or
exprToPartialDefinitionStep(nodeFrom.asExpr(), nodeTo.asPartialDefinition())
+ or
+ // Reverse taint: taint that flows from the post-update node of a reference
+ // returned by a function call, back into the qualifier of that function.
+ // This allows taint to flow 'in' through references returned by a modeled
+ // function such as `operator[]`.
+ exists(TaintFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
+ call.getTarget() = f and
+ inModel.isReturnValueDeref() and
+ nodeFrom.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = call and
+ f.hasTaintFlow(inModel, outModel) and
+ (
+ outModel.isQualifierObject() and
+ nodeTo.asDefiningArgument() = call.getQualifier()
+ or
+ exists(int argOutIndex |
+ outModel.isParameterDeref(argOutIndex) and
+ nodeTo.asDefiningArgument() = call.getArgument(argOutIndex)
+ )
+ )
+ )
}
/**
@@ -226,4 +256,12 @@ private predicate exprToPartialDefinitionStep(Expr exprIn, Expr exprOut) {
exprIn = call.getArgument(argInIndex)
)
)
+ or
+ exists(Assignment a |
+ iteratorDereference(exprOut) and
+ a.getLValue() = exprOut and
+ a.getRValue() = exprIn
+ )
}
+
+private predicate iteratorDereference(Call c) { c.getTarget() instanceof IteratorReferenceFunction }
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
index 0f0607662e90..b509fad9cd20 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
@@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private
* To create a configuration, extend this class with a subclass whose
* characteristic predicate is a unique singleton string. For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
- defaultTaintBarrier(node)
+ defaultTaintSanitizer(node)
}
- /** Holds if data flow into `node` is prohibited. */
+ /** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
- /** Holds if data flow out of `node` is prohibited. */
+ /** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
+ /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
index 0f0607662e90..b509fad9cd20 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
@@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private
* To create a configuration, extend this class with a subclass whose
* characteristic predicate is a unique singleton string. For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
- defaultTaintBarrier(node)
+ defaultTaintSanitizer(node)
}
- /** Holds if data flow into `node` is prohibited. */
+ /** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
- /** Holds if data flow out of `node` is prohibited. */
+ /** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
+ /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
index fe467be6fe4c..681fb41fa678 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides classes for modeling accesses including variable accesses, enum
+ * constant accesses and function accesses.
+ */
+
import semmle.code.cpp.exprs.Expr
import semmle.code.cpp.Variable
import semmle.code.cpp.Enum
@@ -36,7 +41,7 @@ class Access extends Expr, NameQualifiableElement, @access {
* ```
*/
class EnumConstantAccess extends Access, @varaccess {
- override string getCanonicalQLClass() { result = "EnumConstantAccess" }
+ override string getAPrimaryQlClass() { result = "EnumConstantAccess" }
EnumConstantAccess() {
exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c)))
@@ -61,7 +66,7 @@ class EnumConstantAccess extends Access, @varaccess {
* ```
*/
class VariableAccess extends Access, @varaccess {
- override string getCanonicalQLClass() { result = "VariableAccess" }
+ override string getAPrimaryQlClass() { result = "VariableAccess" }
VariableAccess() {
not exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c)))
@@ -166,7 +171,7 @@ class VariableAccess extends Access, @varaccess {
* ```
*/
class FieldAccess extends VariableAccess {
- override string getCanonicalQLClass() { result = "FieldAccess" }
+ override string getAPrimaryQlClass() { result = "FieldAccess" }
FieldAccess() { exists(Field f | varbind(underlyingElement(this), unresolveElement(f))) }
@@ -194,7 +199,7 @@ class FieldAccess extends VariableAccess {
* ```
*/
class PointerFieldAccess extends FieldAccess {
- override string getCanonicalQLClass() { result = "PointerFieldAccess" }
+ override string getAPrimaryQlClass() { result = "PointerFieldAccess" }
PointerFieldAccess() {
exists(PointerType t |
@@ -211,7 +216,7 @@ class PointerFieldAccess extends FieldAccess {
* distinguish whether or not the type of `obj` is a reference type.
*/
class DotFieldAccess extends FieldAccess {
- override string getCanonicalQLClass() { result = "DotFieldAccess" }
+ override string getAPrimaryQlClass() { result = "DotFieldAccess" }
DotFieldAccess() { exists(Class c | c = getQualifier().getFullyConverted().getUnspecifiedType()) }
}
@@ -232,7 +237,7 @@ class DotFieldAccess extends FieldAccess {
* ```
*/
class ReferenceFieldAccess extends DotFieldAccess {
- override string getCanonicalQLClass() { result = "ReferenceFieldAccess" }
+ override string getAPrimaryQlClass() { result = "ReferenceFieldAccess" }
ReferenceFieldAccess() { exprHasReferenceConversion(this.getQualifier()) }
}
@@ -253,7 +258,7 @@ class ReferenceFieldAccess extends DotFieldAccess {
* ```
*/
class ValueFieldAccess extends DotFieldAccess {
- override string getCanonicalQLClass() { result = "ValueFieldAccess" }
+ override string getAPrimaryQlClass() { result = "ValueFieldAccess" }
ValueFieldAccess() { not exprHasReferenceConversion(this.getQualifier()) }
}
@@ -307,7 +312,7 @@ private predicate exprHasReferenceConversion(Expr e) { referenceConversion(e.get
* `ImplicitThisFieldAccess`.
*/
class ImplicitThisFieldAccess extends FieldAccess {
- override string getCanonicalQLClass() { result = "ImplicitThisFieldAccess" }
+ override string getAPrimaryQlClass() { result = "ImplicitThisFieldAccess" }
ImplicitThisFieldAccess() { not exists(this.getQualifier()) }
}
@@ -332,7 +337,7 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess {
override predicate isConstant() { any() }
- override string getCanonicalQLClass() { result = "PointerToFieldLiteral" }
+ override string getAPrimaryQlClass() { result = "PointerToFieldLiteral" }
}
/**
@@ -349,7 +354,7 @@ class PointerToFieldLiteral extends ImplicitThisFieldAccess {
class FunctionAccess extends Access, @routineexpr {
FunctionAccess() { not iscall(underlyingElement(this), _) }
- override string getCanonicalQLClass() { result = "FunctionAccess" }
+ override string getAPrimaryQlClass() { result = "FunctionAccess" }
/** Gets the accessed function. */
override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) }
@@ -399,7 +404,7 @@ class ParamAccessForType extends Expr, @param_ref {
* ```
*/
class TypeName extends Expr, @type_operand {
- override string getCanonicalQLClass() { result = "TypeName" }
+ override string getAPrimaryQlClass() { result = "TypeName" }
override string toString() { result = this.getType().getName() }
}
@@ -418,7 +423,7 @@ class TypeName extends Expr, @type_operand {
* `OverloadedArrayExpr`.
*/
class ArrayExpr extends Expr, @subscriptexpr {
- override string getCanonicalQLClass() { result = "ArrayExpr" }
+ override string getAPrimaryQlClass() { result = "ArrayExpr" }
/**
* Gets the array or pointer expression being subscripted.
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll
index 278db89b41ef..b94c9cee724b 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides classes for modeling arithmetic operations such as `+`, `-`, `*`
+ * and `++`.
+ */
+
import semmle.code.cpp.exprs.Expr
/**
@@ -14,7 +19,7 @@ class UnaryArithmeticOperation extends UnaryOperation, @un_arith_op_expr { }
class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr {
override string getOperator() { result = "-" }
- override string getCanonicalQLClass() { result = "UnaryMinusExpr" }
+ override string getAPrimaryQlClass() { result = "UnaryMinusExpr" }
override int getPrecedence() { result = 16 }
}
@@ -28,7 +33,7 @@ class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr {
class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr {
override string getOperator() { result = "+" }
- override string getCanonicalQLClass() { result = "UnaryPlusExpr" }
+ override string getAPrimaryQlClass() { result = "UnaryPlusExpr" }
override int getPrecedence() { result = 16 }
}
@@ -45,7 +50,7 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr {
class ConjugationExpr extends UnaryArithmeticOperation, @conjugation {
override string getOperator() { result = "~" }
- override string getCanonicalQLClass() { result = "ConjugationExpr" }
+ override string getAPrimaryQlClass() { result = "ConjugationExpr" }
}
/**
@@ -107,7 +112,7 @@ class PostfixCrementOperation extends CrementOperation, @postfix_crement_expr {
class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preincrexpr {
override string getOperator() { result = "++" }
- override string getCanonicalQLClass() { result = "PrefixIncrExpr" }
+ override string getAPrimaryQlClass() { result = "PrefixIncrExpr" }
override int getPrecedence() { result = 16 }
}
@@ -123,7 +128,7 @@ class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preinc
class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predecrexpr {
override string getOperator() { result = "--" }
- override string getCanonicalQLClass() { result = "PrefixDecrExpr" }
+ override string getAPrimaryQlClass() { result = "PrefixDecrExpr" }
override int getPrecedence() { result = 16 }
}
@@ -139,7 +144,7 @@ class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predec
class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @postincrexpr {
override string getOperator() { result = "++" }
- override string getCanonicalQLClass() { result = "PostfixIncrExpr" }
+ override string getAPrimaryQlClass() { result = "PostfixIncrExpr" }
override int getPrecedence() { result = 17 }
@@ -157,7 +162,7 @@ class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @post
class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @postdecrexpr {
override string getOperator() { result = "--" }
- override string getCanonicalQLClass() { result = "PostfixDecrExpr" }
+ override string getAPrimaryQlClass() { result = "PostfixDecrExpr" }
override int getPrecedence() { result = 17 }
@@ -175,7 +180,7 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post
class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
override string getOperator() { result = "__real" }
- override string getCanonicalQLClass() { result = "RealPartExpr" }
+ override string getAPrimaryQlClass() { result = "RealPartExpr" }
}
/**
@@ -189,7 +194,7 @@ class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
class ImaginaryPartExpr extends UnaryArithmeticOperation, @imagpartexpr {
override string getOperator() { result = "__imag" }
- override string getCanonicalQLClass() { result = "ImaginaryPartExpr" }
+ override string getAPrimaryQlClass() { result = "ImaginaryPartExpr" }
}
/**
@@ -208,7 +213,7 @@ class BinaryArithmeticOperation extends BinaryOperation, @bin_arith_op_expr { }
class AddExpr extends BinaryArithmeticOperation, @addexpr {
override string getOperator() { result = "+" }
- override string getCanonicalQLClass() { result = "AddExpr" }
+ override string getAPrimaryQlClass() { result = "AddExpr" }
override int getPrecedence() { result = 13 }
}
@@ -222,7 +227,7 @@ class AddExpr extends BinaryArithmeticOperation, @addexpr {
class SubExpr extends BinaryArithmeticOperation, @subexpr {
override string getOperator() { result = "-" }
- override string getCanonicalQLClass() { result = "SubExpr" }
+ override string getAPrimaryQlClass() { result = "SubExpr" }
override int getPrecedence() { result = 13 }
}
@@ -236,7 +241,7 @@ class SubExpr extends BinaryArithmeticOperation, @subexpr {
class MulExpr extends BinaryArithmeticOperation, @mulexpr {
override string getOperator() { result = "*" }
- override string getCanonicalQLClass() { result = "MulExpr" }
+ override string getAPrimaryQlClass() { result = "MulExpr" }
override int getPrecedence() { result = 14 }
}
@@ -250,7 +255,7 @@ class MulExpr extends BinaryArithmeticOperation, @mulexpr {
class DivExpr extends BinaryArithmeticOperation, @divexpr {
override string getOperator() { result = "/" }
- override string getCanonicalQLClass() { result = "DivExpr" }
+ override string getAPrimaryQlClass() { result = "DivExpr" }
override int getPrecedence() { result = 14 }
}
@@ -264,7 +269,7 @@ class DivExpr extends BinaryArithmeticOperation, @divexpr {
class RemExpr extends BinaryArithmeticOperation, @remexpr {
override string getOperator() { result = "%" }
- override string getCanonicalQLClass() { result = "RemExpr" }
+ override string getAPrimaryQlClass() { result = "RemExpr" }
override int getPrecedence() { result = 14 }
}
@@ -281,7 +286,7 @@ class RemExpr extends BinaryArithmeticOperation, @remexpr {
class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
override string getOperator() { result = "*" }
- override string getCanonicalQLClass() { result = "ImaginaryMulExpr" }
+ override string getAPrimaryQlClass() { result = "ImaginaryMulExpr" }
override int getPrecedence() { result = 14 }
}
@@ -298,7 +303,7 @@ class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
override string getOperator() { result = "/" }
- override string getCanonicalQLClass() { result = "ImaginaryDivExpr" }
+ override string getAPrimaryQlClass() { result = "ImaginaryDivExpr" }
override int getPrecedence() { result = 14 }
}
@@ -316,7 +321,7 @@ class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
override string getOperator() { result = "+" }
- override string getCanonicalQLClass() { result = "RealImaginaryAddExpr" }
+ override string getAPrimaryQlClass() { result = "RealImaginaryAddExpr" }
override int getPrecedence() { result = 13 }
}
@@ -334,7 +339,7 @@ class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
override string getOperator() { result = "+" }
- override string getCanonicalQLClass() { result = "ImaginaryRealAddExpr" }
+ override string getAPrimaryQlClass() { result = "ImaginaryRealAddExpr" }
override int getPrecedence() { result = 13 }
}
@@ -352,7 +357,7 @@ class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
override string getOperator() { result = "-" }
- override string getCanonicalQLClass() { result = "RealImaginarySubExpr" }
+ override string getAPrimaryQlClass() { result = "RealImaginarySubExpr" }
override int getPrecedence() { result = 13 }
}
@@ -370,7 +375,7 @@ class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
override string getOperator() { result = "-" }
- override string getCanonicalQLClass() { result = "ImaginaryRealSubExpr" }
+ override string getAPrimaryQlClass() { result = "ImaginaryRealSubExpr" }
override int getPrecedence() { result = 13 }
}
@@ -384,7 +389,7 @@ class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
class MinExpr extends BinaryArithmeticOperation, @minexpr {
override string getOperator() { result = "" }
- override string getCanonicalQLClass() { result = "MinExpr" }
+ override string getAPrimaryQlClass() { result = "MinExpr" }
}
/**
@@ -396,7 +401,7 @@ class MinExpr extends BinaryArithmeticOperation, @minexpr {
class MaxExpr extends BinaryArithmeticOperation, @maxexpr {
override string getOperator() { result = ">?" }
- override string getCanonicalQLClass() { result = "MaxExpr" }
+ override string getAPrimaryQlClass() { result = "MaxExpr" }
}
/**
@@ -414,7 +419,7 @@ class PointerArithmeticOperation extends BinaryArithmeticOperation, @p_arith_op_
class PointerAddExpr extends PointerArithmeticOperation, @paddexpr {
override string getOperator() { result = "+" }
- override string getCanonicalQLClass() { result = "PointerAddExpr" }
+ override string getAPrimaryQlClass() { result = "PointerAddExpr" }
override int getPrecedence() { result = 13 }
}
@@ -429,7 +434,7 @@ class PointerAddExpr extends PointerArithmeticOperation, @paddexpr {
class PointerSubExpr extends PointerArithmeticOperation, @psubexpr {
override string getOperator() { result = "-" }
- override string getCanonicalQLClass() { result = "PointerSubExpr" }
+ override string getAPrimaryQlClass() { result = "PointerSubExpr" }
override int getPrecedence() { result = 13 }
}
@@ -444,7 +449,7 @@ class PointerSubExpr extends PointerArithmeticOperation, @psubexpr {
class PointerDiffExpr extends PointerArithmeticOperation, @pdiffexpr {
override string getOperator() { result = "-" }
- override string getCanonicalQLClass() { result = "PointerDiffExpr" }
+ override string getAPrimaryQlClass() { result = "PointerDiffExpr" }
override int getPrecedence() { result = 13 }
}
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll b/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll
index 4d2f61f1b6d8..0c56d9a4d51f 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll
@@ -38,7 +38,7 @@ class Assignment extends Operation, @assign_expr {
class AssignExpr extends Assignment, @assignexpr {
override string getOperator() { result = "=" }
- override string getCanonicalQLClass() { result = "AssignExpr" }
+ override string getAPrimaryQlClass() { result = "AssignExpr" }
/** Gets a textual representation of this assignment. */
override string toString() { result = "... = ..." }
@@ -64,7 +64,7 @@ class AssignArithmeticOperation extends AssignOperation, @assign_arith_expr { }
* ```
*/
class AssignAddExpr extends AssignArithmeticOperation, @assignaddexpr {
- override string getCanonicalQLClass() { result = "AssignAddExpr" }
+ override string getAPrimaryQlClass() { result = "AssignAddExpr" }
override string getOperator() { result = "+=" }
}
@@ -76,7 +76,7 @@ class AssignAddExpr extends AssignArithmeticOperation, @assignaddexpr {
* ```
*/
class AssignSubExpr extends AssignArithmeticOperation, @assignsubexpr {
- override string getCanonicalQLClass() { result = "AssignSubExpr" }
+ override string getAPrimaryQlClass() { result = "AssignSubExpr" }
override string getOperator() { result = "-=" }
}
@@ -88,7 +88,7 @@ class AssignSubExpr extends AssignArithmeticOperation, @assignsubexpr {
* ```
*/
class AssignMulExpr extends AssignArithmeticOperation, @assignmulexpr {
- override string getCanonicalQLClass() { result = "AssignMulExpr" }
+ override string getAPrimaryQlClass() { result = "AssignMulExpr" }
override string getOperator() { result = "*=" }
}
@@ -100,7 +100,7 @@ class AssignMulExpr extends AssignArithmeticOperation, @assignmulexpr {
* ```
*/
class AssignDivExpr extends AssignArithmeticOperation, @assigndivexpr {
- override string getCanonicalQLClass() { result = "AssignDivExpr" }
+ override string getAPrimaryQlClass() { result = "AssignDivExpr" }
override string getOperator() { result = "/=" }
}
@@ -112,7 +112,7 @@ class AssignDivExpr extends AssignArithmeticOperation, @assigndivexpr {
* ```
*/
class AssignRemExpr extends AssignArithmeticOperation, @assignremexpr {
- override string getCanonicalQLClass() { result = "AssignRemExpr" }
+ override string getAPrimaryQlClass() { result = "AssignRemExpr" }
override string getOperator() { result = "%=" }
}
@@ -130,7 +130,7 @@ class AssignBitwiseOperation extends AssignOperation, @assign_bitwise_expr { }
* ```
*/
class AssignAndExpr extends AssignBitwiseOperation, @assignandexpr {
- override string getCanonicalQLClass() { result = "AssignAndExpr" }
+ override string getAPrimaryQlClass() { result = "AssignAndExpr" }
override string getOperator() { result = "&=" }
}
@@ -142,7 +142,7 @@ class AssignAndExpr extends AssignBitwiseOperation, @assignandexpr {
* ```
*/
class AssignOrExpr extends AssignBitwiseOperation, @assignorexpr {
- override string getCanonicalQLClass() { result = "AssignOrExpr" }
+ override string getAPrimaryQlClass() { result = "AssignOrExpr" }
override string getOperator() { result = "|=" }
}
@@ -154,7 +154,7 @@ class AssignOrExpr extends AssignBitwiseOperation, @assignorexpr {
* ```
*/
class AssignXorExpr extends AssignBitwiseOperation, @assignxorexpr {
- override string getCanonicalQLClass() { result = "AssignXorExpr" }
+ override string getAPrimaryQlClass() { result = "AssignXorExpr" }
override string getOperator() { result = "^=" }
}
@@ -166,7 +166,7 @@ class AssignXorExpr extends AssignBitwiseOperation, @assignxorexpr {
* ```
*/
class AssignLShiftExpr extends AssignBitwiseOperation, @assignlshiftexpr {
- override string getCanonicalQLClass() { result = "AssignLShiftExpr" }
+ override string getAPrimaryQlClass() { result = "AssignLShiftExpr" }
override string getOperator() { result = "<<=" }
}
@@ -178,7 +178,7 @@ class AssignLShiftExpr extends AssignBitwiseOperation, @assignlshiftexpr {
* ```
*/
class AssignRShiftExpr extends AssignBitwiseOperation, @assignrshiftexpr {
- override string getCanonicalQLClass() { result = "AssignRShiftExpr" }
+ override string getAPrimaryQlClass() { result = "AssignRShiftExpr" }
override string getOperator() { result = ">>=" }
}
@@ -190,7 +190,7 @@ class AssignRShiftExpr extends AssignBitwiseOperation, @assignrshiftexpr {
* ```
*/
class AssignPointerAddExpr extends AssignOperation, @assignpaddexpr {
- override string getCanonicalQLClass() { result = "AssignPointerAddExpr" }
+ override string getAPrimaryQlClass() { result = "AssignPointerAddExpr" }
override string getOperator() { result = "+=" }
}
@@ -202,7 +202,7 @@ class AssignPointerAddExpr extends AssignOperation, @assignpaddexpr {
* ```
*/
class AssignPointerSubExpr extends AssignOperation, @assignpsubexpr {
- override string getCanonicalQLClass() { result = "AssignPointerSubExpr" }
+ override string getAPrimaryQlClass() { result = "AssignPointerSubExpr" }
override string getOperator() { result = "-=" }
}
@@ -227,7 +227,7 @@ class ConditionDeclExpr extends Expr, @condition_decl {
*/
deprecated Expr getExpr() { result = this.getChild(0) }
- override string getCanonicalQLClass() { result = "ConditionDeclExpr" }
+ override string getAPrimaryQlClass() { result = "ConditionDeclExpr" }
/**
* Gets the compiler-generated variable access that conceptually occurs after
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll
index 2d0b6cda0d6e..3a4f14d9ab81 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides classes for modeling bitwise operations such as `~`, `<<`, `&` and
+ * `|`.
+ */
+
import semmle.code.cpp.exprs.Expr
/**
@@ -16,7 +21,7 @@ class ComplementExpr extends UnaryBitwiseOperation, @complementexpr {
override int getPrecedence() { result = 16 }
- override string getCanonicalQLClass() { result = "ComplementExpr" }
+ override string getAPrimaryQlClass() { result = "ComplementExpr" }
}
/**
@@ -35,7 +40,7 @@ class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr {
override int getPrecedence() { result = 12 }
- override string getCanonicalQLClass() { result = "LShiftExpr" }
+ override string getAPrimaryQlClass() { result = "LShiftExpr" }
}
/**
@@ -49,7 +54,7 @@ class RShiftExpr extends BinaryBitwiseOperation, @rshiftexpr {
override int getPrecedence() { result = 12 }
- override string getCanonicalQLClass() { result = "RShiftExpr" }
+ override string getAPrimaryQlClass() { result = "RShiftExpr" }
}
/**
@@ -63,7 +68,7 @@ class BitwiseAndExpr extends BinaryBitwiseOperation, @andexpr {
override int getPrecedence() { result = 8 }
- override string getCanonicalQLClass() { result = "BitwiseAndExpr" }
+ override string getAPrimaryQlClass() { result = "BitwiseAndExpr" }
}
/**
@@ -77,7 +82,7 @@ class BitwiseOrExpr extends BinaryBitwiseOperation, @orexpr {
override int getPrecedence() { result = 6 }
- override string getCanonicalQLClass() { result = "BitwiseOrExpr" }
+ override string getAPrimaryQlClass() { result = "BitwiseOrExpr" }
}
/**
@@ -91,5 +96,5 @@ class BitwiseXorExpr extends BinaryBitwiseOperation, @xorexpr {
override int getPrecedence() { result = 7 }
- override string getCanonicalQLClass() { result = "BitwiseXorExpr" }
+ override string getAPrimaryQlClass() { result = "BitwiseXorExpr" }
}
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll
index 5729a49086b4..3c627d712b44 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll
@@ -1,11 +1,16 @@
+/**
+ * Provides classes for modeling built-in operations. Built-in operations are
+ * typically compiler specific and are used by libraries and generated code.
+ */
+
import semmle.code.cpp.exprs.Expr
/**
- * A C/C++ builtin operation. This is the root QL class encompassing
+ * A C/C++ built-in operation. This is the root QL class encompassing
* built-in functionality.
*/
class BuiltInOperation extends Expr, @builtin_op {
- override string getCanonicalQLClass() { result = "BuiltInOperation" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperation" }
}
/**
@@ -25,7 +30,7 @@ class VarArgsExpr extends BuiltInOperation, @var_args_expr { }
class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr {
override string toString() { result = "__builtin_va_start" }
- override string getCanonicalQLClass() { result = "BuiltInVarArgsStart" }
+ override string getAPrimaryQlClass() { result = "BuiltInVarArgsStart" }
/**
* Gets the `va_list` argument.
@@ -50,7 +55,7 @@ class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr {
class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr {
override string toString() { result = "__builtin_va_end" }
- override string getCanonicalQLClass() { result = "BuiltInVarArgsEnd" }
+ override string getAPrimaryQlClass() { result = "BuiltInVarArgsEnd" }
/**
* Gets the `va_list` argument.
@@ -68,7 +73,7 @@ class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr {
class BuiltInVarArg extends BuiltInOperation, @vaargexpr {
override string toString() { result = "__builtin_va_arg" }
- override string getCanonicalQLClass() { result = "BuiltInVarArg" }
+ override string getAPrimaryQlClass() { result = "BuiltInVarArg" }
/**
* Gets the `va_list` argument.
@@ -88,7 +93,7 @@ class BuiltInVarArg extends BuiltInOperation, @vaargexpr {
class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr {
override string toString() { result = "__builtin_va_copy" }
- override string getCanonicalQLClass() { result = "BuiltInVarArgCopy" }
+ override string getAPrimaryQlClass() { result = "BuiltInVarArgCopy" }
/**
* Gets the destination `va_list` argument.
@@ -110,7 +115,7 @@ class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr {
class BuiltInNoOp extends BuiltInOperation, @noopexpr {
override string toString() { result = "__noop" }
- override string getCanonicalQLClass() { result = "BuiltInNoOp" }
+ override string getAPrimaryQlClass() { result = "BuiltInNoOp" }
}
/**
@@ -132,7 +137,7 @@ deprecated class BuiltInOperationOffsetOf = BuiltInOperationBuiltInOffsetOf;
class BuiltInOperationBuiltInOffsetOf extends BuiltInOperation, @offsetofexpr {
override string toString() { result = "__builtin_offsetof" }
- override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInOffsetOf" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInOffsetOf" }
}
/**
@@ -149,7 +154,7 @@ class BuiltInOperationBuiltInOffsetOf extends BuiltInOperation, @offsetofexpr {
class BuiltInIntAddr extends BuiltInOperation, @intaddrexpr {
override string toString() { result = "__INTADDR__" }
- override string getCanonicalQLClass() { result = "BuiltInIntAddr" }
+ override string getAPrimaryQlClass() { result = "BuiltInIntAddr" }
}
/**
@@ -164,7 +169,7 @@ class BuiltInIntAddr extends BuiltInOperation, @intaddrexpr {
class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr {
override string toString() { result = "__has_assign" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasAssign" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasAssign" }
}
/**
@@ -179,7 +184,7 @@ class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr {
class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr {
override string toString() { result = "__has_copy" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasCopy" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasCopy" }
}
/**
@@ -195,7 +200,7 @@ class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr {
class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassign {
override string toString() { result = "__has_nothrow_assign" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasNoThrowAssign" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasNoThrowAssign" }
}
/**
@@ -211,7 +216,7 @@ class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassi
class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothrowconstr {
override string toString() { result = "__has_nothrow_constructor" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasNoThrowConstructor" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasNoThrowConstructor" }
}
/**
@@ -226,7 +231,7 @@ class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothro
class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy {
override string toString() { result = "__has_nothrow_copy" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasNoThrowCopy" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasNoThrowCopy" }
}
/**
@@ -242,7 +247,7 @@ class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy {
class BuiltInOperationHasTrivialAssign extends BuiltInOperation, @hastrivialassign {
override string toString() { result = "__has_trivial_assign" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialAssign" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialAssign" }
}
/**
@@ -257,7 +262,7 @@ class BuiltInOperationHasTrivialAssign extends BuiltInOperation, @hastrivialassi
class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivialconstr {
override string toString() { result = "__has_trivial_constructor" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialConstructor" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialConstructor" }
}
/**
@@ -272,7 +277,7 @@ class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivia
class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy {
override string toString() { result = "__has_trivial_copy" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialCopy" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialCopy" }
}
/**
@@ -287,7 +292,7 @@ class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy {
class BuiltInOperationHasTrivialDestructor extends BuiltInOperation, @hastrivialdestructor {
override string toString() { result = "__has_trivial_destructor" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialDestructor" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialDestructor" }
}
/**
@@ -302,7 +307,7 @@ class BuiltInOperationHasTrivialDestructor extends BuiltInOperation, @hastrivial
class BuiltInOperationHasUserDestructor extends BuiltInOperation, @hasuserdestr {
override string toString() { result = "__has_user_destructor" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasUserDestructor" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasUserDestructor" }
}
/**
@@ -320,7 +325,7 @@ class BuiltInOperationHasUserDestructor extends BuiltInOperation, @hasuserdestr
class BuiltInOperationHasVirtualDestructor extends BuiltInOperation, @hasvirtualdestr {
override string toString() { result = "__has_virtual_destructor" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasVirtualDestructor" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasVirtualDestructor" }
}
/**
@@ -335,7 +340,7 @@ class BuiltInOperationHasVirtualDestructor extends BuiltInOperation, @hasvirtual
class BuiltInOperationIsAbstract extends BuiltInOperation, @isabstractexpr {
override string toString() { result = "__is_abstract" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsAbstract" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsAbstract" }
}
/**
@@ -350,7 +355,7 @@ class BuiltInOperationIsAbstract extends BuiltInOperation, @isabstractexpr {
class BuiltInOperationIsBaseOf extends BuiltInOperation, @isbaseofexpr {
override string toString() { result = "__is_base_of" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsBaseOf" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsBaseOf" }
}
/**
@@ -365,7 +370,7 @@ class BuiltInOperationIsBaseOf extends BuiltInOperation, @isbaseofexpr {
class BuiltInOperationIsClass extends BuiltInOperation, @isclassexpr {
override string toString() { result = "__is_class" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsClass" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsClass" }
}
/**
@@ -380,7 +385,7 @@ class BuiltInOperationIsClass extends BuiltInOperation, @isclassexpr {
class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr {
override string toString() { result = "__is_convertible_to" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsConvertibleTo" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsConvertibleTo" }
}
/**
@@ -395,7 +400,7 @@ class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr {
class BuiltInOperationIsEmpty extends BuiltInOperation, @isemptyexpr {
override string toString() { result = "__is_empty" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsEmpty" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsEmpty" }
}
/**
@@ -410,7 +415,7 @@ class BuiltInOperationIsEmpty extends BuiltInOperation, @isemptyexpr {
class BuiltInOperationIsEnum extends BuiltInOperation, @isenumexpr {
override string toString() { result = "__is_enum" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsEnum" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsEnum" }
}
/**
@@ -427,7 +432,7 @@ class BuiltInOperationIsEnum extends BuiltInOperation, @isenumexpr {
class BuiltInOperationIsPod extends BuiltInOperation, @ispodexpr {
override string toString() { result = "__is_pod" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsPod" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsPod" }
}
/**
@@ -442,7 +447,7 @@ class BuiltInOperationIsPod extends BuiltInOperation, @ispodexpr {
class BuiltInOperationIsPolymorphic extends BuiltInOperation, @ispolyexpr {
override string toString() { result = "__is_polymorphic" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsPolymorphic" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsPolymorphic" }
}
/**
@@ -457,7 +462,7 @@ class BuiltInOperationIsPolymorphic extends BuiltInOperation, @ispolyexpr {
class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr {
override string toString() { result = "__is_union" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsUnion" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsUnion" }
}
/**
@@ -496,7 +501,7 @@ class BuiltInOperationBuiltInTypesCompatibleP extends BuiltInOperation, @typesco
class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshufflevector {
override string toString() { result = "__builtin_shufflevector" }
- override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInShuffleVector" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInShuffleVector" }
}
/**
@@ -516,7 +521,7 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu
class BuiltInOperationBuiltInConvertVector extends BuiltInOperation, @builtinconvertvector {
override string toString() { result = "__builtin_convertvector" }
- override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInConvertVector" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInConvertVector" }
}
/**
@@ -538,7 +543,7 @@ class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation,
result = this.getOperand().(ReferenceDereferenceExpr).getChild(0).(Access).getTarget()
}
- override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInAddressOf" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInAddressOf" }
override string getOperator() { result = "__builtin_addressof" }
}
@@ -560,7 +565,7 @@ class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation,
@istriviallyconstructibleexpr {
override string toString() { result = "__is_trivially_constructible" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyConstructible" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyConstructible" }
}
/**
@@ -577,7 +582,7 @@ class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation,
class BuiltInOperationIsDestructible extends BuiltInOperation, @isdestructibleexpr {
override string toString() { result = "__is_destructible" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsDestructible" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsDestructible" }
}
/**
@@ -594,7 +599,7 @@ class BuiltInOperationIsDestructible extends BuiltInOperation, @isdestructibleex
class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrowdestructibleexpr {
override string toString() { result = "__is_nothrow_destructible" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsNothrowDestructible" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowDestructible" }
}
/**
@@ -610,7 +615,7 @@ class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrow
class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istriviallydestructibleexpr {
override string toString() { result = "__is_trivially_destructible" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyDestructible" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyDestructible" }
}
/**
@@ -629,7 +634,7 @@ class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istrivi
class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istriviallyassignableexpr {
override string toString() { result = "__is_trivially_assignable" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyAssignable" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyAssignable" }
}
/**
@@ -645,7 +650,7 @@ class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istrivial
class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowassignableexpr {
override string toString() { result = "__is_nothrow_assignable" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsNothrowAssignable" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowAssignable" }
}
/**
@@ -665,7 +670,7 @@ class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowas
class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayoutexpr {
override string toString() { result = "__is_standard_layout" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsStandardLayout" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsStandardLayout" }
}
/**
@@ -679,7 +684,7 @@ class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayo
class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istriviallycopyableexpr {
override string toString() { result = "__is_trivially_copyable" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsTriviallyCopyable" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyCopyable" }
}
/**
@@ -699,7 +704,7 @@ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istrivially
class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr {
override string toString() { result = "__is_literal_type" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsLiteralType" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsLiteralType" }
}
/**
@@ -717,7 +722,7 @@ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation,
@hastrivialmoveconstructorexpr {
override string toString() { result = "__has_trivial_move_constructor" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialMoveConstructor" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialMoveConstructor" }
}
/**
@@ -735,7 +740,7 @@ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation,
class BuiltInOperationHasTrivialMoveAssign extends BuiltInOperation, @hastrivialmoveassignexpr {
override string toString() { result = "__has_trivial_move_assign" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasTrivialMoveAssign" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialMoveAssign" }
}
/**
@@ -751,7 +756,7 @@ class BuiltInOperationHasTrivialMoveAssign extends BuiltInOperation, @hastrivial
class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrowmoveassignexpr {
override string toString() { result = "__has_nothrow_move_assign" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasNothrowMoveAssign" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasNothrowMoveAssign" }
}
/**
@@ -770,7 +775,7 @@ class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrow
class BuiltInOperationIsConstructible extends BuiltInOperation, @isconstructibleexpr {
override string toString() { result = "__is_constructible" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsConstructible" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsConstructible" }
}
/**
@@ -786,7 +791,7 @@ class BuiltInOperationIsConstructible extends BuiltInOperation, @isconstructible
class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothrowconstructibleexpr {
override string toString() { result = "__is_nothrow_constructible" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsNothrowConstructible" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowConstructible" }
}
/**
@@ -801,7 +806,7 @@ class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothro
class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr {
override string toString() { result = "__has_finalizer" }
- override string getCanonicalQLClass() { result = "BuiltInOperationHasFinalizer" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasFinalizer" }
}
/**
@@ -815,7 +820,7 @@ class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr {
class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr {
override string toString() { result = "__is_delegate" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsDelegate" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsDelegate" }
}
/**
@@ -828,7 +833,7 @@ class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr {
class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfaceclassexpr {
override string toString() { result = "__is_interface_class" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsInterfaceClass" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsInterfaceClass" }
}
/**
@@ -845,7 +850,7 @@ class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfacecla
class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr {
override string toString() { result = "__is_ref_array" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsRefArray" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsRefArray" }
}
/**
@@ -862,7 +867,7 @@ class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr {
class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr {
override string toString() { result = "__is_ref_class" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsRefClass" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsRefClass" }
}
/**
@@ -880,7 +885,7 @@ class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr {
class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr {
override string toString() { result = "__is_sealed" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsSealed" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsSealed" }
}
/**
@@ -899,7 +904,7 @@ class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr {
class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalueclassexpr {
override string toString() { result = "__is_simple_value_class" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsSimpleValueClass" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsSimpleValueClass" }
}
/**
@@ -916,7 +921,7 @@ class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalu
class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr {
override string toString() { result = "__is_value_class" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsValueClass" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsValueClass" }
}
/**
@@ -934,7 +939,7 @@ class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr {
class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr {
override string toString() { result = "__is_final" }
- override string getCanonicalQLClass() { result = "BuiltInOperationIsFinal" }
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsFinal" }
}
/**
@@ -949,7 +954,7 @@ class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr {
class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr {
override string toString() { result = "__builtin_choose_expr" }
- override string getCanonicalQLClass() { result = "BuiltInChooseExpr" }
+ override string getAPrimaryQlClass() { result = "BuiltInChooseExpr" }
}
/**
@@ -966,7 +971,7 @@ class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr {
class VectorFillOperation extends UnaryOperation, @vec_fill {
override string getOperator() { result = "(vector fill)" }
- override string getCanonicalQLClass() { result = "VectorFillOperation" }
+ override string getAPrimaryQlClass() { result = "VectorFillOperation" }
}
/**
@@ -975,7 +980,7 @@ class VectorFillOperation extends UnaryOperation, @vec_fill {
class BuiltInComplexOperation extends BuiltInOperation, @builtincomplex {
override string toString() { result = "__builtin_complex" }
- override string getCanonicalQLClass() { result = "BuiltInComplexOperation" }
+ override string getAPrimaryQlClass() { result = "BuiltInComplexOperation" }
/** Gets the operand corresponding to the real part of the complex number. */
Expr getRealOperand() { this.hasChild(result, 0) }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll
index abb26002091c..2f1c29be8bc9 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll
@@ -1,13 +1,29 @@
+/**
+ * Provides classes for modeling call expressions including direct calls to
+ * functions, constructor and destructor calls, and calls made through function
+ * pointers.
+ */
+
import semmle.code.cpp.exprs.Expr
import semmle.code.cpp.Function
private import semmle.code.cpp.dataflow.EscapesTree
+private class TCall = @funbindexpr or @callexpr;
+
/**
* A C/C++ call.
- *
- * This is the abstract root QL class for all types of calls.
*/
-abstract class Call extends Expr, NameQualifiableElement {
+class Call extends Expr, NameQualifiableElement, TCall {
+ // `@funbindexpr` (which is the dbscheme type for FunctionCall) is a union type that includes
+ // `@routineexpr. This dbscheme type includes accesses to functions that are not necessarily calls to
+ // that function. That's why the charpred for `FunctionCall` requires:
+ // ```
+ // iscall(underlyingElement(this), _)
+ // ```
+ // So for the charpred for `Call` we include the requirement that if this is an instance of
+ // `@funbindexpr` it must be a _call_ to the function.
+ Call() { this instanceof @callexpr or iscall(underlyingElement(this), _) }
+
/**
* Gets the number of arguments (actual parameters) of this call. The count
* does _not_ include the qualifier of the call, if any.
@@ -74,7 +90,7 @@ abstract class Call extends Expr, NameQualifiableElement {
* method, and it might not exist.
* - For a variable call, it never exists.
*/
- abstract Function getTarget();
+ Function getTarget() { none() } // overridden in subclasses
override int getPrecedence() { result = 17 }
@@ -148,7 +164,7 @@ abstract class Call extends Expr, NameQualifiableElement {
class FunctionCall extends Call, @funbindexpr {
FunctionCall() { iscall(underlyingElement(this), _) }
- override string getCanonicalQLClass() { result = "FunctionCall" }
+ override string getAPrimaryQlClass() { result = "FunctionCall" }
/** Gets an explicit template argument for this call. */
Locatable getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
@@ -297,7 +313,7 @@ class OverloadedPointerDereferenceExpr extends FunctionCall {
getTarget().getEffectiveNumberOfParameters() = 1
}
- override string getCanonicalQLClass() { result = "OverloadedPointerDereferenceExpr" }
+ override string getAPrimaryQlClass() { result = "OverloadedPointerDereferenceExpr" }
/**
* Gets the expression this operator * applies to.
@@ -345,7 +361,7 @@ class OverloadedPointerDereferenceExpr extends FunctionCall {
class OverloadedArrayExpr extends FunctionCall {
OverloadedArrayExpr() { getTarget().hasName("operator[]") }
- override string getCanonicalQLClass() { result = "OverloadedArrayExpr" }
+ override string getAPrimaryQlClass() { result = "OverloadedArrayExpr" }
/**
* Gets the expression being subscripted.
@@ -377,7 +393,7 @@ class ExprCall extends Call, @callexpr {
*/
Expr getExpr() { result = this.getChild(0) }
- override string getCanonicalQLClass() { result = "ExprCall" }
+ override string getAPrimaryQlClass() { result = "ExprCall" }
override Expr getAnArgument() { exists(int i | result = this.getChild(i) and i >= 1) }
@@ -401,7 +417,7 @@ class ExprCall extends Call, @callexpr {
class VariableCall extends ExprCall {
VariableCall() { this.getExpr() instanceof VariableAccess }
- override string getCanonicalQLClass() { result = "VariableCall" }
+ override string getAPrimaryQlClass() { result = "VariableCall" }
/**
* Gets the variable which yields the function pointer to call.
@@ -419,7 +435,7 @@ class VariableCall extends ExprCall {
class ConstructorCall extends FunctionCall {
ConstructorCall() { super.getTarget() instanceof Constructor }
- override string getCanonicalQLClass() { result = "ConstructorCall" }
+ override string getAPrimaryQlClass() { result = "ConstructorCall" }
/** Gets the constructor being called. */
override Constructor getTarget() { result = super.getTarget() }
@@ -438,7 +454,7 @@ class ThrowExpr extends Expr, @throw_expr {
*/
Expr getExpr() { result = this.getChild(0) }
- override string getCanonicalQLClass() { result = "ThrowExpr" }
+ override string getAPrimaryQlClass() { result = "ThrowExpr" }
override string toString() { result = "throw ..." }
@@ -454,7 +470,7 @@ class ThrowExpr extends Expr, @throw_expr {
class ReThrowExpr extends ThrowExpr {
ReThrowExpr() { this.getType() instanceof VoidType }
- override string getCanonicalQLClass() { result = "ReThrowExpr" }
+ override string getAPrimaryQlClass() { result = "ReThrowExpr" }
override string toString() { result = "re-throw exception " }
}
@@ -469,7 +485,7 @@ class ReThrowExpr extends ThrowExpr {
class DestructorCall extends FunctionCall {
DestructorCall() { super.getTarget() instanceof Destructor }
- override string getCanonicalQLClass() { result = "DestructorCall" }
+ override string getAPrimaryQlClass() { result = "DestructorCall" }
/** Gets the destructor being called. */
override Destructor getTarget() { result = super.getTarget() }
@@ -493,7 +509,7 @@ class VacuousDestructorCall extends Expr, @vacuous_destructor_call {
*/
Expr getQualifier() { result = this.getChild(0) }
- override string getCanonicalQLClass() { result = "VacuousDestructorCall" }
+ override string getAPrimaryQlClass() { result = "VacuousDestructorCall" }
override string toString() { result = "(vacuous destructor call)" }
}
@@ -506,7 +522,7 @@ class VacuousDestructorCall extends Expr, @vacuous_destructor_call {
* initializations.
*/
class ConstructorInit extends Expr, @ctorinit {
- override string getCanonicalQLClass() { result = "ConstructorInit" }
+ override string getAPrimaryQlClass() { result = "ConstructorInit" }
}
/**
@@ -514,7 +530,7 @@ class ConstructorInit extends Expr, @ctorinit {
* initializer list or compiler-generated actions.
*/
class ConstructorBaseInit extends ConstructorInit, ConstructorCall {
- override string getCanonicalQLClass() { result = "ConstructorBaseInit" }
+ override string getAPrimaryQlClass() { result = "ConstructorBaseInit" }
}
/**
@@ -531,7 +547,7 @@ class ConstructorBaseInit extends ConstructorInit, ConstructorCall {
* ```
*/
class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit {
- override string getCanonicalQLClass() { result = "ConstructorDirectInit" }
+ override string getAPrimaryQlClass() { result = "ConstructorDirectInit" }
}
/**
@@ -551,7 +567,7 @@ class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit {
* ```
*/
class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit {
- override string getCanonicalQLClass() { result = "ConstructorVirtualInit" }
+ override string getAPrimaryQlClass() { result = "ConstructorVirtualInit" }
}
/**
@@ -566,7 +582,7 @@ class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit {
* ```
*/
class ConstructorDelegationInit extends ConstructorBaseInit, @ctordelegatinginit {
- override string getCanonicalQLClass() { result = "ConstructorDelegationInit" }
+ override string getAPrimaryQlClass() { result = "ConstructorDelegationInit" }
}
/**
@@ -585,7 +601,7 @@ class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit {
/** Gets the field being initialized. */
Field getTarget() { varbind(underlyingElement(this), unresolveElement(result)) }
- override string getCanonicalQLClass() { result = "ConstructorFieldInit" }
+ override string getAPrimaryQlClass() { result = "ConstructorFieldInit" }
/**
* Gets the expression to which the field is initialized.
@@ -607,7 +623,7 @@ class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit {
* compiler-generated actions.
*/
class DestructorDestruction extends Expr, @dtordestruct {
- override string getCanonicalQLClass() { result = "DestructorDestruction" }
+ override string getAPrimaryQlClass() { result = "DestructorDestruction" }
}
/**
@@ -615,7 +631,7 @@ class DestructorDestruction extends Expr, @dtordestruct {
* compiler-generated actions.
*/
class DestructorBaseDestruction extends DestructorCall, DestructorDestruction {
- override string getCanonicalQLClass() { result = "DestructorBaseDestruction" }
+ override string getAPrimaryQlClass() { result = "DestructorBaseDestruction" }
}
/**
@@ -629,7 +645,7 @@ class DestructorBaseDestruction extends DestructorCall, DestructorDestruction {
* ```
*/
class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirectdestruct {
- override string getCanonicalQLClass() { result = "DestructorDirectDestruction" }
+ override string getAPrimaryQlClass() { result = "DestructorDirectDestruction" }
}
/**
@@ -646,7 +662,7 @@ class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirect
* ```
*/
class DestructorVirtualDestruction extends DestructorBaseDestruction, @dtorvirtualdestruct {
- override string getCanonicalQLClass() { result = "DestructorVirtualDestruction" }
+ override string getAPrimaryQlClass() { result = "DestructorVirtualDestruction" }
}
/**
@@ -664,7 +680,7 @@ class DestructorFieldDestruction extends DestructorDestruction, @dtorfielddestru
/** Gets the field being destructed. */
Field getTarget() { varbind(underlyingElement(this), unresolveElement(result)) }
- override string getCanonicalQLClass() { result = "DestructorFieldDestruction" }
+ override string getAPrimaryQlClass() { result = "DestructorFieldDestruction" }
/** Gets the compiler-generated call to the variable's destructor. */
DestructorCall getExpr() { result = this.getChild(0) }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
index a00014b9af75..e06ed095d401 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides classes for modeling C/C++ casts and conversions, as well as some
+ * type-related operators such as `sizeof` and `alignof`.
+ */
+
import semmle.code.cpp.exprs.Expr
private import semmle.code.cpp.internal.ResolveClass
@@ -92,7 +97,7 @@ module CastConsistency {
class CStyleCast extends Cast, @c_style_cast {
override string toString() { result = "(" + this.getType().getName() + ")..." }
- override string getCanonicalQLClass() { result = "CStyleCast" }
+ override string getAPrimaryQlClass() { result = "CStyleCast" }
override int getPrecedence() { result = 16 }
}
@@ -111,7 +116,7 @@ class CStyleCast extends Cast, @c_style_cast {
class StaticCast extends Cast, @static_cast {
override string toString() { result = "static_cast<" + this.getType().getName() + ">..." }
- override string getCanonicalQLClass() { result = "StaticCast" }
+ override string getAPrimaryQlClass() { result = "StaticCast" }
override int getPrecedence() { result = 17 }
}
@@ -129,7 +134,7 @@ class StaticCast extends Cast, @static_cast {
class ConstCast extends Cast, @const_cast {
override string toString() { result = "const_cast<" + this.getType().getName() + ">..." }
- override string getCanonicalQLClass() { result = "ConstCast" }
+ override string getAPrimaryQlClass() { result = "ConstCast" }
override int getPrecedence() { result = 17 }
}
@@ -147,7 +152,7 @@ class ConstCast extends Cast, @const_cast {
class ReinterpretCast extends Cast, @reinterpret_cast {
override string toString() { result = "reinterpret_cast<" + this.getType().getName() + ">..." }
- override string getCanonicalQLClass() { result = "ReinterpretCast" }
+ override string getAPrimaryQlClass() { result = "ReinterpretCast" }
override int getPrecedence() { result = 17 }
}
@@ -203,7 +208,7 @@ class IntegralConversion extends ArithmeticConversion {
isIntegralOrEnum(getExpr().getUnspecifiedType())
}
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "IntegralConversion"
}
@@ -223,7 +228,7 @@ class FloatingPointConversion extends ArithmeticConversion {
getExpr().getUnspecifiedType() instanceof FloatingPointType
}
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "FloatingPointConversion"
}
@@ -243,7 +248,7 @@ class FloatingPointToIntegralConversion extends ArithmeticConversion {
getExpr().getUnspecifiedType() instanceof FloatingPointType
}
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "FloatingPointToIntegralConversion"
}
@@ -263,7 +268,7 @@ class IntegralToFloatingPointConversion extends ArithmeticConversion {
isIntegralOrEnum(getExpr().getUnspecifiedType())
}
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "IntegralToFloatingPointConversion"
}
@@ -289,9 +294,7 @@ class PointerConversion extends Cast {
isPointerOrNullPointer(getExpr().getUnspecifiedType())
}
- override string getCanonicalQLClass() {
- not exists(qlCast(this)) and result = "PointerConversion"
- }
+ override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerConversion" }
override string getSemanticConversionString() { result = "pointer conversion" }
}
@@ -325,7 +328,7 @@ class PointerToMemberConversion extends Cast {
)
}
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "PointerToMemberConversion"
}
@@ -346,7 +349,7 @@ class PointerToIntegralConversion extends Cast {
isPointerOrNullPointer(getExpr().getUnspecifiedType())
}
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "PointerToIntegralConversion"
}
@@ -367,7 +370,7 @@ class IntegralToPointerConversion extends Cast {
isIntegralOrEnum(getExpr().getUnspecifiedType())
}
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "IntegralToPointerConversion"
}
@@ -385,7 +388,7 @@ class IntegralToPointerConversion extends Cast {
class BoolConversion extends Cast {
BoolConversion() { conversionkinds(underlyingElement(this), 1) }
- override string getCanonicalQLClass() { not exists(qlCast(this)) and result = "BoolConversion" }
+ override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "BoolConversion" }
override string getSemanticConversionString() { result = "conversion to bool" }
}
@@ -403,7 +406,7 @@ class VoidConversion extends Cast {
getUnspecifiedType() instanceof VoidType
}
- override string getCanonicalQLClass() { not exists(qlCast(this)) and result = "VoidConversion" }
+ override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "VoidConversion" }
override string getSemanticConversionString() { result = "conversion to void" }
}
@@ -479,7 +482,7 @@ private Class getConversionClass(Expr expr) {
class BaseClassConversion extends InheritanceConversion {
BaseClassConversion() { conversionkinds(underlyingElement(this), 2) }
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "BaseClassConversion"
}
@@ -506,7 +509,7 @@ class BaseClassConversion extends InheritanceConversion {
class DerivedClassConversion extends InheritanceConversion {
DerivedClassConversion() { conversionkinds(underlyingElement(this), 3) }
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "DerivedClassConversion"
}
@@ -528,7 +531,7 @@ class DerivedClassConversion extends InheritanceConversion {
class PointerToMemberBaseClassConversion extends Cast {
PointerToMemberBaseClassConversion() { conversionkinds(underlyingElement(this), 4) }
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "PointerToMemberBaseClassConversion"
}
@@ -548,7 +551,7 @@ class PointerToMemberBaseClassConversion extends Cast {
class PointerToMemberDerivedClassConversion extends Cast {
PointerToMemberDerivedClassConversion() { conversionkinds(underlyingElement(this), 5) }
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "PointerToMemberDerivedClassConversion"
}
@@ -569,9 +572,7 @@ class PointerToMemberDerivedClassConversion extends Cast {
class GlvalueConversion extends Cast {
GlvalueConversion() { conversionkinds(underlyingElement(this), 6) }
- override string getCanonicalQLClass() {
- not exists(qlCast(this)) and result = "GlvalueConversion"
- }
+ override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "GlvalueConversion" }
override string getSemanticConversionString() { result = "glvalue conversion" }
}
@@ -597,7 +598,7 @@ class GlvalueConversion extends Cast {
class PrvalueAdjustmentConversion extends Cast {
PrvalueAdjustmentConversion() { conversionkinds(underlyingElement(this), 7) }
- override string getCanonicalQLClass() {
+ override string getAPrimaryQlClass() {
not exists(qlCast(this)) and result = "PrvalueAdjustmentConversion"
}
@@ -620,7 +621,7 @@ class DynamicCast extends Cast, @dynamic_cast {
override int getPrecedence() { result = 17 }
- override string getCanonicalQLClass() { result = "DynamicCast" }
+ override string getAPrimaryQlClass() { result = "DynamicCast" }
override string getSemanticConversionString() { result = "dynamic_cast" }
}
@@ -660,6 +661,9 @@ class UuidofOperator extends Expr, @uuidof {
* ```
*/
class TypeidOperator extends Expr, @type_id {
+ /**
+ * Gets the type that is returned by this typeid expression.
+ */
Type getResultType() { typeid_bind(underlyingElement(this), unresolveElement(result)) }
/**
@@ -669,7 +673,7 @@ class TypeidOperator extends Expr, @type_id {
*/
deprecated Type getSpecifiedType() { result = this.getResultType() }
- override string getCanonicalQLClass() { result = "TypeidOperator" }
+ override string getAPrimaryQlClass() { result = "TypeidOperator" }
/**
* Gets the contained expression, if any (if this typeid contains
@@ -699,7 +703,7 @@ class TypeidOperator extends Expr, @type_id {
class SizeofPackOperator extends Expr, @sizeof_pack {
override string toString() { result = "sizeof...(...)" }
- override string getCanonicalQLClass() { result = "SizeofPackOperator" }
+ override string getAPrimaryQlClass() { result = "SizeofPackOperator" }
override predicate mayBeImpure() { none() }
@@ -722,7 +726,7 @@ class SizeofOperator extends Expr, @runtime_sizeof {
class SizeofExprOperator extends SizeofOperator {
SizeofExprOperator() { exists(Expr e | this.getChild(0) = e) }
- override string getCanonicalQLClass() { result = "SizeofExprOperator" }
+ override string getAPrimaryQlClass() { result = "SizeofExprOperator" }
/** Gets the contained expression. */
Expr getExprOperand() { result = this.getChild(0) }
@@ -750,7 +754,7 @@ class SizeofExprOperator extends SizeofOperator {
class SizeofTypeOperator extends SizeofOperator {
SizeofTypeOperator() { sizeof_bind(underlyingElement(this), _) }
- override string getCanonicalQLClass() { result = "SizeofTypeOperator" }
+ override string getAPrimaryQlClass() { result = "SizeofTypeOperator" }
/** Gets the contained type. */
Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) }
@@ -829,7 +833,7 @@ class ArrayToPointerConversion extends Conversion, @array_to_pointer {
/** Gets a textual representation of this conversion. */
override string toString() { result = "array to pointer conversion" }
- override string getCanonicalQLClass() { result = "ArrayToPointerConversion" }
+ override string getAPrimaryQlClass() { result = "ArrayToPointerConversion" }
override predicate mayBeImpure() { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll
index a0688890a23b..0c84e07f5533 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes for modeling comparisons such as `==`, `!=` and `<`.
+ */
+
import semmle.code.cpp.exprs.Expr
/**
@@ -21,7 +25,7 @@ class EqualityOperation extends ComparisonOperation, @eq_op_expr {
* ```
*/
class EQExpr extends EqualityOperation, @eqexpr {
- override string getCanonicalQLClass() { result = "EQExpr" }
+ override string getAPrimaryQlClass() { result = "EQExpr" }
override string getOperator() { result = "==" }
}
@@ -33,7 +37,7 @@ class EQExpr extends EqualityOperation, @eqexpr {
* ```
*/
class NEExpr extends EqualityOperation, @neexpr {
- override string getCanonicalQLClass() { result = "NEExpr" }
+ override string getAPrimaryQlClass() { result = "NEExpr" }
override string getOperator() { result = "!=" }
}
@@ -78,7 +82,7 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr {
* ```
*/
class GTExpr extends RelationalOperation, @gtexpr {
- override string getCanonicalQLClass() { result = "GTExpr" }
+ override string getAPrimaryQlClass() { result = "GTExpr" }
override string getOperator() { result = ">" }
@@ -94,7 +98,7 @@ class GTExpr extends RelationalOperation, @gtexpr {
* ```
*/
class LTExpr extends RelationalOperation, @ltexpr {
- override string getCanonicalQLClass() { result = "LTExpr" }
+ override string getAPrimaryQlClass() { result = "LTExpr" }
override string getOperator() { result = "<" }
@@ -110,7 +114,7 @@ class LTExpr extends RelationalOperation, @ltexpr {
* ```
*/
class GEExpr extends RelationalOperation, @geexpr {
- override string getCanonicalQLClass() { result = "GEExpr" }
+ override string getAPrimaryQlClass() { result = "GEExpr" }
override string getOperator() { result = ">=" }
@@ -126,7 +130,7 @@ class GEExpr extends RelationalOperation, @geexpr {
* ```
*/
class LEExpr extends RelationalOperation, @leexpr {
- override string getCanonicalQLClass() { result = "LEExpr" }
+ override string getAPrimaryQlClass() { result = "LEExpr" }
override string getOperator() { result = "<=" }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
index fd15a22fbd22..a39b215b54a2 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes modeling C/C++ expressions.
+ */
+
import semmle.code.cpp.Element
private import semmle.code.cpp.Enclosing
private import semmle.code.cpp.internal.ResolveClass
@@ -23,7 +27,7 @@ class Expr extends StmtParent, @expr {
Function getEnclosingFunction() { result = exprEnclosingElement(this) }
/** Gets the nearest enclosing set of curly braces around this expression in the source, if any. */
- Block getEnclosingBlock() { result = getEnclosingStmt().getEnclosingBlock() }
+ BlockStmt getEnclosingBlock() { result = getEnclosingStmt().getEnclosingBlock() }
override Stmt getEnclosingStmt() {
result = this.getParent().(Expr).getEnclosingStmt()
@@ -398,7 +402,7 @@ class Expr extends StmtParent, @expr {
*/
predicate hasImplicitConversion() {
exists(Expr e |
- exprconv(underlyingElement(this), unresolveElement(e)) and e.(Cast).isImplicit()
+ exprconv(underlyingElement(this), unresolveElement(e)) and e.(Conversion).isImplicit()
)
}
@@ -410,7 +414,7 @@ class Expr extends StmtParent, @expr {
*/
predicate hasExplicitConversion() {
exists(Expr e |
- exprconv(underlyingElement(this), unresolveElement(e)) and not e.(Cast).isImplicit()
+ exprconv(underlyingElement(this), unresolveElement(e)) and not e.(Conversion).isImplicit()
)
}
@@ -449,12 +453,14 @@ class Expr extends StmtParent, @expr {
* cast from B to C. Only (1) and (2) would be included.
*/
Expr getExplicitlyConverted() {
- // result is this or one of its conversions
- result = this.getConversion*() and
- // result is not an implicit conversion - it's either the expr or an explicit cast
- (result = this or not result.(Cast).isImplicit()) and
- // there is no further explicit conversion after result
- not exists(Cast other | other = result.getConversion+() and not other.isImplicit())
+ // For performance, we avoid a full transitive closure over `getConversion`.
+ // Since there can be several implicit conversions before and after an
+ // explicit conversion, use `getImplicitlyConverted` to step over them
+ // cheaply. Then, if there is an explicit conversion following the implict
+ // conversion sequence, recurse to handle multiple explicit conversions.
+ if this.getImplicitlyConverted().hasExplicitConversion()
+ then result = this.getImplicitlyConverted().getConversion().getExplicitlyConverted()
+ else result = this
}
/**
@@ -535,6 +541,17 @@ class BinaryOperation extends Operation, @bin_op_expr {
/** Gets the right operand of this binary operation. */
Expr getRightOperand() { this.hasChild(result, 1) }
+ /**
+ * Holds if `e1` and `e2` (in either order) are the two operands of this
+ * binary operation.
+ */
+ predicate hasOperands(Expr e1, Expr e2) {
+ exists(int i | i in [0, 1] |
+ this.hasChild(e1, i) and
+ this.hasChild(e2, 1 - i)
+ )
+ }
+
override string toString() { result = "... " + this.getOperator() + " ..." }
override predicate mayBeImpure() {
@@ -565,7 +582,7 @@ class BinaryOperation extends Operation, @bin_op_expr {
class ParenthesizedBracedInitializerList extends Expr, @braced_init_list {
override string toString() { result = "({...})" }
- override string getCanonicalQLClass() { result = "ParenthesizedBracedInitializerList" }
+ override string getAPrimaryQlClass() { result = "ParenthesizedBracedInitializerList" }
}
/**
@@ -580,7 +597,7 @@ class ParenthesizedBracedInitializerList extends Expr, @braced_init_list {
class ParenthesisExpr extends Conversion, @parexpr {
override string toString() { result = "(...)" }
- override string getCanonicalQLClass() { result = "ParenthesisExpr" }
+ override string getAPrimaryQlClass() { result = "ParenthesisExpr" }
}
/**
@@ -591,7 +608,7 @@ class ParenthesisExpr extends Conversion, @parexpr {
class ErrorExpr extends Expr, @errorexpr {
override string toString() { result = "" }
- override string getCanonicalQLClass() { result = "ErrorExpr" }
+ override string getAPrimaryQlClass() { result = "ErrorExpr" }
}
/**
@@ -606,7 +623,7 @@ class ErrorExpr extends Expr, @errorexpr {
class AssumeExpr extends Expr, @assume {
override string toString() { result = "__assume(...)" }
- override string getCanonicalQLClass() { result = "AssumeExpr" }
+ override string getAPrimaryQlClass() { result = "AssumeExpr" }
/**
* Gets the operand of the `__assume` expressions.
@@ -621,7 +638,7 @@ class AssumeExpr extends Expr, @assume {
* ```
*/
class CommaExpr extends Expr, @commaexpr {
- override string getCanonicalQLClass() { result = "CommaExpr" }
+ override string getAPrimaryQlClass() { result = "CommaExpr" }
/**
* Gets the left operand, which is the one whose value is discarded.
@@ -656,7 +673,7 @@ class CommaExpr extends Expr, @commaexpr {
* ```
*/
class AddressOfExpr extends UnaryOperation, @address_of {
- override string getCanonicalQLClass() { result = "AddressOfExpr" }
+ override string getAPrimaryQlClass() { result = "AddressOfExpr" }
/** Gets the function or variable whose address is taken. */
Declaration getAddressable() {
@@ -688,7 +705,7 @@ class AddressOfExpr extends UnaryOperation, @address_of {
class ReferenceToExpr extends Conversion, @reference_to {
override string toString() { result = "(reference to)" }
- override string getCanonicalQLClass() { result = "ReferenceToExpr" }
+ override string getAPrimaryQlClass() { result = "ReferenceToExpr" }
override int getPrecedence() { result = 16 }
}
@@ -702,7 +719,7 @@ class ReferenceToExpr extends Conversion, @reference_to {
* ```
*/
class PointerDereferenceExpr extends UnaryOperation, @indirect {
- override string getCanonicalQLClass() { result = "PointerDereferenceExpr" }
+ override string getAPrimaryQlClass() { result = "PointerDereferenceExpr" }
/**
* DEPRECATED: Use getOperand() instead.
@@ -740,7 +757,7 @@ class PointerDereferenceExpr extends UnaryOperation, @indirect {
class ReferenceDereferenceExpr extends Conversion, @ref_indirect {
override string toString() { result = "(reference dereference)" }
- override string getCanonicalQLClass() { result = "ReferenceDereferenceExpr" }
+ override string getAPrimaryQlClass() { result = "ReferenceDereferenceExpr" }
}
/**
@@ -846,7 +863,7 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
class NewExpr extends NewOrNewArrayExpr, @new_expr {
override string toString() { result = "new" }
- override string getCanonicalQLClass() { result = "NewExpr" }
+ override string getAPrimaryQlClass() { result = "NewExpr" }
/**
* Gets the type that is being allocated.
@@ -876,7 +893,7 @@ class NewExpr extends NewOrNewArrayExpr, @new_expr {
class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr {
override string toString() { result = "new[]" }
- override string getCanonicalQLClass() { result = "NewArrayExpr" }
+ override string getAPrimaryQlClass() { result = "NewArrayExpr" }
/**
* Gets the type that is being allocated.
@@ -924,7 +941,7 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr {
class DeleteExpr extends Expr, @delete_expr {
override string toString() { result = "delete" }
- override string getCanonicalQLClass() { result = "DeleteExpr" }
+ override string getAPrimaryQlClass() { result = "DeleteExpr" }
override int getPrecedence() { result = 16 }
@@ -998,7 +1015,7 @@ class DeleteExpr extends Expr, @delete_expr {
class DeleteArrayExpr extends Expr, @delete_array_expr {
override string toString() { result = "delete[]" }
- override string getCanonicalQLClass() { result = "DeleteArrayExpr" }
+ override string getAPrimaryQlClass() { result = "DeleteArrayExpr" }
override int getPrecedence() { result = 16 }
@@ -1078,7 +1095,7 @@ class StmtExpr extends Expr, @expr_stmt {
*/
Stmt getStmt() { result.getParent() = this }
- override string getCanonicalQLClass() { result = "StmtExpr" }
+ override string getAPrimaryQlClass() { result = "StmtExpr" }
/**
* Gets the result expression of the enclosed statement. For example,
@@ -1094,7 +1111,7 @@ class StmtExpr extends Expr, @expr_stmt {
/** Get the result expression of a statement. (Helper function for StmtExpr.) */
private Expr getStmtResultExpr(Stmt stmt) {
result = stmt.(ExprStmt).getExpr() or
- result = getStmtResultExpr(stmt.(Block).getLastStmt())
+ result = getStmtResultExpr(stmt.(BlockStmt).getLastStmt())
}
/**
@@ -1103,7 +1120,7 @@ private Expr getStmtResultExpr(Stmt stmt) {
class ThisExpr extends Expr, @thisaccess {
override string toString() { result = "this" }
- override string getCanonicalQLClass() { result = "ThisExpr" }
+ override string getAPrimaryQlClass() { result = "ThisExpr" }
override predicate mayBeImpure() { none() }
@@ -1139,7 +1156,7 @@ class BlockExpr extends Literal {
class NoExceptExpr extends Expr, @noexceptexpr {
override string toString() { result = "noexcept(...)" }
- override string getCanonicalQLClass() { result = "NoExceptExpr" }
+ override string getAPrimaryQlClass() { result = "NoExceptExpr" }
/**
* Gets the expression inside this noexcept expression.
@@ -1171,7 +1188,7 @@ class FoldExpr extends Expr, @foldexpr {
)
}
- override string getCanonicalQLClass() { result = "FoldExpr" }
+ override string getAPrimaryQlClass() { result = "FoldExpr" }
/** Gets the binary operator used in this fold expression, as a string. */
string getOperatorString() { fold(underlyingElement(this), result, _) }
@@ -1247,9 +1264,37 @@ private predicate constantTemplateLiteral(Expr e) {
* ```
*/
class SpaceshipExpr extends BinaryOperation, @spaceshipexpr {
- override string getCanonicalQLClass() { result = "SpaceshipExpr" }
+ override string getAPrimaryQlClass() { result = "SpaceshipExpr" }
override int getPrecedence() { result = 11 }
override string getOperator() { result = "<=>" }
}
+
+/**
+ * A C/C++ `co_await` expression.
+ * ```
+ * co_await foo();
+ * ```
+ */
+class CoAwaitExpr extends UnaryOperation, @co_await {
+ override string getAPrimaryQlClass() { result = "CoAwaitExpr" }
+
+ override string getOperator() { result = "co_await" }
+
+ override int getPrecedence() { result = 16 }
+}
+
+/**
+ * A C/C++ `co_yield` expression.
+ * ```
+ * co_yield 1;
+ * ```
+ */
+class CoYieldExpr extends UnaryOperation, @co_yield {
+ override string getAPrimaryQlClass() { result = "CoYieldExpr" }
+
+ override string getOperator() { result = "co_yield" }
+
+ override int getPrecedence() { result = 2 }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll
index 4a5b1b21c8dd..8a51001f4d5e 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes for modeling lambda expressions and their captures.
+ */
+
import semmle.code.cpp.exprs.Expr
import semmle.code.cpp.Class
@@ -15,7 +19,7 @@ import semmle.code.cpp.Class
class LambdaExpression extends Expr, @lambdaexpr {
override string toString() { result = "[...](...){...}" }
- override string getCanonicalQLClass() { result = "LambdaExpression" }
+ override string getAPrimaryQlClass() { result = "LambdaExpression" }
/**
* Gets an implicitly or explicitly captured value of this lambda expression.
@@ -75,7 +79,7 @@ class LambdaExpression extends Expr, @lambdaexpr {
class Closure extends Class {
Closure() { exists(LambdaExpression e | this = e.getType()) }
- override string getCanonicalQLClass() { result = "Closure" }
+ override string getAPrimaryQlClass() { result = "Closure" }
/** Gets the lambda expression of which this is the type. */
LambdaExpression getLambdaExpression() { result.getType() = this }
@@ -101,7 +105,7 @@ class Closure extends Class {
class LambdaCapture extends Locatable, @lambdacapture {
override string toString() { result = getField().getName() }
- override string getCanonicalQLClass() { result = "LambdaCapture" }
+ override string getAPrimaryQlClass() { result = "LambdaCapture" }
/**
* Holds if this capture was made implicitly.
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll
index 3360be330c26..9ab944d2cc33 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides classes for modeling literals in the source code such as `0`, `'c'`
+ * or `"string"`.
+ */
+
import semmle.code.cpp.exprs.Expr
/**
@@ -14,7 +19,7 @@ class Literal extends Expr, @literal {
result = "Unknown literal"
}
- override string getCanonicalQLClass() { result = "Literal" }
+ override string getAPrimaryQlClass() { result = "Literal" }
override predicate mayBeImpure() { none() }
@@ -35,7 +40,7 @@ class Literal extends Expr, @literal {
class LabelLiteral extends Literal {
LabelLiteral() { jumpinfo(underlyingElement(this), _, _) }
- override string getCanonicalQLClass() { result = "LabelLiteral" }
+ override string getAPrimaryQlClass() { result = "LabelLiteral" }
/** Gets the corresponding label statement. */
LabelStmt getLabel() { jumpinfo(underlyingElement(this), _, unresolveElement(result)) }
@@ -93,7 +98,7 @@ abstract class TextLiteral extends Literal {
class CharLiteral extends TextLiteral {
CharLiteral() { this.getValueText().regexpMatch("(?s)\\s*L?'.*") }
- override string getCanonicalQLClass() { result = "CharLiteral" }
+ override string getAPrimaryQlClass() { result = "CharLiteral" }
/**
* Gets the character of this literal. For example `L'a'` has character `"a"`.
@@ -115,7 +120,7 @@ class StringLiteral extends TextLiteral {
// @aggregateliteral rather than @literal.
}
- override string getCanonicalQLClass() { result = "StringLiteral" }
+ override string getAPrimaryQlClass() { result = "StringLiteral" }
}
/**
@@ -128,7 +133,7 @@ class StringLiteral extends TextLiteral {
class OctalLiteral extends Literal {
OctalLiteral() { super.getValueText().regexpMatch("\\s*0[0-7]+[uUlL]*\\s*") }
- override string getCanonicalQLClass() { result = "OctalLiteral" }
+ override string getAPrimaryQlClass() { result = "OctalLiteral" }
}
/**
@@ -140,14 +145,14 @@ class OctalLiteral extends Literal {
class HexLiteral extends Literal {
HexLiteral() { super.getValueText().regexpMatch("\\s*0[xX][0-9a-fA-F]+[uUlL]*\\s*") }
- override string getCanonicalQLClass() { result = "HexLiteral" }
+ override string getAPrimaryQlClass() { result = "HexLiteral" }
}
/**
* A C/C++ aggregate literal.
*/
class AggregateLiteral extends Expr, @aggregateliteral {
- override string getCanonicalQLClass() { result = "AggregateLiteral" }
+ override string getAPrimaryQlClass() { result = "AggregateLiteral" }
/**
* DEPRECATED: Use ClassAggregateLiteral.getFieldExpr() instead.
@@ -179,7 +184,7 @@ class ClassAggregateLiteral extends AggregateLiteral {
ClassAggregateLiteral() { classType = this.getUnspecifiedType() }
- override string getCanonicalQLClass() { result = "ClassAggregateLiteral" }
+ override string getAPrimaryQlClass() { result = "ClassAggregateLiteral" }
/**
* Gets the expression within the aggregate literal that is used to initialize
@@ -299,7 +304,7 @@ class ArrayAggregateLiteral extends ArrayOrVectorAggregateLiteral {
ArrayAggregateLiteral() { arrayType = this.getUnspecifiedType() }
- override string getCanonicalQLClass() { result = "ArrayAggregateLiteral" }
+ override string getAPrimaryQlClass() { result = "ArrayAggregateLiteral" }
override int getArraySize() { result = arrayType.getArraySize() }
@@ -323,7 +328,7 @@ class VectorAggregateLiteral extends ArrayOrVectorAggregateLiteral {
VectorAggregateLiteral() { vectorType = this.getUnspecifiedType() }
- override string getCanonicalQLClass() { result = "VectorAggregateLiteral" }
+ override string getAPrimaryQlClass() { result = "VectorAggregateLiteral" }
override int getArraySize() { result = vectorType.getNumElements() }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll
index fa55c1e91eb4..d0e207f7b31d 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides classes for modeling logical operations such as `!`, `&&`, `||`, and
+ * the ternary `? :` expression.
+ */
+
import semmle.code.cpp.exprs.Expr
/**
@@ -14,7 +19,7 @@ class UnaryLogicalOperation extends UnaryOperation, @un_log_op_expr { }
class NotExpr extends UnaryLogicalOperation, @notexpr {
override string getOperator() { result = "!" }
- override string getCanonicalQLClass() { result = "NotExpr" }
+ override string getAPrimaryQlClass() { result = "NotExpr" }
override int getPrecedence() { result = 16 }
}
@@ -46,7 +51,7 @@ class BinaryLogicalOperation extends BinaryOperation, @bin_log_op_expr {
class LogicalAndExpr extends BinaryLogicalOperation, @andlogicalexpr {
override string getOperator() { result = "&&" }
- override string getCanonicalQLClass() { result = "LogicalAndExpr" }
+ override string getAPrimaryQlClass() { result = "LogicalAndExpr" }
override int getPrecedence() { result = 5 }
@@ -67,7 +72,7 @@ class LogicalAndExpr extends BinaryLogicalOperation, @andlogicalexpr {
class LogicalOrExpr extends BinaryLogicalOperation, @orlogicalexpr {
override string getOperator() { result = "||" }
- override string getCanonicalQLClass() { result = "LogicalOrExpr" }
+ override string getAPrimaryQlClass() { result = "LogicalOrExpr" }
override int getPrecedence() { result = 4 }
@@ -89,7 +94,7 @@ class ConditionalExpr extends Operation, @conditionalexpr {
/** Gets the condition of this conditional expression. */
Expr getCondition() { expr_cond_guard(underlyingElement(this), unresolveElement(result)) }
- override string getCanonicalQLClass() { result = "ConditionalExpr" }
+ override string getAPrimaryQlClass() { result = "ConditionalExpr" }
/** Gets the 'then' expression of this conditional expression. */
Expr getThen() {
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll b/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll
index 61699023f8f1..c651ae9b1534 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/ObjectiveC.qll
@@ -1,3 +1,7 @@
+/**
+ * DEPRECATED: Objective-C is no longer supported.
+ */
+
import semmle.code.cpp.exprs.Expr
import semmle.code.cpp.Class
import semmle.code.cpp.ObjectiveC
diff --git a/cpp/ql/src/semmle/code/cpp/ir/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/IR.qll
index f019f20b6a83..381adad5e418 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/IR.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/IR.qll
@@ -1,3 +1,47 @@
+/**
+ * Provides classes that describe the Intermediate Representation (IR) of the program.
+ *
+ * The IR is a representation of the semantics of the program, with very little dependence on the
+ * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`,
+ * and `++i` all have the same semantic effect, but appear in the AST as three different types of
+ * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental
+ * operations similar to:
+ *
+ * ```
+ * r1(int*) = VariableAddress[i] // Compute the address of variable `i`
+ * r2(int) = Load &:r1, m0 // Load the value of `i`
+ * r3(int) = Constant[1] // An integer constant with the value `1`
+ * r4(int) = Add r2, r3 // Add `1` to the value of `i`
+ * r5(int) = Store &r1, r4 // Store the new value back into the variable `i`
+ * ```
+ *
+ * This allows IR-based analysis to focus on the fundamental operations, rather than having to be
+ * concerned with the various ways of expressing those operations in source code.
+ *
+ * The key classes in the IR are:
+ *
+ * - `IRFunction` - Contains the IR for an entire function definition, including all of that
+ * function's `Instruction`s, `IRBlock`s, and `IRVariables`.
+ * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be
+ * performed, the operands that produce the inputs to that operation, and the type of the result
+ * of the operation. Control flows from an `Instruction` to one of a set of successor
+ * `Instruction`s.
+ * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly
+ * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has
+ * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction`
+ * that produces its value (its "definition").
+ * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is
+ * created for each variable directly accessed by the function. In addition, `IRVariable`s are
+ * created to represent certain temporary storage locations that do not have explicitly declared
+ * variables in the source code, such as the return value of the function.
+ * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a
+ * sequence of instructions such that control flow can only enter the block at the first
+ * instruction, and can only leave the block from the last instruction.
+ * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType`
+ * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all
+ * be represented as the `IRType` `uint4`, a four-byte unsigned integer.
+ */
+
// Most queries should operate on the aliased SSA IR, so that's what we expose
-// publically as the "IR".
+// publicly as the "IR".
import implementation.aliased_ssa.IR
diff --git a/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll
index b5b7d7de7c24..b8abef8a5474 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/IRConfiguration.qll
@@ -1 +1,5 @@
+/**
+ * Module used to configure the IR generation process.
+ */
+
import implementation.IRConfiguration
diff --git a/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll b/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll
index 3ff802376357..c4ebf2f1eba2 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/PrintIR.qll
@@ -1 +1,11 @@
+/**
+ * Outputs a representation of the IR as a control flow graph.
+ *
+ * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
+ * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
+ * uses, however, it is better to write a query that imports `PrintIR.qll`, extends
+ * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to
+ * dump.
+ */
+
import implementation.aliased_ssa.PrintIR
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index ddf43651727c..d735c8422480 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -70,7 +70,7 @@ private DataFlow::Node getNodeForSource(Expr source) {
//
// This case goes together with the similar (but not identical) rule in
// `nodeIsBarrierIn`.
- result = DataFlow::definitionByReferenceNode(source) and
+ result = DataFlow::definitionByReferenceNodeFromArgument(source) and
not argv(source.(VariableAccess).getTarget())
)
}
@@ -210,7 +210,7 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
or
// This case goes together with the similar (but not identical) rule in
// `getNodeForSource`.
- node = DataFlow::definitionByReferenceNode(source)
+ node = DataFlow::definitionByReferenceNodeFromArgument(source)
)
}
@@ -264,9 +264,6 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
t instanceof Union
or
t instanceof ArrayType
- or
- // Buffers of unknown size
- t instanceof UnknownType
)
or
exists(BinaryInstruction bin |
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
index d8904625e0ea..e927634fec23 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
@@ -226,28 +226,13 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
}
/**
- * Holds if the call context `ctx` reduces the set of viable dispatch
- * targets of `ma` in `c`.
+ * Holds if the set of viable implementations that can be called by `call`
+ * might be improved by knowing the call context.
*/
-predicate reducedViableImplInCallContext(CallInstruction call, Function f, CallInstruction ctx) {
- none()
-}
-
-/**
- * Gets a viable dispatch target of `ma` in the context `ctx`. This is
- * restricted to those `ma`s for which the context makes a difference.
- */
-Function prunedViableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() }
-
-/**
- * Holds if flow returning from `m` to `ma` might return further and if
- * this path restricts the set of call sites that can be returned to.
- */
-predicate reducedViableImplInReturn(Function f, CallInstruction call) { none() }
+predicate mayBenefitFromCallContext(CallInstruction call, Function f) { none() }
/**
- * Gets a viable dispatch target of `ma` in the context `ctx`. This is
- * restricted to those `ma`s and results for which the return flow from the
- * result to `ma` restricts the possible context `ctx`.
+ * Gets a viable dispatch target of `call` in the context `ctx`. This is
+ * restricted to those `call`s for which a context might make a difference.
*/
-Function prunedViableImplInCallContextReverse(CallInstruction call, CallInstruction ctx) { none() }
+Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
index f876c04d6c66..8bc3d75ff86e 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll
@@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- store(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- nodeCandFwd1IsStored(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
exists(Node mid |
nodeCandFwd1(mid, fromArg, config) and
- read(mid, f, node)
+ read(mid, c, node)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
- exists(Node mid, Node node |
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
nodeCandFwd1(mid, config) and
- store(mid, f, node)
+ store(mid, tc, node, _) and
+ c = tc.getContent()
)
}
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- nodeCand1IsRead(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate nodeCand1IsRead(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- nodeCandFwd1IsStored(f, unbind(config)) and
- store(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
- nodeCand1IsRead(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
- nodeCand1(n2, unbind(config)) and
- store(n1, f, n2)
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
+ )
}
pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- read(n1, f, n2)
+ read(n1, c, n2)
}
pragma[noinline]
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
)
or
// store
- exists(Node mid, Content f |
+ exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, _, config) and
- store(mid, f, node, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, argStored, config) and
- nodeCandFwd2IsStored(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
// flow into a callable
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCand1(node, unbind(config)) and
nodeCandFwd2(mid, _, _, stored, config) and
- store(mid, f, node, config)
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
private predicate nodeCandFwd2Read(
- Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
) {
exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, true, config) and
- read(mid, f, node, config)
+ read(mid, c, node, config)
)
}
@@ -896,15 +901,15 @@ private predicate nodeCand2(
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, returnRead, read, config) and
- nodeCand2IsRead(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(Node mid, Content f, boolean read0 |
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
@@ -930,51 +935,51 @@ private predicate nodeCand2(
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd2(node, _, _, true, unbind(config)) and
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
Configuration config
) {
exists(Node mid |
- store(node, f, mid, config) and
+ storeCand1(node, c, mid, config) and
nodeCand2(mid, toReturn, returnRead, true, config) and
nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
pragma[nomagic]
-private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
exists(Node node |
- nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2Store(c, node, _, _, stored, conf) and
nodeCand2(node, _, _, stored, conf)
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
+ * Holds if `c` is the target of both a store and a read in the path graph
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
exists(boolean apNonEmpty |
- nodeCand2IsStored(f, apNonEmpty, conf) and
- nodeCand2IsRead(f, apNonEmpty, conf)
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
)
}
@@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2(
}
private module LocalFlowBigStep {
+ /**
+ * A node where some checking is required, and hence the big-step relation
+ * is not allowed to step over.
+ */
+ private class FlowCheckNode extends Node {
+ FlowCheckNode() {
+ this instanceof CastNode or
+ clearsContent(this, _)
+ }
+ }
+
/**
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1058,9 +1074,9 @@ private module LocalFlowBigStep {
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
node instanceof OutNodeExt or
- store(_, _, node) or
+ store(_, _, node, _) or
read(_, _, node) or
- node instanceof CastNode
+ node instanceof FlowCheckNode
)
}
@@ -1074,11 +1090,11 @@ private module LocalFlowBigStep {
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next) or
+ store(node, _, next, _) or
read(node, _, next)
)
or
- node instanceof CastNode
+ node instanceof FlowCheckNode
or
config.isSink(node)
}
@@ -1108,11 +1124,11 @@ private module LocalFlowBigStep {
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getErasedNodeTypeBound(node1)
+ t = getNodeType(node1)
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getErasedNodeTypeBound(node2)
+ t = getNodeType(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
@@ -1122,16 +1138,16 @@ private module LocalFlowBigStep {
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
localFlowStepNodeCand1(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getErasedNodeTypeBound(node2) and
+ t = getNodeType(node2) and
nodeCand2(node2, unbind(config))
)
)
@@ -1154,19 +1170,21 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2, config) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
nodeCand2(node2, _, _, true, unbind(config)) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
@@ -1183,9 +1201,8 @@ private predicate flowCandFwd(
Configuration config
) {
flowCandFwd0(node, fromArg, argApf, apf, config) and
- if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
- else any()
+ not apf.isClearedAt(node) and
+ if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any()
}
pragma[nomagic]
@@ -1197,7 +1214,7 @@ private predicate flowCandFwd0(
config.isSource(node) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
or
exists(Node mid |
flowCandFwd(mid, fromArg, argApf, apf, config) and
@@ -1223,21 +1240,22 @@ private predicate flowCandFwd0(
additionalJumpStep(mid, node, config) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
)
or
// store
- exists(Node mid, Content f |
- flowCandFwd(mid, fromArg, argApf, _, config) and
- storeCand2(mid, f, node, config) and
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
nodeCand2(node, _, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
)
or
// read
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, argApf, config) and
- flowCandFwdConsCand(f, apf, config) and
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
)
or
@@ -1261,24 +1279,30 @@ private predicate flowCandFwd0(
}
pragma[nomagic]
-private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
- exists(Node mid, Node n |
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
flowCandFwd(mid, _, _, apf, config) and
- storeCand2(mid, f, n, config) and
+ storeCand2(mid, tc, n, contentType, config) and
nodeCand2(n, _, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+ compatibleTypes(apf.getType(), contentType)
)
}
+pragma[nomagic]
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
pragma[nomagic]
private predicate flowCandFwdRead(
- Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, argApf, apf0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
- )
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
}
pragma[nomagic]
@@ -1385,17 +1409,15 @@ private predicate flowCand0(
)
or
// store
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, returnApf, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandConsCand(f, apf, config)
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
)
or
// read
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, returnApf, apf0, config) and
- flowCandFwdConsCand(f, apf0, config) and
- apf.headUsesContent(f)
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
)
or
// flow into a callable
@@ -1417,36 +1439,40 @@ private predicate flowCand0(
else returnApf = TAccessPathFrontNone()
}
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
+}
+
pragma[nomagic]
private predicate flowCandRead(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
exists(Node mid |
- readCand2(node, f, mid, config) and
+ readCandFwd(node, tc, apf, mid, config) and
flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
exists(Node mid |
- storeCand2(node, f, mid, config) and
- flowCand(mid, toReturn, returnApf, apf0, config)
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
- flowCandFwdConsCand(f, apf, config) and
- exists(Node n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, _, apf, config)
- )
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
}
pragma[nomagic]
@@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned(
)
}
-private newtype TAccessPath =
+private newtype TAccessPathApprox =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
- * 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 Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1522,20 +1548,18 @@ 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(Content 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)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content 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 Content f;
+private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil {
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathApproxConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) }
}
-private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons {
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override AccessPathApprox pop(TypedContent head) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ result = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ result = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private 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 `apa` from a source
+ * in the configuration `config`.
*
- * The Boolean `fromArg` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * The call context `cc` records whether the node is reached through an
+ * argument in a call, and if so, `argApa` records the approximate access path
+ * of that argument.
*/
private predicate flowFwd(
- Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
- Configuration config
+ Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, Configuration config
) {
- flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowFwd0(node, cc, argApa, apf, apa, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- Node node, boolean fromArg, 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
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
- exists(Node mid |
- flowFwd(mid, fromArg, argAp, apf, ap, config) and
- localFlowBigStep(mid, node, true, _, config, _)
+ exists(Node mid, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and
+ localFlowBigStep(mid, node, true, _, config, localCC)
)
or
- exists(Node mid, AccessPathNil nil |
- flowFwd(mid, fromArg, argAp, _, nil, config) and
- localFlowBigStep(mid, node, false, apf, config, _) and
- apf = ap.(AccessPathNil).getFront()
+ exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and
+ localFlowBigStep(mid, node, false, apf, config, localCC) and
+ 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
- fromArg = false and
- argAp = TAccessPathNone()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone()
)
or
- exists(Node mid, AccessPathNil nil |
+ exists(Node mid, AccessPathApproxNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
)
)
or
// store
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
- ap = push(f, ap0)
- )
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config))
or
// read
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
- flowFwdConsCand(f, apf, ap, config)
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and
+ flowFwdConsCand(tc, apf, apa, config)
)
or
// flow into a callable
- flowFwdIn(_, node, _, _, apf, ap, config) and
- fromArg = true 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 |
- flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
- fromArg = false
+ exists(DataFlowCallable c |
+ 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, fromArg, 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, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, LocalCallContext localCC, Configuration config
+) {
+ flowFwd(node, cc, argApa, apf, apa, config) and
+ localFlowEntry(node, config) and
+ localCC = getLocalCallContext(cc, node.getEnclosingCallable())
+}
+
pragma[nomagic]
private predicate flowFwdStore(
- Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- 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, fromArg, argAp, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ flowFwd(mid, cc, argApa, apf0, apa0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeCand2(mid, f, node, config) and
- flowCand(mid, _, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- flowCandConsCand(f, apf0, config) and
- apf.headUsesContent(f) and
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
flowCand(node, _, _, apf, unbind(config))
}
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2,
+ CallContext cc, AccessPathApproxOption argApa, Configuration config
+) {
+ flowFwd(node1, cc, argApa, apf0, apa0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
+}
+
pragma[nomagic]
private predicate flowFwdRead(
- Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
- Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc,
+ AccessPathApproxOption argApa, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
private predicate flowFwdConsCand(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+ TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(Node n |
- flowFwd(n, _, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ flowFwd(n, _, _, apf, apa, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
)
}
pragma[nomagic]
private predicate flowFwdIn(
- DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
- flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
+ flowFwd(arg, outercc, argApa, apf, apa, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
- flowCand(p, _, _, _, unbind(config))
+ 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, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
- flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowFwd(ret, innercc, argApa, apf, apa, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
- flowCand(node, _, _, _, unbind(config))
+ innerc = ret.getEnclosingCallable() and
+ flowCand(node, _, _, _, unbind(config)) and
+ (
+ resolveReturn(innercc, innerc, call)
+ or
+ 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, true, TAccessPathSome(argAp), apf, ap, config)
+ flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa,
+ config)
}
/**
@@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
- DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+ DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa,
+ Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
- flowFwdIn(call, p, fromArg, 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 `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
- * 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(Content f |
- flowStore(f, node, toReturn, returnAp, ap, config) and
- flowConsCand(f, ap, config)
+ exists(TypedContent tc |
+ 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, true, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- storeCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, apa, _, _, _, config) and
+ apa0 = push(tc, apa)
}
pragma[nomagic]
private predicate flowStore(
- Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- readCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, _, config) and
- ap0 = pop(f, ap) and
- flowFwdConsCand(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, apa, _, _, _, config) and
+ apa0 = pop(tc, apa) and
+ flowFwdConsCand(tc, _, apa0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, 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, f, 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)
}
/**
@@ -1969,12 +2028,13 @@ 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 |
- flowOut(call, ret, toReturn, returnAp, ap, config) and
- flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
+ exists(ReturnNodeExt ret, CallContextCall ccc |
+ flowOut(call, ret, toReturn, returnApa, apa, config) and
+ flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and
+ ccc.matchesCall(call)
)
}
@@ -1985,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, true, TAccessPathSome(ap), _, ap0, config)
- )
- }
+ TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) }
/**
* A context for generating flow summaries. This represents flow entry through
@@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
}
}
+private newtype TAccessPath =
+ TAccessPathNil(DataFlowType t) or
+ TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) }
+
private newtype TPathNode =
TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
@@ -2041,13 +2107,13 @@ private newtype TPathNode =
config.isSource(node) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TNil(getErasedNodeTypeBound(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) and
config = mid.getConfiguration() and
- flow(node, _, _, ap, unbind(config))
+ flow(node, _, _, ap.getApprox(), unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2059,12 +2125,99 @@ 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, _, _, TAccessPathNil(_)) 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 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) }
+
+ 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 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() {
+ 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() }
+}
+
/**
* 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.
@@ -2098,14 +2251,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TNil(getErasedNodeTypeBound(node))
+ ap = TAccessPathNil(getNodeType(node))
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
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
- not innercc instanceof CallContextCall and
- ap = mid.getAp() and
+ innercc instanceof CallContextNoCall and
+ apa = mid.getAp().getApprox() 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)
@@ -2321,10 +2494,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)
}
/**
@@ -2333,10 +2506,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)
)
}
@@ -2345,22 +2517,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 = ap.getApprox()
)
}
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)
)
}
@@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0(
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
AccessPath ap
) {
- pathIntoArg(mid, i, outercc, call, ap) and
- callable = resolveCall(call, outercc) and
- parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration())
+ exists(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())
+ )
}
/**
@@ -2403,7 +2578,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, AccessPath ap, AccessPathApprox apa,
+ Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
@@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough(
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp() and
+ apa = ap.getApprox() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
@@ -2419,11 +2596,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()))
)
}
@@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0(
*/
pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
- exists(DataFlowCall call, ReturnKindExt kind |
- pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
+ exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
+ pathThroughCallable0(call, mid, kind, cc, ap, apa) and
+ out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration()))
)
}
@@ -2521,10 +2699,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2533,7 +2711,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2544,7 +2722,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2562,15 +2740,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2591,7 +2769,7 @@ private module FlowExploration {
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
@@ -2608,7 +2786,7 @@ private module FlowExploration {
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), ap.getType())
+ then compatibleTypes(getNodeType(node), ap.getType())
else any()
)
}
@@ -2721,7 +2899,7 @@ private module FlowExploration {
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
)
or
@@ -2737,7 +2915,7 @@ private module FlowExploration {
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -2746,11 +2924,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getNodeType(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2769,35 +2948,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- store(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
@@ -2806,7 +2992,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
- not innercc instanceof CallContextCall and
+ innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = 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 f876c04d6c66..8bc3d75ff86e 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll
@@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- store(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- nodeCandFwd1IsStored(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
exists(Node mid |
nodeCandFwd1(mid, fromArg, config) and
- read(mid, f, node)
+ read(mid, c, node)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
- exists(Node mid, Node node |
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
nodeCandFwd1(mid, config) and
- store(mid, f, node)
+ store(mid, tc, node, _) and
+ c = tc.getContent()
)
}
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- nodeCand1IsRead(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate nodeCand1IsRead(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- nodeCandFwd1IsStored(f, unbind(config)) and
- store(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
- nodeCand1IsRead(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
- nodeCand1(n2, unbind(config)) and
- store(n1, f, n2)
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
+ )
}
pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- read(n1, f, n2)
+ read(n1, c, n2)
}
pragma[noinline]
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
)
or
// store
- exists(Node mid, Content f |
+ exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, _, config) and
- store(mid, f, node, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, argStored, config) and
- nodeCandFwd2IsStored(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
// flow into a callable
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCand1(node, unbind(config)) and
nodeCandFwd2(mid, _, _, stored, config) and
- store(mid, f, node, config)
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
private predicate nodeCandFwd2Read(
- Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
) {
exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, true, config) and
- read(mid, f, node, config)
+ read(mid, c, node, config)
)
}
@@ -896,15 +901,15 @@ private predicate nodeCand2(
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, returnRead, read, config) and
- nodeCand2IsRead(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(Node mid, Content f, boolean read0 |
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
@@ -930,51 +935,51 @@ private predicate nodeCand2(
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd2(node, _, _, true, unbind(config)) and
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
Configuration config
) {
exists(Node mid |
- store(node, f, mid, config) and
+ storeCand1(node, c, mid, config) and
nodeCand2(mid, toReturn, returnRead, true, config) and
nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
pragma[nomagic]
-private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
exists(Node node |
- nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2Store(c, node, _, _, stored, conf) and
nodeCand2(node, _, _, stored, conf)
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
+ * Holds if `c` is the target of both a store and a read in the path graph
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
exists(boolean apNonEmpty |
- nodeCand2IsStored(f, apNonEmpty, conf) and
- nodeCand2IsRead(f, apNonEmpty, conf)
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
)
}
@@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2(
}
private module LocalFlowBigStep {
+ /**
+ * A node where some checking is required, and hence the big-step relation
+ * is not allowed to step over.
+ */
+ private class FlowCheckNode extends Node {
+ FlowCheckNode() {
+ this instanceof CastNode or
+ clearsContent(this, _)
+ }
+ }
+
/**
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1058,9 +1074,9 @@ private module LocalFlowBigStep {
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
node instanceof OutNodeExt or
- store(_, _, node) or
+ store(_, _, node, _) or
read(_, _, node) or
- node instanceof CastNode
+ node instanceof FlowCheckNode
)
}
@@ -1074,11 +1090,11 @@ private module LocalFlowBigStep {
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next) or
+ store(node, _, next, _) or
read(node, _, next)
)
or
- node instanceof CastNode
+ node instanceof FlowCheckNode
or
config.isSink(node)
}
@@ -1108,11 +1124,11 @@ private module LocalFlowBigStep {
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getErasedNodeTypeBound(node1)
+ t = getNodeType(node1)
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getErasedNodeTypeBound(node2)
+ t = getNodeType(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
@@ -1122,16 +1138,16 @@ private module LocalFlowBigStep {
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
localFlowStepNodeCand1(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getErasedNodeTypeBound(node2) and
+ t = getNodeType(node2) and
nodeCand2(node2, unbind(config))
)
)
@@ -1154,19 +1170,21 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2, config) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
nodeCand2(node2, _, _, true, unbind(config)) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
@@ -1183,9 +1201,8 @@ private predicate flowCandFwd(
Configuration config
) {
flowCandFwd0(node, fromArg, argApf, apf, config) and
- if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
- else any()
+ not apf.isClearedAt(node) and
+ if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any()
}
pragma[nomagic]
@@ -1197,7 +1214,7 @@ private predicate flowCandFwd0(
config.isSource(node) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
or
exists(Node mid |
flowCandFwd(mid, fromArg, argApf, apf, config) and
@@ -1223,21 +1240,22 @@ private predicate flowCandFwd0(
additionalJumpStep(mid, node, config) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
)
or
// store
- exists(Node mid, Content f |
- flowCandFwd(mid, fromArg, argApf, _, config) and
- storeCand2(mid, f, node, config) and
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
nodeCand2(node, _, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
)
or
// read
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, argApf, config) and
- flowCandFwdConsCand(f, apf, config) and
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
)
or
@@ -1261,24 +1279,30 @@ private predicate flowCandFwd0(
}
pragma[nomagic]
-private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
- exists(Node mid, Node n |
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
flowCandFwd(mid, _, _, apf, config) and
- storeCand2(mid, f, n, config) and
+ storeCand2(mid, tc, n, contentType, config) and
nodeCand2(n, _, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+ compatibleTypes(apf.getType(), contentType)
)
}
+pragma[nomagic]
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
pragma[nomagic]
private predicate flowCandFwdRead(
- Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, argApf, apf0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
- )
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
}
pragma[nomagic]
@@ -1385,17 +1409,15 @@ private predicate flowCand0(
)
or
// store
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, returnApf, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandConsCand(f, apf, config)
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
)
or
// read
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, returnApf, apf0, config) and
- flowCandFwdConsCand(f, apf0, config) and
- apf.headUsesContent(f)
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
)
or
// flow into a callable
@@ -1417,36 +1439,40 @@ private predicate flowCand0(
else returnApf = TAccessPathFrontNone()
}
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
+}
+
pragma[nomagic]
private predicate flowCandRead(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
exists(Node mid |
- readCand2(node, f, mid, config) and
+ readCandFwd(node, tc, apf, mid, config) and
flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
exists(Node mid |
- storeCand2(node, f, mid, config) and
- flowCand(mid, toReturn, returnApf, apf0, config)
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
- flowCandFwdConsCand(f, apf, config) and
- exists(Node n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, _, apf, config)
- )
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
}
pragma[nomagic]
@@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned(
)
}
-private newtype TAccessPath =
+private newtype TAccessPathApprox =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
- * 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 Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1522,20 +1548,18 @@ 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(Content 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)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content 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 Content f;
+private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil {
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathApproxConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) }
}
-private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons {
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override AccessPathApprox pop(TypedContent head) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ result = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ result = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private 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 `apa` from a source
+ * in the configuration `config`.
*
- * The Boolean `fromArg` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * The call context `cc` records whether the node is reached through an
+ * argument in a call, and if so, `argApa` records the approximate access path
+ * of that argument.
*/
private predicate flowFwd(
- Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
- Configuration config
+ Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, Configuration config
) {
- flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowFwd0(node, cc, argApa, apf, apa, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- Node node, boolean fromArg, 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
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
- exists(Node mid |
- flowFwd(mid, fromArg, argAp, apf, ap, config) and
- localFlowBigStep(mid, node, true, _, config, _)
+ exists(Node mid, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and
+ localFlowBigStep(mid, node, true, _, config, localCC)
)
or
- exists(Node mid, AccessPathNil nil |
- flowFwd(mid, fromArg, argAp, _, nil, config) and
- localFlowBigStep(mid, node, false, apf, config, _) and
- apf = ap.(AccessPathNil).getFront()
+ exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and
+ localFlowBigStep(mid, node, false, apf, config, localCC) and
+ 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
- fromArg = false and
- argAp = TAccessPathNone()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone()
)
or
- exists(Node mid, AccessPathNil nil |
+ exists(Node mid, AccessPathApproxNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
)
)
or
// store
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
- ap = push(f, ap0)
- )
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config))
or
// read
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
- flowFwdConsCand(f, apf, ap, config)
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and
+ flowFwdConsCand(tc, apf, apa, config)
)
or
// flow into a callable
- flowFwdIn(_, node, _, _, apf, ap, config) and
- fromArg = true 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 |
- flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
- fromArg = false
+ exists(DataFlowCallable c |
+ 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, fromArg, 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, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, LocalCallContext localCC, Configuration config
+) {
+ flowFwd(node, cc, argApa, apf, apa, config) and
+ localFlowEntry(node, config) and
+ localCC = getLocalCallContext(cc, node.getEnclosingCallable())
+}
+
pragma[nomagic]
private predicate flowFwdStore(
- Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- 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, fromArg, argAp, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ flowFwd(mid, cc, argApa, apf0, apa0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeCand2(mid, f, node, config) and
- flowCand(mid, _, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- flowCandConsCand(f, apf0, config) and
- apf.headUsesContent(f) and
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
flowCand(node, _, _, apf, unbind(config))
}
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2,
+ CallContext cc, AccessPathApproxOption argApa, Configuration config
+) {
+ flowFwd(node1, cc, argApa, apf0, apa0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
+}
+
pragma[nomagic]
private predicate flowFwdRead(
- Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
- Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc,
+ AccessPathApproxOption argApa, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
private predicate flowFwdConsCand(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+ TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(Node n |
- flowFwd(n, _, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ flowFwd(n, _, _, apf, apa, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
)
}
pragma[nomagic]
private predicate flowFwdIn(
- DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
- flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
+ flowFwd(arg, outercc, argApa, apf, apa, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
- flowCand(p, _, _, _, unbind(config))
+ 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, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
- flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowFwd(ret, innercc, argApa, apf, apa, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
- flowCand(node, _, _, _, unbind(config))
+ innerc = ret.getEnclosingCallable() and
+ flowCand(node, _, _, _, unbind(config)) and
+ (
+ resolveReturn(innercc, innerc, call)
+ or
+ 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, true, TAccessPathSome(argAp), apf, ap, config)
+ flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa,
+ config)
}
/**
@@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
- DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+ DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa,
+ Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
- flowFwdIn(call, p, fromArg, 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 `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
- * 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(Content f |
- flowStore(f, node, toReturn, returnAp, ap, config) and
- flowConsCand(f, ap, config)
+ exists(TypedContent tc |
+ 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, true, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- storeCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, apa, _, _, _, config) and
+ apa0 = push(tc, apa)
}
pragma[nomagic]
private predicate flowStore(
- Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- readCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, _, config) and
- ap0 = pop(f, ap) and
- flowFwdConsCand(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, apa, _, _, _, config) and
+ apa0 = pop(tc, apa) and
+ flowFwdConsCand(tc, _, apa0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, 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, f, 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)
}
/**
@@ -1969,12 +2028,13 @@ 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 |
- flowOut(call, ret, toReturn, returnAp, ap, config) and
- flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
+ exists(ReturnNodeExt ret, CallContextCall ccc |
+ flowOut(call, ret, toReturn, returnApa, apa, config) and
+ flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and
+ ccc.matchesCall(call)
)
}
@@ -1985,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, true, TAccessPathSome(ap), _, ap0, config)
- )
- }
+ TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) }
/**
* A context for generating flow summaries. This represents flow entry through
@@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
}
}
+private newtype TAccessPath =
+ TAccessPathNil(DataFlowType t) or
+ TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) }
+
private newtype TPathNode =
TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
@@ -2041,13 +2107,13 @@ private newtype TPathNode =
config.isSource(node) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TNil(getErasedNodeTypeBound(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) and
config = mid.getConfiguration() and
- flow(node, _, _, ap, unbind(config))
+ flow(node, _, _, ap.getApprox(), unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2059,12 +2125,99 @@ 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, _, _, TAccessPathNil(_)) 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 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) }
+
+ 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 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() {
+ 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() }
+}
+
/**
* 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.
@@ -2098,14 +2251,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TNil(getErasedNodeTypeBound(node))
+ ap = TAccessPathNil(getNodeType(node))
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
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
- not innercc instanceof CallContextCall and
- ap = mid.getAp() and
+ innercc instanceof CallContextNoCall and
+ apa = mid.getAp().getApprox() 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)
@@ -2321,10 +2494,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)
}
/**
@@ -2333,10 +2506,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)
)
}
@@ -2345,22 +2517,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 = ap.getApprox()
)
}
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)
)
}
@@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0(
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
AccessPath ap
) {
- pathIntoArg(mid, i, outercc, call, ap) and
- callable = resolveCall(call, outercc) and
- parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration())
+ exists(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())
+ )
}
/**
@@ -2403,7 +2578,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, AccessPath ap, AccessPathApprox apa,
+ Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
@@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough(
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp() and
+ apa = ap.getApprox() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
@@ -2419,11 +2596,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()))
)
}
@@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0(
*/
pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
- exists(DataFlowCall call, ReturnKindExt kind |
- pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
+ exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
+ pathThroughCallable0(call, mid, kind, cc, ap, apa) and
+ out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration()))
)
}
@@ -2521,10 +2699,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2533,7 +2711,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2544,7 +2722,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2562,15 +2740,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2591,7 +2769,7 @@ private module FlowExploration {
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
@@ -2608,7 +2786,7 @@ private module FlowExploration {
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), ap.getType())
+ then compatibleTypes(getNodeType(node), ap.getType())
else any()
)
}
@@ -2721,7 +2899,7 @@ private module FlowExploration {
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
)
or
@@ -2737,7 +2915,7 @@ private module FlowExploration {
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -2746,11 +2924,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getNodeType(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2769,35 +2948,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- store(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
@@ -2806,7 +2992,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
- not innercc instanceof CallContextCall and
+ innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = 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 f876c04d6c66..8bc3d75ff86e 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll
@@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- store(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- nodeCandFwd1IsStored(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
exists(Node mid |
nodeCandFwd1(mid, fromArg, config) and
- read(mid, f, node)
+ read(mid, c, node)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
- exists(Node mid, Node node |
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
nodeCandFwd1(mid, config) and
- store(mid, f, node)
+ store(mid, tc, node, _) and
+ c = tc.getContent()
)
}
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- nodeCand1IsRead(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate nodeCand1IsRead(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- nodeCandFwd1IsStored(f, unbind(config)) and
- store(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
- nodeCand1IsRead(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
- nodeCand1(n2, unbind(config)) and
- store(n1, f, n2)
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
+ )
}
pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- read(n1, f, n2)
+ read(n1, c, n2)
}
pragma[noinline]
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
)
or
// store
- exists(Node mid, Content f |
+ exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, _, config) and
- store(mid, f, node, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, argStored, config) and
- nodeCandFwd2IsStored(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
// flow into a callable
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCand1(node, unbind(config)) and
nodeCandFwd2(mid, _, _, stored, config) and
- store(mid, f, node, config)
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
private predicate nodeCandFwd2Read(
- Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
) {
exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, true, config) and
- read(mid, f, node, config)
+ read(mid, c, node, config)
)
}
@@ -896,15 +901,15 @@ private predicate nodeCand2(
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, returnRead, read, config) and
- nodeCand2IsRead(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(Node mid, Content f, boolean read0 |
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
@@ -930,51 +935,51 @@ private predicate nodeCand2(
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd2(node, _, _, true, unbind(config)) and
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
Configuration config
) {
exists(Node mid |
- store(node, f, mid, config) and
+ storeCand1(node, c, mid, config) and
nodeCand2(mid, toReturn, returnRead, true, config) and
nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
pragma[nomagic]
-private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
exists(Node node |
- nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2Store(c, node, _, _, stored, conf) and
nodeCand2(node, _, _, stored, conf)
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
+ * Holds if `c` is the target of both a store and a read in the path graph
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
exists(boolean apNonEmpty |
- nodeCand2IsStored(f, apNonEmpty, conf) and
- nodeCand2IsRead(f, apNonEmpty, conf)
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
)
}
@@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2(
}
private module LocalFlowBigStep {
+ /**
+ * A node where some checking is required, and hence the big-step relation
+ * is not allowed to step over.
+ */
+ private class FlowCheckNode extends Node {
+ FlowCheckNode() {
+ this instanceof CastNode or
+ clearsContent(this, _)
+ }
+ }
+
/**
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1058,9 +1074,9 @@ private module LocalFlowBigStep {
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
node instanceof OutNodeExt or
- store(_, _, node) or
+ store(_, _, node, _) or
read(_, _, node) or
- node instanceof CastNode
+ node instanceof FlowCheckNode
)
}
@@ -1074,11 +1090,11 @@ private module LocalFlowBigStep {
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next) or
+ store(node, _, next, _) or
read(node, _, next)
)
or
- node instanceof CastNode
+ node instanceof FlowCheckNode
or
config.isSink(node)
}
@@ -1108,11 +1124,11 @@ private module LocalFlowBigStep {
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getErasedNodeTypeBound(node1)
+ t = getNodeType(node1)
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getErasedNodeTypeBound(node2)
+ t = getNodeType(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
@@ -1122,16 +1138,16 @@ private module LocalFlowBigStep {
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
localFlowStepNodeCand1(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getErasedNodeTypeBound(node2) and
+ t = getNodeType(node2) and
nodeCand2(node2, unbind(config))
)
)
@@ -1154,19 +1170,21 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2, config) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
nodeCand2(node2, _, _, true, unbind(config)) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
@@ -1183,9 +1201,8 @@ private predicate flowCandFwd(
Configuration config
) {
flowCandFwd0(node, fromArg, argApf, apf, config) and
- if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
- else any()
+ not apf.isClearedAt(node) and
+ if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any()
}
pragma[nomagic]
@@ -1197,7 +1214,7 @@ private predicate flowCandFwd0(
config.isSource(node) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
or
exists(Node mid |
flowCandFwd(mid, fromArg, argApf, apf, config) and
@@ -1223,21 +1240,22 @@ private predicate flowCandFwd0(
additionalJumpStep(mid, node, config) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
)
or
// store
- exists(Node mid, Content f |
- flowCandFwd(mid, fromArg, argApf, _, config) and
- storeCand2(mid, f, node, config) and
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
nodeCand2(node, _, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
)
or
// read
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, argApf, config) and
- flowCandFwdConsCand(f, apf, config) and
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
)
or
@@ -1261,24 +1279,30 @@ private predicate flowCandFwd0(
}
pragma[nomagic]
-private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
- exists(Node mid, Node n |
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
flowCandFwd(mid, _, _, apf, config) and
- storeCand2(mid, f, n, config) and
+ storeCand2(mid, tc, n, contentType, config) and
nodeCand2(n, _, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+ compatibleTypes(apf.getType(), contentType)
)
}
+pragma[nomagic]
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
pragma[nomagic]
private predicate flowCandFwdRead(
- Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, argApf, apf0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
- )
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
}
pragma[nomagic]
@@ -1385,17 +1409,15 @@ private predicate flowCand0(
)
or
// store
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, returnApf, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandConsCand(f, apf, config)
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
)
or
// read
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, returnApf, apf0, config) and
- flowCandFwdConsCand(f, apf0, config) and
- apf.headUsesContent(f)
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
)
or
// flow into a callable
@@ -1417,36 +1439,40 @@ private predicate flowCand0(
else returnApf = TAccessPathFrontNone()
}
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
+}
+
pragma[nomagic]
private predicate flowCandRead(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
exists(Node mid |
- readCand2(node, f, mid, config) and
+ readCandFwd(node, tc, apf, mid, config) and
flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
exists(Node mid |
- storeCand2(node, f, mid, config) and
- flowCand(mid, toReturn, returnApf, apf0, config)
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
- flowCandFwdConsCand(f, apf, config) and
- exists(Node n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, _, apf, config)
- )
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
}
pragma[nomagic]
@@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned(
)
}
-private newtype TAccessPath =
+private newtype TAccessPathApprox =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
- * 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 Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1522,20 +1548,18 @@ 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(Content 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)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content 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 Content f;
+private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil {
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathApproxConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) }
}
-private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons {
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override AccessPathApprox pop(TypedContent head) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ result = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ result = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private 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 `apa` from a source
+ * in the configuration `config`.
*
- * The Boolean `fromArg` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * The call context `cc` records whether the node is reached through an
+ * argument in a call, and if so, `argApa` records the approximate access path
+ * of that argument.
*/
private predicate flowFwd(
- Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
- Configuration config
+ Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, Configuration config
) {
- flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowFwd0(node, cc, argApa, apf, apa, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- Node node, boolean fromArg, 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
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
- exists(Node mid |
- flowFwd(mid, fromArg, argAp, apf, ap, config) and
- localFlowBigStep(mid, node, true, _, config, _)
+ exists(Node mid, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and
+ localFlowBigStep(mid, node, true, _, config, localCC)
)
or
- exists(Node mid, AccessPathNil nil |
- flowFwd(mid, fromArg, argAp, _, nil, config) and
- localFlowBigStep(mid, node, false, apf, config, _) and
- apf = ap.(AccessPathNil).getFront()
+ exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and
+ localFlowBigStep(mid, node, false, apf, config, localCC) and
+ 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
- fromArg = false and
- argAp = TAccessPathNone()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone()
)
or
- exists(Node mid, AccessPathNil nil |
+ exists(Node mid, AccessPathApproxNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
)
)
or
// store
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
- ap = push(f, ap0)
- )
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config))
or
// read
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
- flowFwdConsCand(f, apf, ap, config)
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and
+ flowFwdConsCand(tc, apf, apa, config)
)
or
// flow into a callable
- flowFwdIn(_, node, _, _, apf, ap, config) and
- fromArg = true 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 |
- flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
- fromArg = false
+ exists(DataFlowCallable c |
+ 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, fromArg, 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, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, LocalCallContext localCC, Configuration config
+) {
+ flowFwd(node, cc, argApa, apf, apa, config) and
+ localFlowEntry(node, config) and
+ localCC = getLocalCallContext(cc, node.getEnclosingCallable())
+}
+
pragma[nomagic]
private predicate flowFwdStore(
- Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- 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, fromArg, argAp, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ flowFwd(mid, cc, argApa, apf0, apa0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeCand2(mid, f, node, config) and
- flowCand(mid, _, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- flowCandConsCand(f, apf0, config) and
- apf.headUsesContent(f) and
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
flowCand(node, _, _, apf, unbind(config))
}
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2,
+ CallContext cc, AccessPathApproxOption argApa, Configuration config
+) {
+ flowFwd(node1, cc, argApa, apf0, apa0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
+}
+
pragma[nomagic]
private predicate flowFwdRead(
- Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
- Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc,
+ AccessPathApproxOption argApa, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
private predicate flowFwdConsCand(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+ TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(Node n |
- flowFwd(n, _, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ flowFwd(n, _, _, apf, apa, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
)
}
pragma[nomagic]
private predicate flowFwdIn(
- DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
- flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
+ flowFwd(arg, outercc, argApa, apf, apa, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
- flowCand(p, _, _, _, unbind(config))
+ 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, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
- flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowFwd(ret, innercc, argApa, apf, apa, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
- flowCand(node, _, _, _, unbind(config))
+ innerc = ret.getEnclosingCallable() and
+ flowCand(node, _, _, _, unbind(config)) and
+ (
+ resolveReturn(innercc, innerc, call)
+ or
+ 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, true, TAccessPathSome(argAp), apf, ap, config)
+ flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa,
+ config)
}
/**
@@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
- DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+ DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa,
+ Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
- flowFwdIn(call, p, fromArg, 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 `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
- * 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(Content f |
- flowStore(f, node, toReturn, returnAp, ap, config) and
- flowConsCand(f, ap, config)
+ exists(TypedContent tc |
+ 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, true, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- storeCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, apa, _, _, _, config) and
+ apa0 = push(tc, apa)
}
pragma[nomagic]
private predicate flowStore(
- Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- readCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, _, config) and
- ap0 = pop(f, ap) and
- flowFwdConsCand(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, apa, _, _, _, config) and
+ apa0 = pop(tc, apa) and
+ flowFwdConsCand(tc, _, apa0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, 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, f, 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)
}
/**
@@ -1969,12 +2028,13 @@ 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 |
- flowOut(call, ret, toReturn, returnAp, ap, config) and
- flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
+ exists(ReturnNodeExt ret, CallContextCall ccc |
+ flowOut(call, ret, toReturn, returnApa, apa, config) and
+ flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and
+ ccc.matchesCall(call)
)
}
@@ -1985,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, true, TAccessPathSome(ap), _, ap0, config)
- )
- }
+ TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) }
/**
* A context for generating flow summaries. This represents flow entry through
@@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
}
}
+private newtype TAccessPath =
+ TAccessPathNil(DataFlowType t) or
+ TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) }
+
private newtype TPathNode =
TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
@@ -2041,13 +2107,13 @@ private newtype TPathNode =
config.isSource(node) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TNil(getErasedNodeTypeBound(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) and
config = mid.getConfiguration() and
- flow(node, _, _, ap, unbind(config))
+ flow(node, _, _, ap.getApprox(), unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2059,12 +2125,99 @@ 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, _, _, TAccessPathNil(_)) 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 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) }
+
+ 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 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() {
+ 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() }
+}
+
/**
* 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.
@@ -2098,14 +2251,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TNil(getErasedNodeTypeBound(node))
+ ap = TAccessPathNil(getNodeType(node))
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
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
- not innercc instanceof CallContextCall and
- ap = mid.getAp() and
+ innercc instanceof CallContextNoCall and
+ apa = mid.getAp().getApprox() 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)
@@ -2321,10 +2494,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)
}
/**
@@ -2333,10 +2506,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)
)
}
@@ -2345,22 +2517,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 = ap.getApprox()
)
}
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)
)
}
@@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0(
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
AccessPath ap
) {
- pathIntoArg(mid, i, outercc, call, ap) and
- callable = resolveCall(call, outercc) and
- parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration())
+ exists(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())
+ )
}
/**
@@ -2403,7 +2578,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, AccessPath ap, AccessPathApprox apa,
+ Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
@@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough(
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp() and
+ apa = ap.getApprox() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
@@ -2419,11 +2596,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()))
)
}
@@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0(
*/
pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
- exists(DataFlowCall call, ReturnKindExt kind |
- pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
+ exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
+ pathThroughCallable0(call, mid, kind, cc, ap, apa) and
+ out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration()))
)
}
@@ -2521,10 +2699,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2533,7 +2711,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2544,7 +2722,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2562,15 +2740,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2591,7 +2769,7 @@ private module FlowExploration {
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
@@ -2608,7 +2786,7 @@ private module FlowExploration {
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), ap.getType())
+ then compatibleTypes(getNodeType(node), ap.getType())
else any()
)
}
@@ -2721,7 +2899,7 @@ private module FlowExploration {
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
)
or
@@ -2737,7 +2915,7 @@ private module FlowExploration {
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -2746,11 +2924,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getNodeType(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2769,35 +2948,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- store(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
@@ -2806,7 +2992,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
- not innercc instanceof CallContextCall and
+ innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = 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 f876c04d6c66..8bc3d75ff86e 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll
@@ -19,7 +19,7 @@ import DataFlowImplSpecific::Public
* a subclass whose characteristic predicate is a unique singleton string.
* For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends DataFlow::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -37,7 +37,7 @@ import DataFlowImplSpecific::Public
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -286,14 +286,14 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
exists(Node mid |
useFieldFlow(config) and
nodeCandFwd1(mid, fromArg, config) and
- store(mid, _, node) and
+ store(mid, _, node, _) and
not outBarrier(mid, config)
)
or
// read
- exists(Content f |
- nodeCandFwd1Read(f, node, fromArg, config) and
- nodeCandFwd1IsStored(f, config) and
+ exists(Content c |
+ nodeCandFwd1Read(c, node, fromArg, config) and
+ nodeCandFwd1IsStored(c, config) and
not inBarrier(node, config)
)
or
@@ -318,23 +318,24 @@ private predicate nodeCandFwd1(Node node, boolean fromArg, Configuration config)
private predicate nodeCandFwd1(Node node, Configuration config) { nodeCandFwd1(node, _, config) }
pragma[nomagic]
-private predicate nodeCandFwd1Read(Content f, Node node, boolean fromArg, Configuration config) {
+private predicate nodeCandFwd1Read(Content c, Node node, boolean fromArg, Configuration config) {
exists(Node mid |
nodeCandFwd1(mid, fromArg, config) and
- read(mid, f, node)
+ read(mid, c, node)
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd1`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd1`.
*/
pragma[nomagic]
-private predicate nodeCandFwd1IsStored(Content f, Configuration config) {
- exists(Node mid, Node node |
+private predicate nodeCandFwd1IsStored(Content c, Configuration config) {
+ exists(Node mid, Node node, TypedContent tc |
not fullBarrier(node, config) and
useFieldFlow(config) and
nodeCandFwd1(mid, config) and
- store(mid, f, node)
+ store(mid, tc, node, _) and
+ c = tc.getContent()
)
}
@@ -417,15 +418,15 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
)
or
// store
- exists(Content f |
- nodeCand1Store(f, node, toReturn, config) and
- nodeCand1IsRead(f, config)
+ exists(Content c |
+ nodeCand1Store(c, node, toReturn, config) and
+ nodeCand1IsRead(c, config)
)
or
// read
- exists(Node mid, Content f |
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ exists(Node mid, Content c |
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, toReturn, config)
)
or
@@ -447,35 +448,36 @@ private predicate nodeCand1_0(Node node, boolean toReturn, Configuration config)
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand1`.
*/
pragma[nomagic]
-private predicate nodeCand1IsRead(Content f, Configuration config) {
+private predicate nodeCand1IsRead(Content c, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd1(node, unbind(config)) and
- read(node, f, mid) and
- nodeCandFwd1IsStored(f, unbind(config)) and
+ read(node, c, mid) and
+ nodeCandFwd1IsStored(c, unbind(config)) and
nodeCand1(mid, _, config)
)
}
pragma[nomagic]
-private predicate nodeCand1Store(Content f, Node node, boolean toReturn, Configuration config) {
- exists(Node mid |
+private predicate nodeCand1Store(Content c, Node node, boolean toReturn, Configuration config) {
+ exists(Node mid, TypedContent tc |
nodeCand1(mid, toReturn, config) and
- nodeCandFwd1IsStored(f, unbind(config)) and
- store(node, f, mid)
+ nodeCandFwd1IsStored(c, unbind(config)) and
+ store(node, tc, mid, _) and
+ c = tc.getContent()
)
}
/**
- * Holds if `f` is the target of both a read and a store in the flow covered
+ * Holds if `c` is the target of both a read and a store in the flow covered
* by `nodeCand1`.
*/
-private predicate nodeCand1IsReadAndStored(Content f, Configuration conf) {
- nodeCand1IsRead(f, conf) and
- nodeCand1Store(f, _, _, conf)
+private predicate nodeCand1IsReadAndStored(Content c, Configuration conf) {
+ nodeCand1IsRead(c, conf) and
+ nodeCand1Store(c, _, _, conf)
}
pragma[nomagic]
@@ -566,17 +568,20 @@ private predicate parameterThroughFlowNodeCand1(ParameterNode p, Configuration c
}
pragma[nomagic]
-private predicate store(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
- nodeCand1(n2, unbind(config)) and
- store(n1, f, n2)
+private predicate storeCand1(Node n1, Content c, Node n2, Configuration config) {
+ exists(TypedContent tc |
+ nodeCand1IsReadAndStored(c, config) and
+ nodeCand1(n2, unbind(config)) and
+ store(n1, tc, n2, _) and
+ c = tc.getContent()
+ )
}
pragma[nomagic]
-private predicate read(Node n1, Content f, Node n2, Configuration config) {
- nodeCand1IsReadAndStored(f, config) and
+private predicate read(Node n1, Content c, Node n2, Configuration config) {
+ nodeCand1IsReadAndStored(c, config) and
nodeCand1(n2, unbind(config)) and
- read(n1, f, n2)
+ read(n1, c, n2)
}
pragma[noinline]
@@ -748,16 +753,16 @@ private predicate nodeCandFwd2(
)
or
// store
- exists(Node mid, Content f |
+ exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, _, config) and
- store(mid, f, node, config) and
+ storeCand1(mid, _, node, config) and
stored = true
)
or
// read
- exists(Content f |
- nodeCandFwd2Read(f, node, fromArg, argStored, config) and
- nodeCandFwd2IsStored(f, stored, config)
+ exists(Content c |
+ nodeCandFwd2Read(c, node, fromArg, argStored, config) and
+ nodeCandFwd2IsStored(c, stored, config)
)
or
// flow into a callable
@@ -781,25 +786,25 @@ private predicate nodeCandFwd2(
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCandFwd2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCandFwd2`.
*/
pragma[noinline]
-private predicate nodeCandFwd2IsStored(Content f, boolean stored, Configuration config) {
+private predicate nodeCandFwd2IsStored(Content c, boolean stored, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCand1(node, unbind(config)) and
nodeCandFwd2(mid, _, _, stored, config) and
- store(mid, f, node, config)
+ storeCand1(mid, c, node, config)
)
}
pragma[nomagic]
private predicate nodeCandFwd2Read(
- Content f, Node node, boolean fromArg, BooleanOption argStored, Configuration config
+ Content c, Node node, boolean fromArg, BooleanOption argStored, Configuration config
) {
exists(Node mid |
nodeCandFwd2(mid, fromArg, argStored, true, config) and
- read(mid, f, node, config)
+ read(mid, c, node, config)
)
}
@@ -896,15 +901,15 @@ private predicate nodeCand2(
)
or
// store
- exists(Content f |
- nodeCand2Store(f, node, toReturn, returnRead, read, config) and
- nodeCand2IsRead(f, read, config)
+ exists(Content c |
+ nodeCand2Store(c, node, toReturn, returnRead, read, config) and
+ nodeCand2IsRead(c, read, config)
)
or
// read
- exists(Node mid, Content f, boolean read0 |
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read0), unbind(config)) and
+ exists(Node mid, Content c, boolean read0 |
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read0), unbind(config)) and
nodeCand2(mid, toReturn, returnRead, read0, config) and
read = true
)
@@ -930,51 +935,51 @@ private predicate nodeCand2(
}
/**
- * Holds if `f` is the target of a read in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a read in the flow covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsRead(Content f, boolean read, Configuration config) {
+private predicate nodeCand2IsRead(Content c, boolean read, Configuration config) {
exists(Node mid, Node node |
useFieldFlow(config) and
nodeCandFwd2(node, _, _, true, unbind(config)) and
- read(node, f, mid, config) and
- nodeCandFwd2IsStored(f, unbindBool(read), unbind(config)) and
+ read(node, c, mid, config) and
+ nodeCandFwd2IsStored(c, unbindBool(read), unbind(config)) and
nodeCand2(mid, _, _, read, config)
)
}
pragma[nomagic]
private predicate nodeCand2Store(
- Content f, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
+ Content c, Node node, boolean toReturn, BooleanOption returnRead, boolean stored,
Configuration config
) {
exists(Node mid |
- store(node, f, mid, config) and
+ storeCand1(node, c, mid, config) and
nodeCand2(mid, toReturn, returnRead, true, config) and
nodeCandFwd2(node, _, _, stored, unbind(config))
)
}
/**
- * Holds if `f` is the target of a store in the flow covered by `nodeCand2`.
+ * Holds if `c` is the target of a store in the flow covered by `nodeCand2`.
*/
pragma[nomagic]
-private predicate nodeCand2IsStored(Content f, boolean stored, Configuration conf) {
+private predicate nodeCand2IsStored(Content c, boolean stored, Configuration conf) {
exists(Node node |
- nodeCand2Store(f, node, _, _, stored, conf) and
+ nodeCand2Store(c, node, _, _, stored, conf) and
nodeCand2(node, _, _, stored, conf)
)
}
/**
- * Holds if `f` is the target of both a store and a read in the path graph
+ * Holds if `c` is the target of both a store and a read in the path graph
* covered by `nodeCand2`.
*/
pragma[noinline]
-private predicate nodeCand2IsReadAndStored(Content f, Configuration conf) {
+private predicate nodeCand2IsReadAndStored(Content c, Configuration conf) {
exists(boolean apNonEmpty |
- nodeCand2IsStored(f, apNonEmpty, conf) and
- nodeCand2IsRead(f, apNonEmpty, conf)
+ nodeCand2IsStored(c, apNonEmpty, conf) and
+ nodeCand2IsRead(c, apNonEmpty, conf)
)
}
@@ -1046,11 +1051,22 @@ private predicate flowIntoCallNodeCand2(
}
private module LocalFlowBigStep {
+ /**
+ * A node where some checking is required, and hence the big-step relation
+ * is not allowed to step over.
+ */
+ private class FlowCheckNode extends Node {
+ FlowCheckNode() {
+ this instanceof CastNode or
+ clearsContent(this, _)
+ }
+ }
+
/**
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
- private predicate localFlowEntry(Node node, Configuration config) {
+ predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1058,9 +1074,9 @@ private module LocalFlowBigStep {
additionalJumpStep(_, node, config) or
node instanceof ParameterNode or
node instanceof OutNodeExt or
- store(_, _, node) or
+ store(_, _, node, _) or
read(_, _, node) or
- node instanceof CastNode
+ node instanceof FlowCheckNode
)
}
@@ -1074,11 +1090,11 @@ private module LocalFlowBigStep {
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
- store(node, _, next) or
+ store(node, _, next, _) or
read(node, _, next)
)
or
- node instanceof CastNode
+ node instanceof FlowCheckNode
or
config.isSink(node)
}
@@ -1108,11 +1124,11 @@ private module LocalFlowBigStep {
(
localFlowStepNodeCand1(node1, node2, config) and
preservesValue = true and
- t = getErasedNodeTypeBound(node1)
+ t = getNodeType(node1)
or
additionalLocalFlowStepNodeCand2(node1, node2, config) and
preservesValue = false and
- t = getErasedNodeTypeBound(node2)
+ t = getNodeType(node2)
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
@@ -1122,16 +1138,16 @@ private module LocalFlowBigStep {
exists(Node mid |
localFlowStepPlus(node1, mid, preservesValue, t, config, cc) and
localFlowStepNodeCand1(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
nodeCand2(node2, unbind(config))
)
or
exists(Node mid |
localFlowStepPlus(node1, mid, _, _, config, cc) and
additionalLocalFlowStepNodeCand2(mid, node2, config) and
- not mid instanceof CastNode and
+ not mid instanceof FlowCheckNode and
preservesValue = false and
- t = getErasedNodeTypeBound(node2) and
+ t = getNodeType(node2) and
nodeCand2(node2, unbind(config))
)
)
@@ -1154,19 +1170,21 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
pragma[nomagic]
-private predicate readCand2(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2, config) and
+private predicate readCand2(Node node1, Content c, Node node2, Configuration config) {
+ read(node1, c, node2, config) and
nodeCand2(node1, _, _, true, unbind(config)) and
nodeCand2(node2, config) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(c, unbind(config))
}
pragma[nomagic]
-private predicate storeCand2(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2, config) and
+private predicate storeCand2(
+ Node node1, TypedContent tc, Node node2, DataFlowType contentType, Configuration config
+) {
+ store(node1, tc, node2, contentType) and
nodeCand2(node1, config) and
nodeCand2(node2, _, _, true, unbind(config)) and
- nodeCand2IsReadAndStored(f, unbind(config))
+ nodeCand2IsReadAndStored(tc.getContent(), unbind(config))
}
/**
@@ -1183,9 +1201,8 @@ private predicate flowCandFwd(
Configuration config
) {
flowCandFwd0(node, fromArg, argApf, apf, config) and
- if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), apf.getType())
- else any()
+ not apf.isClearedAt(node) and
+ if node instanceof CastingNode then compatibleTypes(getNodeType(node), apf.getType()) else any()
}
pragma[nomagic]
@@ -1197,7 +1214,7 @@ private predicate flowCandFwd0(
config.isSource(node) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
or
exists(Node mid |
flowCandFwd(mid, fromArg, argApf, apf, config) and
@@ -1223,21 +1240,22 @@ private predicate flowCandFwd0(
additionalJumpStep(mid, node, config) and
fromArg = false and
argApf = TAccessPathFrontNone() and
- apf = TFrontNil(getErasedNodeTypeBound(node))
+ apf = TFrontNil(getNodeType(node))
)
or
// store
- exists(Node mid, Content f |
- flowCandFwd(mid, fromArg, argApf, _, config) and
- storeCand2(mid, f, node, config) and
+ exists(Node mid, TypedContent tc, AccessPathFront apf0, DataFlowType contentType |
+ flowCandFwd(mid, fromArg, argApf, apf0, config) and
+ storeCand2(mid, tc, node, contentType, config) and
nodeCand2(node, _, _, true, unbind(config)) and
- apf.headUsesContent(f)
+ apf.headUsesContent(tc) and
+ compatibleTypes(apf0.getType(), contentType)
)
or
// read
- exists(Content f |
- flowCandFwdRead(f, node, fromArg, argApf, config) and
- flowCandFwdConsCand(f, apf, config) and
+ exists(TypedContent tc |
+ flowCandFwdRead(tc, node, fromArg, argApf, config) and
+ flowCandFwdConsCand(tc, apf, config) and
nodeCand2(node, _, _, unbindBool(apf.toBoolNonEmpty()), unbind(config))
)
or
@@ -1261,24 +1279,30 @@ private predicate flowCandFwd0(
}
pragma[nomagic]
-private predicate flowCandFwdConsCand(Content f, AccessPathFront apf, Configuration config) {
- exists(Node mid, Node n |
+private predicate flowCandFwdConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ exists(Node mid, Node n, DataFlowType contentType |
flowCandFwd(mid, _, _, apf, config) and
- storeCand2(mid, f, n, config) and
+ storeCand2(mid, tc, n, contentType, config) and
nodeCand2(n, _, _, true, unbind(config)) and
- compatibleTypes(apf.getType(), f.getType())
+ compatibleTypes(apf.getType(), contentType)
)
}
+pragma[nomagic]
+private predicate flowCandFwdRead0(
+ Node node1, TypedContent tc, Content c, Node node2, boolean fromArg, AccessPathFrontOption argApf,
+ AccessPathFrontHead apf, Configuration config
+) {
+ flowCandFwd(node1, fromArg, argApf, apf, config) and
+ readCand2(node1, c, node2, config) and
+ apf.headUsesContent(tc)
+}
+
pragma[nomagic]
private predicate flowCandFwdRead(
- Content f, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
+ TypedContent tc, Node node, boolean fromArg, AccessPathFrontOption argApf, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowCandFwd(mid, fromArg, argApf, apf0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f)
- )
+ flowCandFwdRead0(_, tc, tc.getContent(), node, fromArg, argApf, _, config)
}
pragma[nomagic]
@@ -1385,17 +1409,15 @@ private predicate flowCand0(
)
or
// store
- exists(Content f, AccessPathFrontHead apf0 |
- flowCandStore(node, f, toReturn, returnApf, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandConsCand(f, apf, config)
+ exists(TypedContent tc |
+ flowCandStore(node, tc, apf, toReturn, returnApf, config) and
+ flowCandConsCand(tc, apf, config)
)
or
// read
- exists(Content f, AccessPathFront apf0 |
- flowCandRead(node, f, toReturn, returnApf, apf0, config) and
- flowCandFwdConsCand(f, apf0, config) and
- apf.headUsesContent(f)
+ exists(TypedContent tc, AccessPathFront apf0 |
+ flowCandRead(node, tc, apf, toReturn, returnApf, apf0, config) and
+ flowCandFwdConsCand(tc, apf0, config)
)
or
// flow into a callable
@@ -1417,36 +1439,40 @@ private predicate flowCand0(
else returnApf = TAccessPathFrontNone()
}
+pragma[nomagic]
+private predicate readCandFwd(
+ Node node1, TypedContent tc, AccessPathFront apf, Node node2, Configuration config
+) {
+ flowCandFwdRead0(node1, tc, tc.getContent(), node2, _, _, apf, config)
+}
+
pragma[nomagic]
private predicate flowCandRead(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFront apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, AccessPathFront apf0, Configuration config
) {
exists(Node mid |
- readCand2(node, f, mid, config) and
+ readCandFwd(node, tc, apf, mid, config) and
flowCand(mid, toReturn, returnApf, apf0, config)
)
}
pragma[nomagic]
private predicate flowCandStore(
- Node node, Content f, boolean toReturn, AccessPathFrontOption returnApf, AccessPathFrontHead apf0,
- Configuration config
+ Node node, TypedContent tc, AccessPathFront apf, boolean toReturn,
+ AccessPathFrontOption returnApf, Configuration config
) {
exists(Node mid |
- storeCand2(node, f, mid, config) and
- flowCand(mid, toReturn, returnApf, apf0, config)
+ flowCandFwd(node, _, _, apf, config) and
+ storeCand2(node, tc, mid, _, unbind(config)) and
+ flowCand(mid, toReturn, returnApf, TFrontHead(tc), unbind(config))
)
}
pragma[nomagic]
-private predicate flowCandConsCand(Content f, AccessPathFront apf, Configuration config) {
- flowCandFwdConsCand(f, apf, config) and
- exists(Node n, AccessPathFrontHead apf0 |
- flowCandFwd(n, _, _, apf0, config) and
- apf0.headUsesContent(f) and
- flowCandRead(n, f, _, _, apf, config)
- )
+private predicate flowCandConsCand(TypedContent tc, AccessPathFront apf, Configuration config) {
+ flowCandFwdConsCand(tc, apf, config) and
+ flowCandRead(_, tc, _, _, _, apf, config)
}
pragma[nomagic]
@@ -1497,24 +1523,24 @@ private predicate flowCandIsReturned(
)
}
-private newtype TAccessPath =
+private newtype TAccessPathApprox =
TNil(DataFlowType t) or
- TConsNil(Content f, DataFlowType t) { flowCandConsCand(f, TFrontNil(t), _) } or
- TConsCons(Content f1, Content f2, int len) {
- flowCandConsCand(f1, TFrontHead(f2), _) and len in [2 .. accessPathLimit()]
+ TConsNil(TypedContent tc, DataFlowType t) { flowCandConsCand(tc, TFrontNil(t), _) } or
+ TConsCons(TypedContent tc1, TypedContent tc2, int len) {
+ flowCandConsCand(tc1, TFrontHead(tc2), _) and len in [2 .. accessPathLimit()]
}
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first two
- * 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 Content getHead();
+ abstract TypedContent getHead();
abstract int len();
@@ -1522,20 +1548,18 @@ 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(Content 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)) }
- override Content getHead() { none() }
+ override TypedContent getHead() { none() }
override int len() { result = 0 }
@@ -1543,258 +1567,285 @@ private class AccessPathNil extends AccessPath, TNil {
override AccessPathFront getFront() { result = TFrontNil(t) }
- override predicate pop(Content 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 Content f;
+private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil {
+ private TypedContent tc;
private DataFlowType t;
- AccessPathConsNil() { this = TConsNil(f, t) }
+ AccessPathApproxConsNil() { this = TConsNil(tc, t) }
override string toString() {
// The `concat` becomes "" if `ppReprType` has no result.
- result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
+ result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t))
}
- override Content getHead() { result = f }
+ override TypedContent getHead() { result = tc }
override int len() { result = 1 }
- override DataFlowType getType() { result = f.getContainerType() }
+ override DataFlowType getType() { result = tc.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f) }
+ override AccessPathFront getFront() { result = TFrontHead(tc) }
- override predicate pop(Content head, AccessPath tail) { head = f and tail = TNil(t) }
+ override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) }
}
-private class AccessPathConsCons extends AccessPathCons, TConsCons {
- private Content f1;
- private Content f2;
+private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons {
+ private TypedContent tc1;
+ private TypedContent tc2;
private int len;
- AccessPathConsCons() { this = TConsCons(f1, f2, len) }
+ AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) }
override string toString() {
if len = 2
- then result = "[" + f1.toString() + ", " + f2.toString() + "]"
- else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc1.toString() + ", " + tc2.toString() + "]"
+ else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]"
}
- override Content getHead() { result = f1 }
+ override TypedContent getHead() { result = tc1 }
override int len() { result = len }
- override DataFlowType getType() { result = f1.getContainerType() }
+ override DataFlowType getType() { result = tc1.getContainerType() }
- override AccessPathFront getFront() { result = TFrontHead(f1) }
+ override AccessPathFront getFront() { result = TFrontHead(tc1) }
- override predicate pop(Content head, AccessPath tail) {
- head = f1 and
+ override AccessPathApprox pop(TypedContent head) {
+ head = tc1 and
(
- tail = TConsCons(f2, _, len - 1)
+ result = TConsCons(tc2, _, len - 1)
or
len = 2 and
- tail = TConsNil(f2, _)
+ result = TConsNil(tc2, _)
)
}
}
-/** Gets the access path obtained by popping `f` from `ap`, if any. */
-private AccessPath pop(Content f, AccessPath ap) { ap.pop(f, result) }
+/** Gets the access path obtained by popping `tc` from `ap`, if any. */
+private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) }
-/** Gets the access path obtained by pushing `f` onto `ap`. */
-private AccessPath push(Content f, AccessPath ap) { ap = pop(f, result) }
+/** Gets the access path obtained by pushing `tc` onto `ap`. */
+private 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 `apa` from a source
+ * in the configuration `config`.
*
- * The Boolean `fromArg` records whether the node is reached through an
- * argument in a call, and if so, `argAp` records the access path of that
- * argument.
+ * The call context `cc` records whether the node is reached through an
+ * argument in a call, and if so, `argApa` records the approximate access path
+ * of that argument.
*/
private predicate flowFwd(
- Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
- Configuration config
+ Node node, CallContext cc, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, Configuration config
) {
- flowFwd0(node, fromArg, argAp, apf, ap, config) and
+ flowFwd0(node, cc, argApa, apf, apa, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
- Node node, boolean fromArg, 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
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
- exists(Node mid |
- flowFwd(mid, fromArg, argAp, apf, ap, config) and
- localFlowBigStep(mid, node, true, _, config, _)
+ exists(Node mid, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, apf, apa, localCC, config) and
+ localFlowBigStep(mid, node, true, _, config, localCC)
)
or
- exists(Node mid, AccessPathNil nil |
- flowFwd(mid, fromArg, argAp, _, nil, config) and
- localFlowBigStep(mid, node, false, apf, config, _) and
- apf = ap.(AccessPathNil).getFront()
+ exists(Node mid, AccessPathApproxNil nil, LocalCallContext localCC |
+ flowFwdLocalEntry(mid, cc, argApa, _, nil, localCC, config) and
+ localFlowBigStep(mid, node, false, apf, config, localCC) and
+ 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
- fromArg = false and
- argAp = TAccessPathNone()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone()
)
or
- exists(Node mid, AccessPathNil nil |
+ exists(Node mid, AccessPathApproxNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
- fromArg = false and
- argAp = TAccessPathNone() and
- ap = TNil(getErasedNodeTypeBound(node)) and
- apf = ap.(AccessPathNil).getFront()
+ cc instanceof CallContextAny and
+ argApa = TAccessPathApproxNone() and
+ apa = TNil(getNodeType(node)) and
+ apf = apa.(AccessPathApproxNil).getFront()
)
)
or
// store
- exists(Content f, AccessPath ap0 |
- flowFwdStore(node, f, ap0, apf, fromArg, argAp, config) and
- ap = push(f, ap0)
- )
+ exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, apa), apf, cc, argApa, config))
or
// read
- exists(Content f |
- flowFwdRead(node, f, push(f, ap), fromArg, argAp, config) and
- flowFwdConsCand(f, apf, ap, config)
+ exists(TypedContent tc |
+ flowFwdRead(node, _, push(tc, apa), apf, cc, argApa, config) and
+ flowFwdConsCand(tc, apf, apa, config)
)
or
// flow into a callable
- flowFwdIn(_, node, _, _, apf, ap, config) and
- fromArg = true 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 |
- flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
- fromArg = false
+ exists(DataFlowCallable c |
+ 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, fromArg, 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, AccessPathApproxOption argApa, AccessPathFront apf,
+ AccessPathApprox apa, LocalCallContext localCC, Configuration config
+) {
+ flowFwd(node, cc, argApa, apf, apa, config) and
+ localFlowEntry(node, config) and
+ localCC = getLocalCallContext(cc, node.getEnclosingCallable())
+}
+
pragma[nomagic]
private predicate flowFwdStore(
- Node node, Content f, AccessPath ap0, AccessPathFront apf, boolean fromArg,
- 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, fromArg, argAp, apf0, ap0, config) and
- flowFwdStore1(mid, f, node, apf0, apf, config)
+ flowFwd(mid, cc, argApa, apf0, apa0, config) and
+ flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
pragma[nomagic]
-private predicate flowFwdStore0(
- Node mid, Content f, Node node, AccessPathFront apf0, Configuration config
+private predicate storeCand(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFront apf,
+ Configuration config
) {
- storeCand2(mid, f, node, config) and
- flowCand(mid, _, _, apf0, config)
+ storeCand2(mid, tc, node, _, config) and
+ flowCand(mid, _, _, apf0, config) and
+ apf.headUsesContent(tc)
}
pragma[noinline]
-private predicate flowFwdStore1(
- Node mid, Content f, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
+private predicate flowFwdStore0(
+ Node mid, TypedContent tc, Node node, AccessPathFront apf0, AccessPathFrontHead apf,
Configuration config
) {
- flowFwdStore0(mid, f, node, apf0, config) and
- flowCandConsCand(f, apf0, config) and
- apf.headUsesContent(f) and
+ storeCand(mid, tc, node, apf0, apf, config) and
+ flowCandConsCand(tc, apf0, config) and
flowCand(node, _, _, apf, unbind(config))
}
+pragma[nomagic]
+private predicate flowFwdRead0(
+ Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPathApprox apa0, Node node2,
+ CallContext cc, AccessPathApproxOption argApa, Configuration config
+) {
+ flowFwd(node1, cc, argApa, apf0, apa0, config) and
+ readCandFwd(node1, tc, apf0, node2, config)
+}
+
pragma[nomagic]
private predicate flowFwdRead(
- Node node, Content f, AccessPath ap0, boolean fromArg, AccessPathOption argAp,
- Configuration config
+ Node node, AccessPathFrontHead apf0, AccessPathApprox apa0, AccessPathFront apf, CallContext cc,
+ AccessPathApproxOption argApa, Configuration config
) {
- exists(Node mid, AccessPathFrontHead apf0 |
- flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
- readCand2(mid, f, node, config) and
- apf0.headUsesContent(f) and
- flowCand(node, _, _, _, unbind(config))
+ exists(Node mid, TypedContent tc |
+ flowFwdRead0(mid, tc, apf0, apa0, node, cc, argApa, config) and
+ flowCand(node, _, _, apf, unbind(config)) and
+ flowCandConsCand(tc, apf, unbind(config))
)
}
pragma[nomagic]
private predicate flowFwdConsCand(
- Content f, AccessPathFront apf, AccessPath ap, Configuration config
+ TypedContent tc, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(Node n |
- flowFwd(n, _, _, apf, ap, config) and
- flowFwdStore1(n, f, _, apf, _, config)
+ flowFwd(n, _, _, apf, apa, config) and
+ flowFwdStore0(n, tc, _, apf, _, config)
)
}
pragma[nomagic]
private predicate flowFwdIn(
- DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
- exists(ArgumentNode arg, boolean allowsFieldFlow |
- flowFwd(arg, fromArg, argAp, apf, ap, config) and
+ exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
+ flowFwd(arg, outercc, argApa, apf, apa, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
- flowCand(p, _, _, _, unbind(config))
+ 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, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
- AccessPath ap, Configuration config
+ DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
+ AccessPathApproxOption argApa, AccessPathFront apf, AccessPathApprox apa, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
- flowFwd(ret, fromArg, argAp, apf, ap, config) and
+ flowFwd(ret, innercc, argApa, apf, apa, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
- flowCand(node, _, _, _, unbind(config))
+ innerc = ret.getEnclosingCallable() and
+ flowCand(node, _, _, _, unbind(config)) and
+ (
+ resolveReturn(innercc, innerc, call)
+ or
+ 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, true, TAccessPathSome(argAp), apf, ap, config)
+ flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathApproxSome(argApa), apf, apa,
+ config)
}
/**
@@ -1802,166 +1853,174 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
- DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
+ DataFlowCall call, CallContext cc, AccessPathApproxOption argApa, AccessPathApprox apa,
+ Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
- flowFwdIn(call, p, fromArg, 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 `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
- * 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(Content f |
- flowStore(f, node, toReturn, returnAp, ap, config) and
- flowConsCand(f, ap, config)
+ exists(TypedContent tc |
+ 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, true, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- storeCand2(node1, f, node2, config) and
- flowFwdStore(node2, f, ap, _, _, _, config) and
- ap0 = push(f, ap)
+ storeCand2(node1, tc, node2, _, config) and
+ flowFwdStore(node2, tc, apa, _, _, _, config) and
+ apa0 = push(tc, apa)
}
pragma[nomagic]
private predicate flowStore(
- Content f, 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, f, 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, Content f, Node node2, AccessPath ap, AccessPath ap0, Configuration config
+ Node node1, TypedContent tc, Node node2, AccessPathApprox apa, AccessPathApprox apa0,
+ Configuration config
) {
- readCand2(node1, f, node2, config) and
- flowFwdRead(node2, f, ap, _, _, config) and
- ap0 = pop(f, ap) and
- flowFwdConsCand(f, _, ap0, unbind(config))
+ exists(AccessPathFrontHead apf |
+ readCandFwd(node1, tc, apf, node2, config) and
+ flowFwdRead(node2, apf, apa, _, _, _, config) and
+ apa0 = pop(tc, apa) and
+ flowFwdConsCand(tc, _, apa0, unbind(config))
+ )
}
pragma[nomagic]
-private predicate flowConsCand(Content f, 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, f, 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)
}
/**
@@ -1969,12 +2028,13 @@ 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 |
- flowOut(call, ret, toReturn, returnAp, ap, config) and
- flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
+ exists(ReturnNodeExt ret, CallContextCall ccc |
+ flowOut(call, ret, toReturn, returnApa, apa, config) and
+ flowFwd(ret, ccc, TAccessPathApproxSome(_), _, apa, config) and
+ ccc.matchesCall(call)
)
}
@@ -1985,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, true, TAccessPathSome(ap), _, ap0, config)
- )
- }
+ TSummaryCtxSome(ParameterNode p, AccessPath ap) { parameterMayFlowThrough(p, ap.getApprox()) }
/**
* A context for generating flow summaries. This represents flow entry through
@@ -2034,6 +2096,10 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
}
}
+private newtype TAccessPath =
+ TAccessPathNil(DataFlowType t) or
+ TAccessPathCons(TypedContent head, AccessPath tail) { flowConsCand(head, tail.getApprox(), _) }
+
private newtype TPathNode =
TPathNodeMid(Node node, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config) {
// A PathNode is introduced by a source ...
@@ -2041,13 +2107,13 @@ private newtype TPathNode =
config.isSource(node) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
- ap = TNil(getErasedNodeTypeBound(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) and
config = mid.getConfiguration() and
- flow(node, _, _, ap, unbind(config))
+ flow(node, _, _, ap.getApprox(), unbind(config))
)
} or
TPathNodeSink(Node node, Configuration config) {
@@ -2059,12 +2125,99 @@ 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, _, _, TAccessPathNil(_)) 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 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) }
+
+ 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 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() {
+ 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() }
+}
+
/**
* 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.
@@ -2098,14 +2251,31 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
+ private predicate isHidden() {
+ nodeIsHidden(this.getNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ }
+
+ private PathNode getASuccessorIfHidden() {
+ this.isHidden() and
+ result = this.(PathNodeImpl).getASuccessorImpl()
+ }
+
/** Gets a successor of this node, if any. */
- PathNode getASuccessor() { none() }
+ final PathNode getASuccessor() {
+ result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
+ not this.isHidden() and
+ not result.isHidden()
+ }
/** Holds if this node is a source. */
predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
+ abstract PathNode getASuccessorImpl();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -2180,7 +2350,7 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
result.getConfiguration() = unbind(this.getConfiguration())
}
- override PathNodeImpl getASuccessor() {
+ override PathNodeImpl getASuccessorImpl() {
// an intermediate step to another intermediate node
result = getSuccMid()
or
@@ -2217,7 +2387,7 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config }
- override PathNode getASuccessor() { none() }
+ override PathNode getASuccessorImpl() { none() }
override predicate isSource() { config.isSource(node) }
}
@@ -2251,12 +2421,12 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
- ap = TNil(getErasedNodeTypeBound(node))
+ ap = TAccessPathNil(getNodeType(node))
or
- exists(Content f | pathStoreStep(mid, node, pop(f, ap), f, cc)) and
+ exists(TypedContent tc | pathStoreStep(mid, node, ap.pop(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
- exists(Content f | pathReadStep(mid, node, push(f, ap), f, cc)) and
+ exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
sc = mid.getSummaryCtx()
or
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
@@ -2267,50 +2437,53 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, SummaryCt
}
pragma[nomagic]
-private predicate readCand(Node node1, Content f, Node node2, Configuration config) {
- read(node1, f, node2) and
+private predicate readCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ readCandFwd(node1, tc, _, node2, config) and
flow(node2, config)
}
pragma[nomagic]
-private predicate pathReadStep(PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc) {
+private predicate pathReadStep(
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
+) {
ap0 = mid.getAp() and
- readCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ readCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
pragma[nomagic]
-private predicate storeCand(Node node1, Content f, Node node2, Configuration config) {
- store(node1, f, node2) and
+private predicate storeCand(Node node1, TypedContent tc, Node node2, Configuration config) {
+ storeCand2(node1, tc, node2, _, config) and
flow(node2, config)
}
pragma[nomagic]
private predicate pathStoreStep(
- PathNodeMid mid, Node node, AccessPath ap0, Content f, CallContext cc
+ PathNodeMid mid, Node node, AccessPath ap0, TypedContent tc, CallContext cc
) {
ap0 = mid.getAp() and
- storeCand(mid.getNode(), f, node, mid.getConfiguration()) and
+ storeCand(mid.getNode(), tc, node, mid.getConfiguration()) and
cc = mid.getCallContext()
}
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
- not innercc instanceof CallContextCall and
- ap = mid.getAp() and
+ innercc instanceof CallContextNoCall and
+ apa = mid.getAp().getApprox() 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)
@@ -2321,10 +2494,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)
}
/**
@@ -2333,10 +2506,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)
)
}
@@ -2345,22 +2517,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 = ap.getApprox()
)
}
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)
)
}
@@ -2370,9 +2543,11 @@ private predicate pathIntoCallable0(
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
AccessPath ap
) {
- pathIntoArg(mid, i, outercc, call, ap) and
- callable = resolveCall(call, outercc) and
- parameterCand(callable, any(int j | j <= i and j >= i), ap, mid.getConfiguration())
+ exists(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())
+ )
}
/**
@@ -2403,7 +2578,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, AccessPath ap, AccessPathApprox apa,
+ Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
@@ -2412,6 +2588,7 @@ private predicate paramFlowsThrough(
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp() and
+ apa = ap.getApprox() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
@@ -2419,11 +2596,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()))
)
}
@@ -2433,9 +2611,9 @@ private predicate pathThroughCallable0(
*/
pragma[noinline]
private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPath ap) {
- exists(DataFlowCall call, ReturnKindExt kind |
- pathThroughCallable0(call, mid, kind, cc, ap) and
- out = getAnOutNodeFlow(kind, call, ap, mid.getConfiguration())
+ exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
+ pathThroughCallable0(call, mid, kind, cc, ap, apa) and
+ out = getAnOutNodeFlow(kind, call, apa, unbind(mid.getConfiguration()))
)
}
@@ -2521,10 +2699,10 @@ private module FlowExploration {
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
- TPartialCons(Content f, int len) { len in [1 .. 5] }
+ TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
/**
- * Conceptually a list of `Content`s followed by a `Type`, but only the first
+ * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first
* element of the list and its length are tracked. If data flows from a source to
* a given node with a given `AccessPath`, this indicates the sequence of
* dereference operations needed to get from the value in the node to the
@@ -2533,7 +2711,7 @@ private module FlowExploration {
private class PartialAccessPath extends TPartialAccessPath {
abstract string toString();
- Content getHead() { this = TPartialCons(result, _) }
+ TypedContent getHead() { this = TPartialCons(result, _) }
int len() {
this = TPartialNil(_) and result = 0
@@ -2544,7 +2722,7 @@ private module FlowExploration {
DataFlowType getType() {
this = TPartialNil(result)
or
- exists(Content head | this = TPartialCons(head, _) | result = head.getContainerType())
+ exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
@@ -2562,15 +2740,15 @@ private module FlowExploration {
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
override string toString() {
- exists(Content f, int len | this = TPartialCons(f, len) |
+ exists(TypedContent tc, int len | this = TPartialCons(tc, len) |
if len = 1
- then result = "[" + f.toString() + "]"
- else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
+ then result = "[" + tc.toString() + "]"
+ else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
override AccessPathFront getFront() {
- exists(Content f | this = TPartialCons(f, _) | result = TFrontHead(f))
+ exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
}
}
@@ -2591,7 +2769,7 @@ private module FlowExploration {
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
@@ -2608,7 +2786,7 @@ private module FlowExploration {
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
- then compatibleTypes(getErasedNodeTypeBound(node), ap.getType())
+ then compatibleTypes(getNodeType(node), ap.getType())
else any()
)
}
@@ -2721,7 +2899,7 @@ private module FlowExploration {
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
)
or
@@ -2737,7 +2915,7 @@ private module FlowExploration {
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedNodeTypeBound(node)) and
+ ap = TPartialNil(getNodeType(node)) and
config = mid.getConfiguration()
or
partialPathStoreStep(mid, _, _, node, ap) and
@@ -2746,11 +2924,12 @@ private module FlowExploration {
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
- exists(PartialAccessPath ap0, Content f |
- partialPathReadStep(mid, ap0, f, node, cc, config) and
+ exists(PartialAccessPath ap0, TypedContent tc |
+ partialPathReadStep(mid, ap0, tc, node, cc, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
- apConsFwd(ap, f, ap0, config)
+ apConsFwd(ap, tc, ap0, config) and
+ compatibleTypes(ap.getType(), getNodeType(node))
)
or
partialPathIntoCallable(mid, node, _, cc, sc1, sc2, _, ap, config)
@@ -2769,35 +2948,42 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
- PartialPathNodePriv mid, PartialAccessPath ap1, Content f, Node node, PartialAccessPath ap2
+ PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
+ PartialAccessPath ap2
) {
- ap1 = mid.getAp() and
- store(mid.getNode(), f, node) and
- ap2.getHead() = f and
- ap2.len() = unbindInt(ap1.len() + 1) and
- compatibleTypes(ap1.getType(), f.getType())
+ exists(Node midNode, DataFlowType contentType |
+ midNode = mid.getNode() and
+ ap1 = mid.getAp() and
+ store(midNode, tc, node, contentType) and
+ ap2.getHead() = tc and
+ ap2.len() = unbindInt(ap1.len() + 1) and
+ compatibleTypes(ap1.getType(), contentType)
+ )
}
pragma[nomagic]
private predicate apConsFwd(
- PartialAccessPath ap1, Content f, PartialAccessPath ap2, Configuration config
+ PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
- partialPathStoreStep(mid, ap1, f, _, ap2) and
+ partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate partialPathReadStep(
- PartialPathNodePriv mid, PartialAccessPath ap, Content f, Node node, CallContext cc,
+ PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
- ap = mid.getAp() and
- readStep(mid.getNode(), f, node) and
- ap.getHead() = f and
- config = mid.getConfiguration() and
- cc = mid.getCallContext()
+ exists(Node midNode |
+ midNode = mid.getNode() and
+ ap = mid.getAp() and
+ read(midNode, tc.getContent(), node) and
+ ap.getHead() = tc and
+ config = mid.getConfiguration() and
+ cc = mid.getCallContext()
+ )
}
private predicate partialPathOutOfCallable0(
@@ -2806,7 +2992,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
- not innercc instanceof CallContextCall and
+ innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
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 852f54974e24..892250f44bb8 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
@@ -22,7 +22,7 @@ private module Cached {
exists(int i |
viableParam(call, i, p) and
arg.argumentOf(call, i) and
- compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(p))
+ compatibleTypes(getNodeType(arg), getNodeType(p))
)
}
@@ -147,174 +147,140 @@ private module Cached {
}
}
- private module LocalFlowBigStep {
- private predicate localFlowEntry(Node n) {
- Cand::cand(_, n) and
- (
- n instanceof ParameterNode or
- n instanceof OutNode or
- readStep(_, _, n) or
- n instanceof CastNode
- )
- }
-
- private predicate localFlowExit(Node n) {
- Cand::cand(_, n) and
- (
- n instanceof ArgumentNode
- or
- n instanceof ReturnNode
- or
- readStep(n, _, _)
- or
- n instanceof CastNode
- or
- n =
- any(PostUpdateNode pun | Cand::parameterValueFlowsToPreUpdateCand(_, pun))
- .getPreUpdateNode()
- )
- }
-
- pragma[nomagic]
- private predicate localFlowStepPlus(Node node1, Node node2) {
- localFlowEntry(node1) and
- simpleLocalFlowStep(node1, node2) and
- node1 != node2
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid) and
- simpleLocalFlowStep(mid, node2) and
- not mid instanceof CastNode
- )
- }
-
- pragma[nomagic]
- predicate localFlowBigStep(Node node1, Node node2) {
- localFlowStepPlus(node1, node2) and
- localFlowExit(node2)
- }
- }
-
/**
* The final flow-through calculation:
*
- * - Input access paths are abstracted with a `ContentOption` parameter
- * that represents the head of the access path. `TContentNone()` means that
- * the access path is unrestricted.
+ * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`)
+ * or summarized as a single read step with before and after types recorded
+ * in the `ReadStepTypesOption` parameter.
* - Types are checked using the `compatibleTypes()` relation.
*/
private module Final {
/**
* Holds if `p` can flow to `node` in the same callable using only
- * value-preserving steps, not taking call contexts into account.
+ * value-preserving steps and possibly a single read step, not taking
+ * call contexts into account.
*
- * `contentIn` describes the content of `p` that can flow to `node`
- * (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
- predicate parameterValueFlow(ParameterNode p, Node node, ContentOption contentIn) {
- parameterValueFlow0(p, node, contentIn) and
+ predicate parameterValueFlow(ParameterNode p, Node node, ReadStepTypesOption read) {
+ parameterValueFlow0(p, node, read) and
if node instanceof CastingNode
then
// normal flow through
- contentIn = TContentNone() and
- compatibleTypes(getErasedNodeTypeBound(p), getErasedNodeTypeBound(node))
+ read = TReadStepTypesNone() and
+ compatibleTypes(getNodeType(p), getNodeType(node))
or
// getter
- exists(Content fIn |
- contentIn.getContent() = fIn and
- compatibleTypes(fIn.getType(), getErasedNodeTypeBound(node))
- )
+ compatibleTypes(read.getContentType(), getNodeType(node))
else any()
}
pragma[nomagic]
- private predicate parameterValueFlow0(ParameterNode p, Node node, ContentOption contentIn) {
+ private predicate parameterValueFlow0(ParameterNode p, Node node, ReadStepTypesOption read) {
p = node and
Cand::cand(p, _) and
- contentIn = TContentNone()
+ read = TReadStepTypesNone()
or
// local flow
exists(Node mid |
- parameterValueFlow(p, mid, contentIn) and
- LocalFlowBigStep::localFlowBigStep(mid, node)
+ parameterValueFlow(p, mid, read) and
+ simpleLocalFlowStep(mid, node)
)
or
// read
- exists(Node mid, Content f |
- parameterValueFlow(p, mid, TContentNone()) and
- readStep(mid, f, node) and
- contentIn.getContent() = f and
+ exists(Node mid |
+ parameterValueFlow(p, mid, TReadStepTypesNone()) and
+ readStepWithTypes(mid, read.getContainerType(), read.getContent(), node,
+ read.getContentType()) and
Cand::parameterValueFlowReturnCand(p, _, true) and
- compatibleTypes(getErasedNodeTypeBound(p), f.getContainerType())
+ compatibleTypes(getNodeType(p), read.getContainerType())
)
or
+ parameterValueFlow0_0(TReadStepTypesNone(), p, node, read)
+ }
+
+ pragma[nomagic]
+ private predicate parameterValueFlow0_0(
+ ReadStepTypesOption mustBeNone, ParameterNode p, Node node, ReadStepTypesOption read
+ ) {
// flow through: no prior read
exists(ArgumentNode arg |
- parameterValueFlowArg(p, arg, TContentNone()) and
- argumentValueFlowsThrough(arg, contentIn, node)
+ parameterValueFlowArg(p, arg, mustBeNone) and
+ argumentValueFlowsThrough(arg, read, node)
)
or
// flow through: no read inside method
exists(ArgumentNode arg |
- parameterValueFlowArg(p, arg, contentIn) and
- argumentValueFlowsThrough(arg, TContentNone(), node)
+ parameterValueFlowArg(p, arg, read) and
+ argumentValueFlowsThrough(arg, mustBeNone, node)
)
}
pragma[nomagic]
private predicate parameterValueFlowArg(
- ParameterNode p, ArgumentNode arg, ContentOption contentIn
+ ParameterNode p, ArgumentNode arg, ReadStepTypesOption read
) {
- parameterValueFlow(p, arg, contentIn) and
+ parameterValueFlow(p, arg, read) and
Cand::argumentValueFlowsThroughCand(arg, _, _)
}
pragma[nomagic]
private predicate argumentValueFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKind kind, ContentOption contentIn
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind, ReadStepTypesOption read
) {
exists(ParameterNode param | viableParamArg(call, param, arg) |
- parameterValueFlowReturn(param, kind, contentIn)
+ parameterValueFlowReturn(param, kind, read)
)
}
/**
- * Holds if `arg` flows to `out` through a call using only value-preserving steps,
- * not taking call contexts into account.
+ * Holds if `arg` flows to `out` through a call using only
+ * value-preserving steps and possibly a single read step, not taking
+ * call contexts into account.
*
- * `contentIn` describes the content of `arg` that can flow to `out` (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
pragma[nomagic]
- predicate argumentValueFlowsThrough(ArgumentNode arg, ContentOption contentIn, Node out) {
+ predicate argumentValueFlowsThrough(ArgumentNode arg, ReadStepTypesOption read, Node out) {
exists(DataFlowCall call, ReturnKind kind |
- argumentValueFlowsThrough0(call, arg, kind, contentIn) and
+ argumentValueFlowsThrough0(call, arg, kind, read) and
out = getAnOutNode(call, kind)
|
// normal flow through
- contentIn = TContentNone() and
- compatibleTypes(getErasedNodeTypeBound(arg), getErasedNodeTypeBound(out))
+ read = TReadStepTypesNone() and
+ compatibleTypes(getNodeType(arg), getNodeType(out))
or
// getter
- exists(Content fIn |
- contentIn.getContent() = fIn and
- compatibleTypes(getErasedNodeTypeBound(arg), fIn.getContainerType()) and
- compatibleTypes(fIn.getType(), getErasedNodeTypeBound(out))
- )
+ compatibleTypes(getNodeType(arg), read.getContainerType()) and
+ compatibleTypes(read.getContentType(), getNodeType(out))
)
}
+ /**
+ * Holds if `arg` flows to `out` through a call using only
+ * value-preserving steps and a single read step, not taking call
+ * contexts into account, thus representing a getter-step.
+ */
+ predicate getterStep(ArgumentNode arg, Content c, Node out) {
+ argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
+ }
+
/**
* Holds if `p` can flow to a return node of kind `kind` in the same
- * callable using only value-preserving steps.
+ * callable using only value-preserving steps and possibly a single read
+ * step.
*
- * `contentIn` describes the content of `p` that can flow to the return
- * node (if any).
+ * If a read step was taken, then `read` captures the `Content`, the
+ * container type, and the content type.
*/
private predicate parameterValueFlowReturn(
- ParameterNode p, ReturnKind kind, ContentOption contentIn
+ ParameterNode p, ReturnKind kind, ReadStepTypesOption read
) {
exists(ReturnNode ret |
- parameterValueFlow(p, ret, contentIn) and
+ parameterValueFlow(p, ret, read) and
kind = ret.getKind()
)
}
@@ -323,37 +289,107 @@ private module Cached {
import Final
}
+ import FlowThrough
+
+ cached
+ private module DispatchWithCallContext {
+ /**
+ * Holds if the call context `ctx` reduces the set of viable run-time
+ * dispatch targets of call `call` in `c`.
+ */
+ cached
+ predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) {
+ exists(int tgts, int ctxtgts |
+ mayBenefitFromCallContext(call, c) and
+ c = viableCallable(ctx) and
+ ctxtgts = count(viableImplInCallContext(call, ctx)) and
+ tgts = strictcount(viableCallable(call)) and
+ ctxtgts < tgts
+ )
+ }
+
+ /**
+ * Gets a viable run-time dispatch target for the call `call` in the
+ * context `ctx`. This is restricted to those calls for which a context
+ * makes a difference.
+ */
+ cached
+ DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
+ result = viableImplInCallContext(call, ctx) and
+ reducedViableImplInCallContext(call, _, ctx)
+ }
+
+ /**
+ * Holds if flow returning from callable `c` to call `call` might return
+ * further and if this path restricts the set of call sites that can be
+ * returned to.
+ */
+ cached
+ predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) {
+ exists(int tgts, int ctxtgts |
+ mayBenefitFromCallContext(call, _) and
+ c = viableCallable(call) and
+ ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContext(call, ctx)) and
+ tgts = strictcount(DataFlowCall ctx | viableCallable(ctx) = call.getEnclosingCallable()) and
+ ctxtgts < tgts
+ )
+ }
+
+ /**
+ * Gets a viable run-time dispatch target for the call `call` in the
+ * context `ctx`. This is restricted to those calls and results for which
+ * the return flow from the result to `call` restricts the possible context
+ * `ctx`.
+ */
+ cached
+ DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) {
+ result = viableImplInCallContext(call, ctx) and
+ reducedViableImplInReturn(result, call)
+ }
+ }
+
+ import DispatchWithCallContext
+
/**
* Holds if `p` can flow to the pre-update node associated with post-update
* node `n`, in the same callable, using only value-preserving steps.
*/
cached
predicate parameterValueFlowsToPreUpdate(ParameterNode p, PostUpdateNode n) {
- parameterValueFlow(p, n.getPreUpdateNode(), TContentNone())
+ parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
}
- /**
- * Holds if data can flow from `node1` to `node2` via a direct assignment to
- * `f`.
- *
- * This includes reverse steps through reads when the result of the read has
- * been stored into, in order to handle cases like `x.f1.f2 = y`.
- */
- cached
- predicate store(Node node1, Content f, Node node2) {
- storeStep(node1, f, node2) and readStep(_, f, _)
+ private predicate store(
+ Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
+ ) {
+ storeStep(node1, c, node2) and
+ readStep(_, c, _) and
+ contentType = getNodeType(node1) and
+ containerType = getNodeType(node2)
or
exists(Node n1, Node n2 |
n1 = node1.(PostUpdateNode).getPreUpdateNode() and
n2 = node2.(PostUpdateNode).getPreUpdateNode()
|
- argumentValueFlowsThrough(n2, TContentSome(f), n1)
+ argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
or
- readStep(n2, f, n1)
+ readStep(n2, c, n1) and
+ contentType = getNodeType(n1) and
+ containerType = getNodeType(n2)
)
}
- import FlowThrough
+ /**
+ * Holds if data can flow from `node1` to `node2` via a direct assignment to
+ * `f`.
+ *
+ * This includes reverse steps through reads when the result of the read has
+ * been stored into, in order to handle cases like `x.f1.f2 = y`.
+ */
+ cached
+ predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) {
+ store(node1, tc.getContent(), node2, contentType, tc.getContainerType())
+ }
/**
* Holds if the call context `call` either improves virtual dispatch in
@@ -397,10 +433,13 @@ private module Cached {
TBooleanNone() or
TBooleanSome(boolean b) { b = true or b = false }
+ cached
+ newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
+
cached
newtype TAccessPathFront =
TFrontNil(DataFlowType t) or
- TFrontHead(Content f)
+ TFrontHead(TypedContent tc)
cached
newtype TAccessPathFrontOption =
@@ -415,25 +454,38 @@ class CastingNode extends Node {
CastingNode() {
this instanceof ParameterNode or
this instanceof CastNode or
- this instanceof OutNodeExt
+ this instanceof OutNodeExt or
+ // For reads, `x.f`, we want to check that the tracked type after the read (which
+ // is obtained by popping the head of the access path stack) is compatible with
+ // the type of `x.f`.
+ readStep(_, _, this)
}
}
-newtype TContentOption =
- TContentNone() or
- TContentSome(Content f)
+private predicate readStepWithTypes(
+ Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
+) {
+ readStep(n1, c, n2) and
+ container = getNodeType(n1) and
+ content = getNodeType(n2)
+}
-private class ContentOption extends TContentOption {
- Content getContent() { this = TContentSome(result) }
+private newtype TReadStepTypesOption =
+ TReadStepTypesNone() or
+ TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) {
+ readStepWithTypes(_, container, c, _, content)
+ }
- predicate hasContent() { exists(this.getContent()) }
+private class ReadStepTypesOption extends TReadStepTypesOption {
+ predicate isSome() { this instanceof TReadStepTypesSome }
- string toString() {
- result = this.getContent().toString()
- or
- not this.hasContent() and
- result = ""
- }
+ DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) }
+
+ Content getContent() { this = TReadStepTypesSome(_, result, _) }
+
+ DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) }
+
+ string toString() { if this.isSome() then result = "Some(..)" else result = "None()" }
}
/**
@@ -460,13 +512,19 @@ abstract class CallContext extends TCallContext {
abstract predicate relevantFor(DataFlowCallable callable);
}
-class CallContextAny extends CallContext, TAnyCallContext {
+abstract class CallContextNoCall extends CallContext { }
+
+class CallContextAny extends CallContextNoCall, TAnyCallContext {
override string toString() { result = "CcAny" }
override predicate relevantFor(DataFlowCallable callable) { any() }
}
-abstract class CallContextCall extends CallContext { }
+abstract class CallContextCall extends CallContext {
+ /** Holds if this call context may be `call`. */
+ bindingset[call]
+ abstract predicate matchesCall(DataFlowCall call);
+}
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
override string toString() {
@@ -477,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
recordDataFlowCallSite(getCall(), callable)
}
+ override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
+
DataFlowCall getCall() { this = TSpecificCall(result) }
}
@@ -486,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
override predicate relevantFor(DataFlowCallable callable) {
exists(ParameterNode p | p.getEnclosingCallable() = callable)
}
+
+ override predicate matchesCall(DataFlowCall call) { any() }
}
-class CallContextReturn extends CallContext, TReturn {
+class CallContextReturn extends CallContextNoCall, TReturn {
override string toString() {
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
}
@@ -678,9 +740,6 @@ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
result = viableCallable(call) and cc instanceof CallContextReturn
}
-pragma[noinline]
-DataFlowType getErasedNodeTypeBound(Node n) { result = getErasedRepr(n.getTypeBound()) }
-
predicate read = readStep/3;
/** An optional Boolean value. */
@@ -692,6 +751,23 @@ class BooleanOption extends TBooleanOption {
}
}
+/** Content tagged with the type of a containing object. */
+class TypedContent extends MkTypedContent {
+ private Content c;
+ private DataFlowType t;
+
+ TypedContent() { this = MkTypedContent(c, t) }
+
+ /** Gets the content. */
+ Content getContent() { result = c }
+
+ /** Gets the container type. */
+ DataFlowType getContainerType() { result = t }
+
+ /** Gets a textual representation of this content. */
+ string toString() { result = c.toString() }
+}
+
/**
* The front of an access path. This is either a head or a nil.
*/
@@ -702,25 +778,36 @@ abstract class AccessPathFront extends TAccessPathFront {
abstract boolean toBoolNonEmpty();
- predicate headUsesContent(Content f) { this = TFrontHead(f) }
+ predicate headUsesContent(TypedContent tc) { this = TFrontHead(tc) }
+
+ predicate isClearedAt(Node n) {
+ exists(TypedContent tc |
+ this.headUsesContent(tc) and
+ clearsContent(n, tc.getContent())
+ )
+ }
}
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
- override string toString() {
- exists(DataFlowType t | this = TFrontNil(t) | result = ppReprType(t))
- }
+ private DataFlowType t;
+
+ AccessPathFrontNil() { this = TFrontNil(t) }
+
+ override string toString() { result = ppReprType(t) }
- override DataFlowType getType() { this = TFrontNil(result) }
+ override DataFlowType getType() { result = t }
override boolean toBoolNonEmpty() { result = false }
}
class AccessPathFrontHead extends AccessPathFront, TFrontHead {
- override string toString() { exists(Content f | this = TFrontHead(f) | result = f.toString()) }
+ private TypedContent tc;
- override DataFlowType getType() {
- exists(Content head | this = TFrontHead(head) | result = head.getContainerType())
- }
+ AccessPathFrontHead() { this = TFrontHead(tc) }
+
+ override string toString() { result = tc.toString() }
+
+ override DataFlowType getType() { result = tc.getContainerType() }
override boolean toBoolNonEmpty() { result = true }
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
index 0dc3b8eff450..4e1cd281488f 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
@@ -37,21 +37,12 @@ module Consistency {
)
}
- query predicate uniqueTypeBound(Node n, string msg) {
+ query predicate uniqueType(Node n, string msg) {
exists(int c |
n instanceof RelevantNode and
- c = count(n.getTypeBound()) and
+ c = count(getNodeType(n)) and
c != 1 and
- msg = "Node should have one type bound but has " + c + "."
- )
- }
-
- query predicate uniqueTypeRepr(Node n, string msg) {
- exists(int c |
- n instanceof RelevantNode and
- c = count(getErasedRepr(n.getTypeBound())) and
- c != 1 and
- msg = "Node should have one type representation but has " + c + "."
+ msg = "Node should have one type but has " + c + "."
)
}
@@ -104,7 +95,7 @@ module Consistency {
msg = "Local flow step does not preserve enclosing callable."
}
- private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) }
+ private DataFlowType typeRepr() { result = getNodeType(_) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
t = typeRepr() and
@@ -132,8 +123,18 @@ module Consistency {
n.getEnclosingCallable() != call.getEnclosingCallable()
}
+ // This predicate helps the compiler forget that in some languages
+ // it is impossible for a result of `getPreUpdateNode` to be an
+ // instance of `PostUpdateNode`.
+ private Node getPre(PostUpdateNode n) {
+ result = n.getPreUpdateNode()
+ or
+ none()
+ }
+
query predicate postIsNotPre(PostUpdateNode n, string msg) {
- n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
+ getPre(n) = n and
+ msg = "PostUpdateNode should not equal its pre-update node."
}
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
@@ -161,15 +162,14 @@ module Consistency {
msg = "Origin of readStep is missing a PostUpdateNode."
}
- query predicate storeIsPostUpdate(Node n, string msg) {
- storeStep(_, _, n) and
- not n instanceof PostUpdateNode and
- msg = "Store targets should be PostUpdateNodes."
- }
-
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not isImmutableOrUnobservable(n) and
msg = "ArgumentNode is missing PostUpdateNode."
}
+
+ query predicate postWithInFlow(PostUpdateNode n, string msg) {
+ simpleLocalFlowStep(_, n) and
+ msg = "PostUpdateNode should not be the target of local flow."
+ }
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index 97118da117c8..e780c5c7eb36 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -5,35 +5,58 @@ private import DataFlowDispatch
/**
* A data flow node that occurs as the argument of a call and is passed as-is
- * to the callable. Instance arguments (`this` pointer) are also included.
+ * to the callable. Instance arguments (`this` pointer) and read side effects
+ * on parameters are also included.
*/
-class ArgumentNode extends InstructionNode {
- ArgumentNode() {
- exists(CallInstruction call |
- instr = call.getAnArgument()
- or
- instr.(ReadSideEffectInstruction).getPrimaryInstruction() = call
- )
- }
-
+abstract class ArgumentNode extends OperandNode {
/**
* Holds if this argument occurs at the given position in the given call.
* The instance argument is considered to have index `-1`.
*/
- predicate argumentOf(DataFlowCall call, int pos) {
- instr = call.getPositionalArgument(pos)
+ abstract predicate argumentOf(DataFlowCall call, int pos);
+
+ /** Gets the call in which this node is an argument. */
+ DataFlowCall getCall() { this.argumentOf(result, _) }
+}
+
+/**
+ * A data flow node that occurs as the argument to a call, or an
+ * implicit `this` pointer argument.
+ */
+private class PrimaryArgumentNode extends ArgumentNode {
+ override ArgumentOperand op;
+
+ PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) }
+
+ override predicate argumentOf(DataFlowCall call, int pos) {
+ op = call.getPositionalArgumentOperand(pos)
or
- instr = call.getThisArgument() and pos = -1
+ op = call.getThisArgumentOperand() and pos = -1
+ }
+
+ override string toString() {
+ result = "Argument " + op.(PositionalArgumentOperand).getIndex()
or
- exists(ReadSideEffectInstruction read |
- read = instr and
- read.getPrimaryInstruction() = call and
- pos = getArgumentPosOfSideEffect(read.getIndex())
- )
+ op instanceof ThisArgumentOperand and result = "This argument"
}
+}
- /** Gets the call in which this node is an argument. */
- DataFlowCall getCall() { this.argumentOf(result, _) }
+/**
+ * A data flow node representing the read side effect of a call on a
+ * specific parameter.
+ */
+private class SideEffectArgumentNode extends ArgumentNode {
+ override SideEffectOperand op;
+ ReadSideEffectInstruction read;
+
+ SideEffectArgumentNode() { op = read.getSideEffectOperand() }
+
+ override predicate argumentOf(DataFlowCall call, int pos) {
+ read.getPrimaryInstruction() = call and
+ pos = getArgumentPosOfSideEffect(read.getIndex())
+ }
+
+ override string toString() { result = "Argument " + read.getIndex() + " indirection" }
}
private newtype TReturnKind =
@@ -86,7 +109,12 @@ class ReturnValueNode extends ReturnNode {
class ReturnIndirectionNode extends ReturnNode {
override ReturnIndirectionInstruction primary;
- override ReturnKind getKind() { result = TIndirectReturnKind(primary.getParameter().getIndex()) }
+ override ReturnKind getKind() {
+ result = TIndirectReturnKind(-1) and
+ primary.isThisIndirection()
+ or
+ result = TIndirectReturnKind(primary.getParameter().getIndex())
+ }
}
/** A data flow node that represents the output of a call. */
@@ -123,8 +151,13 @@ private class SideEffectOutNode extends OutNode {
* `kind`.
*/
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
- result.getCall() = call and
- result.getReturnKind() = kind
+ // There should be only one `OutNode` for a given `(call, kind)` pair. Showing the optimizer that
+ // this is true helps it make better decisions downstream, especially in virtual dispatch.
+ result =
+ unique(OutNode outNode |
+ outNode.getCall() = call and
+ outNode.getReturnKind() = kind
+ )
}
/**
@@ -134,8 +167,23 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
*/
predicate jumpStep(Node n1, Node n2) { none() }
+/**
+ * Gets a field corresponding to the bit range `[startBit..endBit)` of class `c`, if any.
+ */
+private Field getAField(Class c, int startBit, int endBit) {
+ result.getDeclaringType() = c and
+ startBit = 8 * result.getByteOffset() and
+ endBit = 8 * result.getType().getSize() + startBit
+ or
+ exists(Field f, Class cInner |
+ f = c.getAField() and
+ cInner = f.getUnderlyingType() and
+ result = getAField(cInner, startBit - 8 * f.getByteOffset(), endBit - 8 * f.getByteOffset())
+ )
+}
+
private newtype TContent =
- TFieldContent(Field f) or
+ TFieldContent(Class c, int startBit, int endBit) { exists(getAField(c, startBit, endBit)) } or
TCollectionContent() or
TArrayContent()
@@ -150,63 +198,92 @@ class Content extends TContent {
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
}
-
- /** Gets the type of the object containing this content. */
- abstract Type getContainerType();
-
- /** Gets the type of this content. */
- abstract Type getType();
}
private class FieldContent extends Content, TFieldContent {
- Field f;
-
- FieldContent() { this = TFieldContent(f) }
+ Class c;
+ int startBit;
+ int endBit;
- Field getField() { result = f }
+ FieldContent() { this = TFieldContent(c, startBit, endBit) }
- override string toString() { result = f.toString() }
+ // Ensure that there's just 1 result for `toString`.
+ override string toString() { result = min(Field f | f = getAField() | f.toString()) }
- override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
- f.getLocation().hasLocationInfo(path, sl, sc, el, ec)
- }
-
- override Type getContainerType() { result = f.getDeclaringType() }
+ predicate hasOffset(Class cl, int start, int end) { cl = c and start = startBit and end = endBit }
- override Type getType() { result = f.getType() }
+ Field getAField() { result = getAField(c, startBit, endBit) }
}
private class CollectionContent extends Content, TCollectionContent {
override string toString() { result = "collection" }
-
- override Type getContainerType() { none() }
-
- override Type getType() { none() }
}
private class ArrayContent extends Content, TArrayContent {
- override string toString() { result = "array" }
-
- override Type getContainerType() { none() }
+ ArrayContent() { this = TArrayContent() }
- override Type getType() { none() }
+ override string toString() { result = "array content" }
}
-private predicate storeStepNoChi(Node node1, Content f, PostUpdateNode node2) {
- exists(FieldAddressInstruction fa, StoreInstruction store |
+private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode node2) {
+ exists(StoreInstruction store, Class c |
store = node2.asInstruction() and
- store.getDestinationAddress() = fa and
store.getSourceValue() = node1.asInstruction() and
- f.(FieldContent).getField() = fa.getField()
+ getWrittenField(store, f.(FieldContent).getAField(), c) and
+ f.hasOffset(c, _, _)
+ )
+}
+
+private FieldAddressInstruction getFieldInstruction(Instruction instr) {
+ result = instr or
+ result = instr.(CopyValueInstruction).getUnary()
+}
+
+pragma[noinline]
+private predicate getWrittenField(Instruction instr, Field f, Class c) {
+ exists(FieldAddressInstruction fa |
+ fa =
+ getFieldInstruction([instr.(StoreInstruction).getDestinationAddress(),
+ instr.(WriteSideEffectInstruction).getDestinationAddress()]) and
+ f = fa.getField() and
+ c = f.getDeclaringType()
+ )
+}
+
+private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode node2) {
+ exists(StoreInstruction store, ChiInstruction chi |
+ node1.asInstruction() = store and
+ node2.asInstruction() = chi and
+ chi.getPartial() = store and
+ exists(Class c |
+ c = chi.getResultType() and
+ exists(int startBit, int endBit |
+ chi.getUpdatedInterval(startBit, endBit) and
+ f.hasOffset(c, startBit, endBit)
+ )
+ or
+ getWrittenField(store, f.getAField(), c) and
+ f.hasOffset(c, _, _)
+ )
)
}
-private predicate storeStepChi(Node node1, Content f, PostUpdateNode node2) {
- exists(FieldAddressInstruction fa, StoreInstruction store |
+private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode node2) {
+ a = TArrayContent() and
+ exists(StoreInstruction store |
node1.asInstruction() = store and
- store.getDestinationAddress() = fa and
- node2.asInstruction().(ChiInstruction).getPartial() = store and
- f.(FieldContent).getField() = fa.getField()
+ (
+ // `x[i] = taint()`
+ // This matches the characteristic predicate in `ArrayStoreNode`.
+ store.getDestinationAddress() instanceof PointerAddInstruction
+ or
+ // `*p = taint()`
+ // This matches the characteristic predicate in `PointerStoreNode`.
+ store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction
+ ) and
+ // This `ChiInstruction` will always have a non-conflated result because both `ArrayStoreNode`
+ // and `PointerStoreNode` require it in their characteristic predicates.
+ node2.asInstruction().(ChiInstruction).getPartial() = store
)
}
@@ -216,8 +293,37 @@ private predicate storeStepChi(Node node1, Content f, PostUpdateNode node2) {
* value of `node1`.
*/
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
- storeStepNoChi(node1, f, node2) or
- storeStepChi(node1, f, node2)
+ fieldStoreStepNoChi(node1, f, node2) or
+ fieldStoreStepChi(node1, f, node2) or
+ arrayStoreStepChi(node1, f, node2) or
+ fieldStoreStepAfterArraySuppression(node1, f, node2)
+}
+
+// This predicate pushes the correct `FieldContent` onto the access path when the
+// `suppressArrayRead` predicate has popped off an `ArrayContent`.
+private predicate fieldStoreStepAfterArraySuppression(
+ Node node1, FieldContent f, PostUpdateNode node2
+) {
+ exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi, Class c |
+ not chi.isResultConflated() and
+ node1.asInstruction() = chi and
+ node2.asInstruction() = chi and
+ chi.getPartial() = write and
+ getWrittenField(write, f.getAField(), c) and
+ f.hasOffset(c, _, _)
+ )
+}
+
+bindingset[result, i]
+private int unbindInt(int i) { i <= result and i >= result }
+
+pragma[noinline]
+private predicate getLoadedField(LoadInstruction load, Field f, Class c) {
+ exists(FieldAddressInstruction fa |
+ fa = load.getSourceAddress() and
+ f = fa.getField() and
+ c = f.getDeclaringType()
+ )
}
/**
@@ -225,38 +331,148 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
* Thus, `node1` references an object with a field `f` whose value ends up in
* `node2`.
*/
-predicate readStep(Node node1, Content f, Node node2) {
- exists(FieldAddressInstruction fa, LoadInstruction load |
- load.getSourceAddress() = fa and
+private predicate fieldReadStep(Node node1, FieldContent f, Node node2) {
+ exists(LoadInstruction load |
+ node2.asInstruction() = load and
+ node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
+ exists(Class c |
+ c = load.getSourceValueOperand().getAnyDef().getResultType() and
+ exists(int startBit, int endBit |
+ load.getSourceValueOperand().getUsedInterval(unbindInt(startBit), unbindInt(endBit)) and
+ f.hasOffset(c, startBit, endBit)
+ )
+ or
+ getLoadedField(load, f.getAField(), c) and
+ f.hasOffset(c, _, _)
+ )
+ )
+}
+
+/**
+ * When a store step happens in a function that looks like an array write such as:
+ * ```cpp
+ * void f(int* pa) {
+ * pa = source();
+ * }
+ * ```
+ * it can be a write to an array, but it can also happen that `f` is called as `f(&a.x)`. If that is
+ * the case, the `ArrayContent` that was written by the call to `f` should be popped off the access
+ * path, and a `FieldContent` containing `x` should be pushed instead.
+ * So this case pops `ArrayContent` off the access path, and the `fieldStoreStepAfterArraySuppression`
+ * predicate in `storeStep` ensures that we push the right `FieldContent` onto the access path.
+ */
+predicate suppressArrayRead(Node node1, ArrayContent a, Node node2) {
+ a = TArrayContent() and
+ exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi |
+ node1.asInstruction() = write and
+ node2.asInstruction() = chi and
+ chi.getPartial() = write and
+ getWrittenField(write, _, _)
+ )
+}
+
+private class ArrayToPointerConvertInstruction extends ConvertInstruction {
+ ArrayToPointerConvertInstruction() {
+ this.getUnary().getResultType() instanceof ArrayType and
+ this.getResultType() instanceof PointerType
+ }
+}
+
+private Instruction skipOneCopyValueInstructionRec(CopyValueInstruction copy) {
+ copy.getUnary() = result and not result instanceof CopyValueInstruction
+ or
+ result = skipOneCopyValueInstructionRec(copy.getUnary())
+}
+
+private Instruction skipCopyValueInstructions(Instruction instr) {
+ not result instanceof CopyValueInstruction and result = instr
+ or
+ result = skipOneCopyValueInstructionRec(instr)
+}
+
+private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
+ a = TArrayContent() and
+ // Explicit dereferences such as `*p` or `p[i]` where `p` is a pointer or array.
+ exists(LoadInstruction load, Instruction address |
+ load.getSourceValueOperand().isDefinitionInexact() and
node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
- fa.getField() = f.(FieldContent).getField() and
- load = node2.asInstruction()
+ load = node2.asInstruction() and
+ address = skipCopyValueInstructions(load.getSourceAddress()) and
+ (
+ address instanceof LoadInstruction or
+ address instanceof ArrayToPointerConvertInstruction or
+ address instanceof PointerOffsetInstruction
+ )
+ )
+}
+
+/**
+ * In cases such as:
+ * ```cpp
+ * void f(int* pa) {
+ * *pa = source();
+ * }
+ * ...
+ * int x;
+ * f(&x);
+ * use(x);
+ * ```
+ * the load on `x` in `use(x)` will exactly overlap with its definition (in this case the definition
+ * is a `BufferMayWriteSideEffect`). This predicate pops the `ArrayContent` (pushed by the store in `f`)
+ * from the access path.
+ */
+private predicate exactReadStep(Node node1, ArrayContent a, Node node2) {
+ a = TArrayContent() and
+ exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi |
+ not chi.isResultConflated() and
+ chi.getPartial() = write and
+ node1.asInstruction() = write and
+ node2.asInstruction() = chi and
+ // To distinquish this case from the `arrayReadStep` case we require that the entire variable was
+ // overwritten by the `BufferMayWriteSideEffectInstruction` (i.e., there is a load that reads the
+ // entire variable).
+ exists(LoadInstruction load | load.getSourceValue() = chi)
)
}
/**
- * Gets a representative (boxed) type for `t` for the purpose of pruning
- * possible flow. A single type is used for all numeric types to account for
- * numeric conversions, and otherwise the erasure is used.
+ * Holds if data can flow from `node1` to `node2` via a read of `f`.
+ * Thus, `node1` references an object with a field `f` whose value ends up in
+ * `node2`.
+ */
+predicate readStep(Node node1, Content f, Node node2) {
+ fieldReadStep(node1, f, node2) or
+ arrayReadStep(node1, f, node2) or
+ exactReadStep(node1, f, node2) or
+ suppressArrayRead(node1, f, node2)
+}
+
+/**
+ * Holds if values stored inside content `c` are cleared at node `n`.
*/
-Type getErasedRepr(Type t) {
- suppressUnusedType(t) and
- result instanceof VoidType // stub implementation
+predicate clearsContent(Node n, Content c) {
+ none() // stub implementation
}
-/** Gets a string representation of a type returned by `getErasedRepr`. */
-string ppReprType(Type t) { none() } // stub implementation
+/** Gets the type of `n` used for type pruning. */
+IRType getNodeType(Node n) {
+ suppressUnusedNode(n) and
+ result instanceof IRVoidType // stub implementation
+}
+
+/** Gets a string representation of a type returned by `getNodeType`. */
+string ppReprType(IRType t) { none() } // stub implementation
/**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`.
*/
pragma[inline]
-predicate compatibleTypes(Type t1, Type t2) {
+predicate compatibleTypes(IRType t1, IRType t2) {
any() // stub implementation
}
-private predicate suppressUnusedType(Type t) { any() }
+private predicate suppressUnusedNode(Node n) { any() }
//////////////////////////////////////////////////////////////////////////////
// Java QL library compatibility wrappers
@@ -276,7 +492,7 @@ class DataFlowCallable = Declaration;
class DataFlowExpr = Expr;
-class DataFlowType = Type;
+class DataFlowType = IRType;
/** A function call relevant for data flow. */
class DataFlowCall extends CallInstruction {
@@ -306,3 +522,6 @@ predicate isImmutableOrUnobservable(Node n) {
// complex to model here.
any()
}
+
+/** Holds if `n` should be hidden from path explanations. */
+predicate nodeIsHidden(Node n) { n instanceof OperandNode }
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 beb3c8d954d1..1270b8d67da5 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
@@ -11,8 +11,10 @@ private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.DataFlow
+cached
private newtype TIRDataFlowNode =
TInstructionNode(Instruction i) or
+ TOperandNode(Operand op) or
TVariableNode(Variable var)
/**
@@ -32,15 +34,23 @@ class Node extends TIRDataFlowNode {
Function getFunction() { none() } // overridden in subclasses
/** Gets the type of this node. */
- Type getType() { none() } // overridden in subclasses
+ IRType getType() { none() } // overridden in subclasses
/** Gets the instruction corresponding to this node, if any. */
Instruction asInstruction() { result = this.(InstructionNode).getInstruction() }
+ /** Gets the operands corresponding to this node, if any. */
+ Operand asOperand() { result = this.(OperandNode).getOperand() }
+
/**
- * Gets the non-conversion expression corresponding to this node, if any. If
- * this node strictly (in the sense of `asConvertedExpr`) corresponds to a
- * `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
+ * Gets the non-conversion expression corresponding to this node, if any.
+ * This predicate only has a result on nodes that represent the value of
+ * evaluating the expression. For data flowing _out of_ an expression, like
+ * when an argument is passed by reference, use `asDefiningArgument` instead
+ * of `asExpr`.
+ *
+ * If this node strictly (in the sense of `asConvertedExpr`) corresponds to
+ * a `Conversion`, then the result is the underlying non-`Conversion` base
* expression.
*/
Expr asExpr() { result = this.(ExprNode).getExpr() }
@@ -51,7 +61,13 @@ class Node extends TIRDataFlowNode {
*/
Expr asConvertedExpr() { result = this.(ExprNode).getConvertedExpr() }
- /** Gets the argument that defines this `DefinitionByReferenceNode`, if any. */
+ /**
+ * Gets the argument that defines this `DefinitionByReferenceNode`, if any.
+ * This predicate should be used instead of `asExpr` when referring to the
+ * value of a reference argument _after_ the call has returned. For example,
+ * in `f(&x)`, this predicate will have `&x` as its result for the `Node`
+ * that represents the new value of `x`.
+ */
Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() }
/** Gets the positional parameter corresponding to this node, if any. */
@@ -59,7 +75,7 @@ class Node extends TIRDataFlowNode {
/**
* Gets the variable corresponding to this node, if any. This can be used for
- * modelling flow in and out of global variables.
+ * modeling flow in and out of global variables.
*/
Variable asVariable() { result = this.(VariableNode).getVariable() }
@@ -84,7 +100,7 @@ class Node extends TIRDataFlowNode {
/**
* Gets an upper bound on the type of this node.
*/
- Type getTypeBound() { result = getType() }
+ IRType getTypeBound() { result = getType() }
/** Gets the location of this element. */
Location getLocation() { none() } // overridden by subclasses
@@ -121,7 +137,7 @@ class InstructionNode extends Node, TInstructionNode {
override Function getFunction() { result = instr.getEnclosingFunction() }
- override Type getType() { result = instr.getResultType() }
+ override IRType getType() { result = instr.getResultIRType() }
override Location getLocation() { result = instr.getLocation() }
@@ -132,6 +148,28 @@ class InstructionNode extends Node, TInstructionNode {
}
}
+/**
+ * An operand, viewed as a node in a data flow graph.
+ */
+class OperandNode extends Node, TOperandNode {
+ Operand op;
+
+ OperandNode() { this = TOperandNode(op) }
+
+ /** Gets the operand corresponding to this node. */
+ Operand getOperand() { result = op }
+
+ override Declaration getEnclosingCallable() { result = this.getFunction() }
+
+ override Function getFunction() { result = op.getUse().getEnclosingFunction() }
+
+ override IRType getType() { result = op.getIRType() }
+
+ override Location getLocation() { result = op.getLocation() }
+
+ override string toString() { result = this.getOperand().toString() }
+}
+
/**
* An expression, viewed as a node in a data flow graph.
*/
@@ -291,29 +329,36 @@ abstract class PostUpdateNode extends InstructionNode {
* setY(&x); // a partial definition of the object `x`.
* ```
*/
-abstract private class PartialDefinitionNode extends PostUpdateNode, TInstructionNode {
+abstract private class PartialDefinitionNode extends PostUpdateNode {
abstract Expr getDefinedExpr();
}
private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
override ChiInstruction instr;
- FieldAddressInstruction field;
+ StoreInstruction store;
ExplicitFieldStoreQualifierNode() {
not instr.isResultConflated() and
- exists(StoreInstruction store |
- instr.getPartial() = store and field = store.getDestinationAddress()
+ instr.getPartial() = store and
+ (
+ instr.getUpdatedInterval(_, _) or
+ store.getDestinationAddress() instanceof FieldAddressInstruction
)
}
- // There might be multiple `ChiInstructions` that has a particular instruction as
- // the total operand - so this definition gives consistency errors in
- // DataFlowImplConsistency::Consistency. However, it's not clear what (if any) implications
- // this consistency failure has.
- override Node getPreUpdateNode() { result.asInstruction() = instr.getTotal() }
+ // By using an operand as the result of this predicate we avoid the dataflow inconsistency errors
+ // caused by having multiple nodes sharing the same pre update node. This inconsistency error can cause
+ // a tuple explosion in the big step dataflow relation since it can make many nodes be the entry node
+ // into a big step.
+ override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
override Expr getDefinedExpr() {
- result = field.getObjectAddress().getUnconvertedResultExpression()
+ result =
+ store
+ .getDestinationAddress()
+ .(FieldAddressInstruction)
+ .getObjectAddress()
+ .getUnconvertedResultExpression()
}
}
@@ -325,20 +370,93 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
*/
private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode {
override StoreInstruction instr;
- FieldAddressInstruction field;
ExplicitSingleFieldStoreQualifierNode() {
- field = instr.getDestinationAddress() and
- not exists(ChiInstruction chi | chi.getPartial() = instr)
+ not exists(ChiInstruction chi | chi.getPartial() = instr) and
+ // Without this condition any store would create a `PostUpdateNode`.
+ instr.getDestinationAddress() instanceof FieldAddressInstruction
}
override Node getPreUpdateNode() { none() }
+ override Expr getDefinedExpr() {
+ result =
+ instr
+ .getDestinationAddress()
+ .(FieldAddressInstruction)
+ .getObjectAddress()
+ .getUnconvertedResultExpression()
+ }
+}
+
+private FieldAddressInstruction getFieldInstruction(Instruction instr) {
+ result = instr or
+ result = instr.(CopyValueInstruction).getUnary()
+}
+
+/**
+ * The target of a `fieldStoreStepAfterArraySuppression` store step, which is used to convert
+ * an `ArrayContent` to a `FieldContent` when the `BufferMayWriteSideEffect` instruction stores
+ * into a field. See the QLDoc for `suppressArrayRead` for an example of where such a conversion
+ * is inserted.
+ */
+private class BufferMayWriteSideEffectFieldStoreQualifierNode extends PartialDefinitionNode {
+ override ChiInstruction instr;
+ BufferMayWriteSideEffectInstruction write;
+ FieldAddressInstruction field;
+
+ BufferMayWriteSideEffectFieldStoreQualifierNode() {
+ not instr.isResultConflated() and
+ instr.getPartial() = write and
+ field = getFieldInstruction(write.getDestinationAddress())
+ }
+
+ override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
+
override Expr getDefinedExpr() {
result = field.getObjectAddress().getUnconvertedResultExpression()
}
}
+/**
+ * The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden
+ * `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`.
+ */
+private class ArrayStoreNode extends PartialDefinitionNode {
+ override ChiInstruction instr;
+ PointerAddInstruction add;
+
+ ArrayStoreNode() {
+ not instr.isResultConflated() and
+ exists(StoreInstruction store |
+ instr.getPartial() = store and
+ add = store.getDestinationAddress()
+ )
+ }
+
+ override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
+
+ override Expr getDefinedExpr() { result = add.getLeft().getUnconvertedResultExpression() }
+}
+
+/**
+ * The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden
+ * `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`.
+ */
+private class PointerStoreNode extends PostUpdateNode {
+ override ChiInstruction instr;
+
+ PointerStoreNode() {
+ not instr.isResultConflated() and
+ exists(StoreInstruction store |
+ instr.getPartial() = store and
+ store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction
+ )
+ }
+
+ override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
+}
+
/**
* A node that represents the value of a variable after a function call that
* may have changed the variable because it's passed by reference.
@@ -352,7 +470,7 @@ private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNod
class DefinitionByReferenceNode extends InstructionNode {
override WriteSideEffectInstruction instr;
- /** Gets the argument corresponding to this node. */
+ /** Gets the unconverted argument corresponding to this node. */
Expr getArgument() {
result =
instr
@@ -387,22 +505,10 @@ class DefinitionByReferenceNode extends InstructionNode {
}
}
-/**
- * A node representing the memory pointed to by a function argument.
- *
- * This class exists only in order to override `toString`, which would
- * otherwise be the default implementation inherited from `InstructionNode`.
- */
-private class ArgumentIndirectionNode extends InstructionNode {
- override ReadSideEffectInstruction instr;
-
- override string toString() { result = "Argument " + instr.getIndex() + " indirection" }
-}
-
/**
* A `Node` corresponding to a variable in the program, as opposed to the
* value of that variable at some particular point. This can be used for
- * modelling flow in and out of global variables.
+ * modeling flow in and out of global variables.
*/
class VariableNode extends Node, TVariableNode {
Variable v;
@@ -423,7 +529,7 @@ class VariableNode extends Node, TVariableNode {
result = v
}
- override Type getType() { result = v.getType() }
+ override IRType getType() { result.getCanonicalLanguageType().hasUnspecifiedType(v.getType(), _) }
override Location getLocation() { result = v.getLocation() }
@@ -436,20 +542,26 @@ class VariableNode extends Node, TVariableNode {
InstructionNode instructionNode(Instruction instr) { result.getInstruction() = instr }
/**
+ * DEPRECATED: use `definitionByReferenceNodeFromArgument` instead.
+ *
* Gets the `Node` corresponding to a definition by reference of the variable
* that is passed as `argument` of a call.
*/
-DefinitionByReferenceNode definitionByReferenceNode(Expr e) { result.getArgument() = e }
+deprecated DefinitionByReferenceNode definitionByReferenceNode(Expr e) { result.getArgument() = e }
/**
- * Gets a `Node` corresponding to `e` or any of its conversions. There is no
- * result if `e` is a `Conversion`.
+ * Gets the `Node` corresponding to the value of evaluating `e` or any of its
+ * conversions. There is no result if `e` is a `Conversion`. For data flowing
+ * _out of_ an expression, like when an argument is passed by reference, use
+ * `definitionByReferenceNodeFromArgument` instead.
*/
ExprNode exprNode(Expr e) { result.getExpr() = e }
/**
- * Gets the `Node` corresponding to `e`, if any. Here, `e` may be a
- * `Conversion`.
+ * Gets the `Node` corresponding to the value of evaluating `e`. Here, `e` may
+ * be a `Conversion`. For data flowing _out of_ an expression, like when an
+ * argument is passed by reference, use
+ * `definitionByReferenceNodeFromArgument` instead.
*/
ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e }
@@ -458,6 +570,14 @@ ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e }
*/
ExplicitParameterNode parameterNode(Parameter p) { result.getParameter() = p }
+/**
+ * Gets the `Node` corresponding to a definition by reference of the variable
+ * that is passed as unconverted `argument` of a call.
+ */
+DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) {
+ result.getArgument() = argument
+}
+
/** Gets the `VariableNode` corresponding to the variable `v`. */
VariableNode variableNode(Variable v) { result.getVariable() = v }
@@ -481,29 +601,39 @@ 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) {
- simpleInstructionLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asInstruction())
+ // Operand -> Instruction flow
+ simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
+ or
+ // Instruction -> Operand flow
+ simpleOperandLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asOperand())
}
pragma[noinline]
private predicate getFieldSizeOfClass(Class c, Type type, int size) {
exists(Field f |
f.getDeclaringType() = c and
- f.getType() = type and
+ f.getUnderlyingType() = type and
type.getSize() = size
)
}
-cached
-private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction iTo) {
- iTo.(CopyInstruction).getSourceValue() = iFrom
- or
- iTo.(PhiInstruction).getAnOperand().getDef() = iFrom
+private predicate isSingleFieldClass(Type type, Operand op) {
+ exists(int size, Class c |
+ c = op.getType().getUnderlyingType() and
+ c.getSize() = size and
+ getFieldSizeOfClass(c, type, size)
+ )
+}
+
+private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) {
+ // Propagate flow from an instruction to its exact uses.
+ opTo.getDef() = iFrom
or
- // A read side effect is almost never exact since we don't know exactly how
- // much memory the callee will read.
- iTo.(ReadSideEffectInstruction).getSideEffectOperand().getAnyDef() = iFrom and
- not iFrom.isResultConflated()
+ opTo = any(ReadSideEffectInstruction read).getSideEffectOperand() and
+ not iFrom.isResultConflated() and
+ iFrom = opTo.getAnyDef()
or
// Loading a single `int` from an `int *` parameter is not an exact load since
// the parameter may point to an entire array rather than a single `int`. The
@@ -517,20 +647,33 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
// leads to a phi node.
exists(InitializeIndirectionInstruction init |
iFrom = init and
- iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = init and
+ opTo.(LoadOperand).getAnyDef() = init and
// Check that the types match. Otherwise we can get flow from an object to
// its fields, which leads to field conflation when there's flow from other
// fields to the object elsewhere.
init.getParameter().getType().getUnspecifiedType().(DerivedType).getBaseType() =
- iTo.getResultType().getUnspecifiedType()
+ opTo.getType().getUnspecifiedType()
)
or
+ // Flow from stores to structs with a single field to a load of that field.
+ exists(LoadInstruction load |
+ load.getSourceValueOperand() = opTo and
+ opTo.getAnyDef() = iFrom and
+ isSingleFieldClass(iFrom.getResultType(), opTo)
+ )
+}
+
+private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
+ iTo.(CopyInstruction).getSourceValueOperand() = opFrom
+ or
+ iTo.(PhiInstruction).getAnInputOperand() = opFrom
+ or
// Treat all conversions as flow, even conversions between different numeric types.
- iTo.(ConvertInstruction).getUnary() = iFrom
+ iTo.(ConvertInstruction).getUnaryOperand() = opFrom
or
- iTo.(CheckedConvertOrNullInstruction).getUnary() = iFrom
+ iTo.(CheckedConvertOrNullInstruction).getUnaryOperand() = opFrom
or
- iTo.(InheritanceConversionInstruction).getUnary() = iFrom
+ iTo.(InheritanceConversionInstruction).getUnaryOperand() = opFrom
or
// A chi instruction represents a point where a new value (the _partial_
// operand) may overwrite an old value (the _total_ operand), but the alias
@@ -543,7 +686,7 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
//
// Flow through the partial operand belongs in the taint-tracking libraries
// for now.
- iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom
+ iTo.getAnOperand().(ChiTotalOperand) = opFrom
or
// Add flow from write side-effects to non-conflated chi instructions through their
// partial operands. From there, a `readStep` will find subsequent reads of that field.
@@ -558,23 +701,16 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
// Here, a `WriteSideEffectInstruction` will provide a new definition for `p->x` after the call to
// `setX`, which will be melded into `p` through a chi instruction.
exists(ChiInstruction chi | chi = iTo |
- chi.getPartialOperand().getDef() = iFrom.(WriteSideEffectInstruction) and
+ opFrom.getAnyDef() instanceof WriteSideEffectInstruction and
+ chi.getPartialOperand() = opFrom and
not chi.isResultConflated()
)
or
- // Flow from stores to structs with a single field to a load of that field.
- iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = iFrom and
- exists(int size, Type type |
- type = iFrom.getResultType() and
- iTo.getResultType().getSize() = size and
- getFieldSizeOfClass(iTo.getResultType(), type, size)
- )
- or
// Flow through modeled functions
- modelFlow(iFrom, iTo)
+ modelFlow(opFrom, iTo)
}
-private predicate modelFlow(Instruction iFrom, Instruction iTo) {
+private predicate modelFlow(Operand opFrom, Instruction iTo) {
exists(
CallInstruction call, DataFlowFunction func, FunctionInput modelIn, FunctionOutput modelOut
|
@@ -594,23 +730,33 @@ private predicate modelFlow(Instruction iFrom, Instruction iTo) {
iTo = outNode and
outNode = getSideEffectFor(call, index)
)
- // TODO: add write side effects for qualifiers
+ or
+ exists(WriteSideEffectInstruction outNode |
+ modelOut.isQualifierObject() and
+ iTo = outNode and
+ outNode = getSideEffectFor(call, -1)
+ )
) and
(
exists(int index |
modelIn.isParameter(index) and
- iFrom = call.getPositionalArgument(index)
+ opFrom = call.getPositionalArgumentOperand(index)
)
or
exists(int index, ReadSideEffectInstruction read |
modelIn.isParameterDeref(index) and
read = getSideEffectFor(call, index) and
- iFrom = read.getSideEffectOperand().getAnyDef()
+ opFrom = read.getSideEffectOperand()
)
or
modelIn.isQualifierAddress() and
- iFrom = call.getThisArgument()
- // TODO: add read side effects for qualifiers
+ opFrom = call.getThisArgumentOperand()
+ or
+ exists(ReadSideEffectInstruction read |
+ modelIn.isQualifierObject() and
+ read = getSideEffectFor(call, -1) and
+ opFrom = read.getSideEffectOperand()
+ )
)
)
}
@@ -658,13 +804,20 @@ predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)
*/
class BarrierGuard extends IRGuardCondition {
/** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */
- abstract predicate checks(Instruction instr, boolean b);
+ predicate checksInstr(Instruction instr, boolean b) { none() }
+
+ /** Override this predicate to hold if this guard validates `expr` upon evaluating to `b`. */
+ predicate checks(Expr e, boolean b) { none() }
/** Gets a node guarded by this guard. */
final Node getAGuardedNode() {
exists(ValueNumber value, boolean edge |
+ (
+ this.checksInstr(value.getAnInstruction(), edge)
+ or
+ this.checks(value.getAnInstruction().getConvertedResultExpression(), edge)
+ ) and
result.asInstruction() = value.getAnInstruction() and
- this.checks(value.getAnInstruction(), edge) and
this.controls(result.asInstruction().getBlock(), edge)
)
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll
index d1cafb28f1c1..93d74519ca5c 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll
@@ -9,24 +9,31 @@ private import semmle.code.cpp.ir.dataflow.DataFlow
/**
* Gets the instruction that goes into `input` for `call`.
*/
-Instruction callInput(CallInstruction call, FunctionInput input) {
+DataFlow::Node callInput(CallInstruction call, FunctionInput input) {
// A positional argument
exists(int index |
- result = call.getPositionalArgument(index) and
+ result.asInstruction() = call.getPositionalArgument(index) and
input.isParameter(index)
)
or
// A value pointed to by a positional argument
exists(ReadSideEffectInstruction read |
- result = read and
+ result.asOperand() = read.getSideEffectOperand() and
read.getPrimaryInstruction() = call and
input.isParameterDeref(read.getIndex())
)
or
// The qualifier pointer
- result = call.getThisArgument() and
+ result.asInstruction() = call.getThisArgument() and
input.isQualifierAddress()
- //TODO: qualifier deref
+ or
+ // The qualifier object
+ exists(ReadSideEffectInstruction read |
+ result.asOperand() = read.getSideEffectOperand() and
+ read.getPrimaryInstruction() = call and
+ read.getIndex() = -1 and
+ input.isQualifierObject()
+ )
}
/**
@@ -43,5 +50,13 @@ Instruction callOutput(CallInstruction call, FunctionOutput output) {
effect.getPrimaryInstruction() = call and
output.isParameterDeref(effect.getIndex())
)
- // TODO: qualifiers, return value dereference
+ or
+ // The side effect of a call on the qualifier object
+ exists(WriteSideEffectInstruction effect |
+ result = effect and
+ effect.getPrimaryInstruction() = call and
+ effect.getIndex() = -1 and
+ output.isQualifierObject()
+ )
+ // TODO: return value dereference
}
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 000000000000..edbb11db2f37
--- /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/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
index 2290bab05713..202d3f1c14ef 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
@@ -19,8 +19,11 @@ predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
* different objects.
*/
+cached
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
localInstructionTaintStep(nodeFrom.asInstruction(), nodeTo.asInstruction())
+ or
+ modeledTaintStep(nodeFrom, nodeTo)
}
/**
@@ -49,8 +52,6 @@ private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction no
or
nodeTo.(LoadInstruction).getSourceAddress() = nodeFrom
or
- modeledInstructionTaintStep(nodeFrom, nodeTo)
- or
// Flow through partial reads of arrays and unions
nodeTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = nodeFrom and
not nodeFrom.isResultConflated() and
@@ -100,19 +101,26 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
}
/**
- * Holds if `node` should be a barrier in all global taint flow configurations
+ * Holds if `node` should be a sanitizer in all global taint flow configurations
* but not in local taint.
*/
-predicate defaultTaintBarrier(DataFlow::Node node) { none() }
+predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
/**
* Holds if taint can flow from `instrIn` to `instrOut` through a call to a
* modeled function.
*/
-predicate modeledInstructionTaintStep(Instruction instrIn, Instruction instrOut) {
+predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
- instrIn = callInput(call, modelIn) and
- instrOut = callOutput(call, modelOut) and
+ (
+ nodeIn = callInput(call, modelIn)
+ or
+ exists(int n |
+ modelIn.isParameterDeref(n) and
+ nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n))
+ )
+ ) and
+ nodeOut.asInstruction() = callOutput(call, modelOut) and
call.getStaticCallTarget() = func and
func.hasTaintFlow(modelIn, modelOut)
)
@@ -126,8 +134,8 @@ predicate modeledInstructionTaintStep(Instruction instrIn, Instruction instrOut)
CallInstruction call, Function func, FunctionInput modelIn, OutParameterDeref modelMidOut,
int indexMid, InParameter modelMidIn, OutReturnValue modelOut
|
- instrIn = callInput(call, modelIn) and
- instrOut = callOutput(call, modelOut) and
+ nodeIn = callInput(call, modelIn) and
+ nodeOut.asInstruction() = callOutput(call, modelOut) and
call.getStaticCallTarget() = func and
func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and
func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
index 0f0607662e90..b509fad9cd20 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
@@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private
* To create a configuration, extend this class with a subclass whose
* characteristic predicate is a unique singleton string. For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
- defaultTaintBarrier(node)
+ defaultTaintSanitizer(node)
}
- /** Holds if data flow into `node` is prohibited. */
+ /** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
- /** Holds if data flow out of `node` is prohibited. */
+ /** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
+ /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
index 0f0607662e90..b509fad9cd20 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
@@ -26,7 +26,7 @@ private import TaintTrackingParameter::Private
* To create a configuration, extend this class with a subclass whose
* characteristic predicate is a unique singleton string. For example, write
*
- * ```
+ * ```ql
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
@@ -41,7 +41,7 @@ private import TaintTrackingParameter::Private
* Then, to query whether there is flow between some `source` and `sink`,
* write
*
- * ```
+ * ```ql
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
* ```
*
@@ -76,20 +76,20 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrier(DataFlow::Node node) {
isSanitizer(node) or
- defaultTaintBarrier(node)
+ defaultTaintSanitizer(node)
}
- /** Holds if data flow into `node` is prohibited. */
+ /** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
- /** Holds if data flow out of `node` is prohibited. */
+ /** Holds if taint propagation out of `node` is prohibited. */
predicate isSanitizerOut(DataFlow::Node node) { none() }
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
+ /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll
index 54059fb5b82f..32e36bb67876 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/EdgeKind.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes that specify the conditions under which control flows along a given edge.
+ */
+
private import internal.EdgeKindInternal
private newtype TEdgeKind =
@@ -77,9 +81,15 @@ class CaseEdge extends EdgeKind, TCaseEdge {
else result = "Case[" + minValue + ".." + maxValue + "]"
}
- string getMinValue() { result = minValue }
+ /**
+ * Gets the smallest value of the switch expression for which control will flow along this edge.
+ */
+ final string getMinValue() { result = minValue }
- string getMaxValue() { result = maxValue }
+ /**
+ * Gets the largest value of the switch expression for which control will flow along this edge.
+ */
+ final string getMaxValue() { result = maxValue }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll
index 71bc8ec2b0f2..37ac2fccdd94 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRConfiguration.qll
@@ -10,6 +10,7 @@ private newtype TIRConfiguration = MkIRConfiguration()
* The query can extend this class to control which functions have IR generated for them.
*/
class IRConfiguration extends TIRConfiguration {
+ /** Gets a textual representation of this element. */
string toString() { result = "IRConfiguration" }
/**
@@ -17,6 +18,13 @@ class IRConfiguration extends TIRConfiguration {
*/
predicate shouldCreateIRForFunction(Language::Function func) { any() }
+ /**
+ * Holds if the strings used as part of an IR dump should be generated for function `func`.
+ *
+ * This predicate is overridden in `PrintIR.qll` to avoid the expense of generating a large number
+ * of debug strings for IR that will not be dumped. We still generate the actual IR for these
+ * functions, however, to preserve the results of any interprocedural analysis.
+ */
predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() }
}
@@ -26,6 +34,7 @@ private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration
* The query can extend this class to control what escape analysis is used when generating SSA.
*/
class IREscapeAnalysisConfiguration extends TIREscapeAnalysisConfiguration {
+ /** Gets a textual representation of this element. */
string toString() { result = "IREscapeAnalysisConfiguration" }
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
index d196cdce0ab2..3bf3bf2e2766 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
@@ -32,6 +32,7 @@ private newtype TIRType =
* all pointer types map to the same instance of `IRAddressType`.
*/
class IRType extends TIRType {
+ /** Gets a textual representation of this type. */
string toString() { none() }
/**
@@ -111,6 +112,8 @@ private class IRSizedType extends IRType {
this = TIRFunctionAddressType(byteSize) or
this = TIROpaqueType(_, byteSize)
}
+ // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
+ // overridden only in the leaf classes.
}
/**
@@ -128,7 +131,7 @@ class IRBooleanType extends IRSizedType, TIRBooleanType {
}
/**
- * A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
+ * A numeric type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
* `IRFloatingPointType`.
*/
class IRNumericType extends IRSizedType {
@@ -137,13 +140,33 @@ class IRNumericType extends IRSizedType {
this = TIRUnsignedIntegerType(byteSize) or
this = TIRFloatingPointType(byteSize, _, _)
}
+ // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
+ // overridden only in the leaf classes.
+}
+
+/**
+ * An integer type. This includes `IRSignedIntegerType` and `IRUnsignedIntegerType`.
+ */
+class IRIntegerType extends IRNumericType {
+ IRIntegerType() {
+ this = TIRSignedIntegerType(byteSize) or
+ this = TIRUnsignedIntegerType(byteSize)
+ }
+
+ /** Holds if this integer type is signed. */
+ predicate isSigned() { none() }
+
+ /** Holds if this integer type is unsigned. */
+ predicate isUnsigned() { none() }
+ // Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
+ // overridden only in the leaf classes.
}
/**
* A signed two's-complement integer. Also used to represent enums whose underlying type is a signed
* integer, as well as character types whose representation is signed.
*/
-class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
+class IRSignedIntegerType extends IRIntegerType, TIRSignedIntegerType {
final override string toString() { result = "int" + byteSize.toString() }
final override Language::LanguageType getCanonicalLanguageType() {
@@ -152,13 +175,15 @@ class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
pragma[noinline]
final override int getByteSize() { result = byteSize }
+
+ override predicate isSigned() { any() }
}
/**
* An unsigned two's-complement integer. Also used to represent enums whose underlying type is an
* unsigned integer, as well as character types whose representation is unsigned.
*/
-class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
+class IRUnsignedIntegerType extends IRIntegerType, TIRUnsignedIntegerType {
final override string toString() { result = "uint" + byteSize.toString() }
final override Language::LanguageType getCanonicalLanguageType() {
@@ -167,6 +192,8 @@ class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
pragma[noinline]
final override int getByteSize() { result = byteSize }
+
+ override predicate isUnsigned() { any() }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll
index 6852a9654017..5e11a310e2fb 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll
@@ -1,3 +1,9 @@
+/**
+ * Provides classes that describe how a particular `Instruction` or its operands access memory.
+ */
+
+private import IRConfiguration
+
private newtype TMemoryAccessKind =
TIndirectMemoryAccess() or
TBufferMemoryAccess() or
@@ -14,6 +20,7 @@ private newtype TMemoryAccessKind =
* memory result.
*/
class MemoryAccessKind extends TMemoryAccessKind {
+ /** Gets a textual representation of this access kind. */
string toString() { none() }
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll
index c0b8adbe56be..c4134d240aba 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides `Opcode`s that specify the operation performed by an `Instruction`, as well as metadata
+ * about those opcodes, such as operand kinds and memory accesses.
+ */
+
private import internal.OpcodeImports as Imports
private import internal.OperandTag
import Imports::MemoryAccessKind
@@ -45,7 +50,7 @@ private newtype TOpcode =
TConvertToDerived() or
TCheckedConvertOrNull() or
TCheckedConvertOrThrow() or
- TDynamicCastToVoid() or
+ TCompleteObjectAddress() or
TVariableAddress() or
TFieldAddress() or
TFunctionAddress() or
@@ -86,7 +91,11 @@ private newtype TOpcode =
TUnreached() or
TNewObj()
+/**
+ * An opcode that specifies the operation performed by an `Instruction`.
+ */
class Opcode extends TOpcode {
+ /** Gets a textual representation of this element. */
string toString() { result = "UnknownOpcode" }
/**
@@ -139,10 +148,20 @@ class Opcode extends TOpcode {
predicate hasOperandInternal(OperandTag tag) { none() }
}
+/**
+ * The `Opcode` for a `UnaryInstruction`.
+ *
+ * See the `UnaryInstruction` documentation for more details.
+ */
abstract class UnaryOpcode extends Opcode {
final override predicate hasOperandInternal(OperandTag tag) { tag instanceof UnaryOperandTag }
}
+/**
+ * The `Opcode` for a `BinaryInstruction`.
+ *
+ * See the `BinaryInstruction` documentation for more details.
+ */
abstract class BinaryOpcode extends Opcode {
final override predicate hasOperandInternal(OperandTag tag) {
tag instanceof LeftOperandTag or
@@ -150,44 +169,127 @@ abstract class BinaryOpcode extends Opcode {
}
}
+/**
+ * The `Opcode` for a `PointerArithmeticInstruction`.
+ *
+ * See the `PointerArithmeticInstruction` documentation for more details.
+ */
abstract class PointerArithmeticOpcode extends BinaryOpcode { }
+/**
+ * The `Opcode` for a `PointerOffsetInstruction`.
+ *
+ * See the `PointerOffsetInstruction` documentation for more details.
+ */
abstract class PointerOffsetOpcode extends PointerArithmeticOpcode { }
+/**
+ * The `Opcode` for an `ArithmeticInstruction`.
+ *
+ * See the `ArithmeticInstruction` documentation for more details.
+ */
abstract class ArithmeticOpcode extends Opcode { }
+/**
+ * The `Opcode` for a `BinaryArithmeticInstruction`.
+ *
+ * See the `BinaryArithmeticInstruction` documentation for more details.
+ */
abstract class BinaryArithmeticOpcode extends BinaryOpcode, ArithmeticOpcode { }
+/**
+ * The `Opcode` for a `UnaryArithmeticInstruction`.
+ *
+ * See the `UnaryArithmeticInstruction` documentation for more details.
+ */
abstract class UnaryArithmeticOpcode extends UnaryOpcode, ArithmeticOpcode { }
+/**
+ * The `Opcode` for a `BitwiseInstruction`.
+ *
+ * See the `BitwiseInstruction` documentation for more details.
+ */
abstract class BitwiseOpcode extends Opcode { }
+/**
+ * The `Opcode` for a `BinaryBitwiseInstruction`.
+ *
+ * See the `BinaryBitwiseInstruction` documentation for more details.
+ */
abstract class BinaryBitwiseOpcode extends BinaryOpcode, BitwiseOpcode { }
+/**
+ * The `Opcode` for a `UnaryBitwiseInstruction`.
+ *
+ * See the `UnaryBitwiseInstruction` documentation for more details.
+ */
abstract class UnaryBitwiseOpcode extends UnaryOpcode, BitwiseOpcode { }
+/**
+ * The `Opcode` for a `CompareInstruction`.
+ *
+ * See the `CompareInstruction` documentation for more details.
+ */
abstract class CompareOpcode extends BinaryOpcode { }
+/**
+ * The `Opcode` for a `RelationalInstruction`.
+ *
+ * See the `RelationalInstruction` documentation for more details.
+ */
abstract class RelationalOpcode extends CompareOpcode { }
+/**
+ * The `Opcode` for a `CopyInstruction`.
+ *
+ * See the `CopyInstruction` documentation for more details.
+ */
abstract class CopyOpcode extends Opcode { }
+/**
+ * The `Opcode` for a `ConvertToBaseInstruction`.
+ *
+ * See the `ConvertToBaseInstruction` documentation for more details.
+ */
abstract class ConvertToBaseOpcode extends UnaryOpcode { }
-abstract class MemoryAccessOpcode extends Opcode { }
-
+/**
+ * The `Opcode` for a `ReturnInstruction`.
+ *
+ * See the `ReturnInstruction` documentation for more details.
+ */
abstract class ReturnOpcode extends Opcode { }
+/**
+ * The `Opcode` for a `ThrowInstruction`.
+ *
+ * See the `ThrowInstruction` documentation for more details.
+ */
abstract class ThrowOpcode extends Opcode { }
+/**
+ * The `Opcode` for a `CatchInstruction`.
+ *
+ * See the `CatchInstruction` documentation for more details.
+ */
abstract class CatchOpcode extends Opcode { }
-abstract class OpcodeWithCondition extends Opcode {
+abstract private class OpcodeWithCondition extends Opcode {
final override predicate hasOperandInternal(OperandTag tag) { tag instanceof ConditionOperandTag }
}
+/**
+ * The `Opcode` for a `BuiltInOperationInstruction`.
+ *
+ * See the `BuiltInOperationInstruction` documentation for more details.
+ */
abstract class BuiltInOperationOpcode extends Opcode { }
+/**
+ * The `Opcode` for a `SideEffectInstruction`.
+ *
+ * See the `SideEffectInstruction` documentation for more details.
+ */
abstract class SideEffectOpcode extends Opcode { }
/**
@@ -323,7 +425,9 @@ abstract class OpcodeWithLoad extends IndirectReadOpcode {
}
/**
- * An opcode that reads from a set of memory locations as a side effect.
+ * The `Opcode` for a `ReadSideEffectInstruction`.
+ *
+ * See the `ReadSideEffectInstruction` documentation for more details.
*/
abstract class ReadSideEffectOpcode extends SideEffectOpcode {
final override predicate hasOperandInternal(OperandTag tag) {
@@ -332,51 +436,111 @@ abstract class ReadSideEffectOpcode extends SideEffectOpcode {
}
/**
- * An opcode that writes to a set of memory locations as a side effect.
+ * The `Opcode` for a `WriteSideEffectInstruction`.
+ *
+ * See the `WriteSideEffectInstruction` documentation for more details.
*/
abstract class WriteSideEffectOpcode extends SideEffectOpcode { }
+/**
+ * Provides `Opcode`s that specify the operation performed by an `Instruction`.
+ */
module Opcode {
+ /**
+ * The `Opcode` for a `NoOpInstruction`.
+ *
+ * See the `NoOpInstruction` documentation for more details.
+ */
class NoOp extends Opcode, TNoOp {
final override string toString() { result = "NoOp" }
}
+ /**
+ * The `Opcode` for an `UninitializedInstruction`.
+ *
+ * See the `UninitializedInstruction` documentation for more details.
+ */
class Uninitialized extends IndirectWriteOpcode, TUninitialized {
final override string toString() { result = "Uninitialized" }
}
+ /**
+ * The `Opcode` for an `ErrorInstruction`.
+ *
+ * See the `ErrorInstruction` documentation for more details.
+ */
class Error extends Opcode, TError {
final override string toString() { result = "Error" }
}
+ /**
+ * The `Opcode` for an `InitializeParameterInstruction`.
+ *
+ * See the `InitializeParameterInstruction` documentation for more details.
+ */
class InitializeParameter extends IndirectWriteOpcode, TInitializeParameter {
final override string toString() { result = "InitializeParameter" }
}
+ /**
+ * The `Opcode` for an `InitializeIndirectionInstruction`.
+ *
+ * See the `InitializeIndirectionInstruction` documentation for more details.
+ */
class InitializeIndirection extends EntireAllocationWriteOpcode, TInitializeIndirection {
final override string toString() { result = "InitializeIndirection" }
}
+ /**
+ * The `Opcode` for an `InitializeThisInstruction`.
+ *
+ * See the `InitializeThisInstruction` documentation for more details.
+ */
class InitializeThis extends Opcode, TInitializeThis {
final override string toString() { result = "InitializeThis" }
}
+ /**
+ * The `Opcode` for an `EnterFunctionInstruction`.
+ *
+ * See the `EnterFunctionInstruction` documentation for more details.
+ */
class EnterFunction extends Opcode, TEnterFunction {
final override string toString() { result = "EnterFunction" }
}
+ /**
+ * The `Opcode` for an `ExitFunctionInstruction`.
+ *
+ * See the `ExitFunctionInstruction` documentation for more details.
+ */
class ExitFunction extends Opcode, TExitFunction {
final override string toString() { result = "ExitFunction" }
}
+ /**
+ * The `Opcode` for a `ReturnValueInstruction`.
+ *
+ * See the `ReturnValueInstruction` documentation for more details.
+ */
class ReturnValue extends ReturnOpcode, OpcodeWithLoad, TReturnValue {
final override string toString() { result = "ReturnValue" }
}
+ /**
+ * The `Opcode` for a `ReturnVoidInstruction`.
+ *
+ * See the `ReturnVoidInstruction` documentation for more details.
+ */
class ReturnVoid extends ReturnOpcode, TReturnVoid {
final override string toString() { result = "ReturnVoid" }
}
+ /**
+ * The `Opcode` for a `ReturnIndirectionInstruction`.
+ *
+ * See the `ReturnIndirectionInstruction` documentation for more details.
+ */
class ReturnIndirection extends EntireAllocationReadOpcode, TReturnIndirection {
final override string toString() { result = "ReturnIndirection" }
@@ -385,14 +549,29 @@ module Opcode {
}
}
+ /**
+ * The `Opcode` for a `CopyValueInstruction`.
+ *
+ * See the `CopyValueInstruction` documentation for more details.
+ */
class CopyValue extends UnaryOpcode, CopyOpcode, TCopyValue {
final override string toString() { result = "CopyValue" }
}
+ /**
+ * The `Opcode` for a `LoadInstruction`.
+ *
+ * See the `LoadInstruction` documentation for more details.
+ */
class Load extends CopyOpcode, OpcodeWithLoad, TLoad {
final override string toString() { result = "Load" }
}
+ /**
+ * The `Opcode` for a `StoreInstruction`.
+ *
+ * See the `StoreInstruction` documentation for more details.
+ */
class Store extends CopyOpcode, IndirectWriteOpcode, TStore {
final override string toString() { result = "Store" }
@@ -401,154 +580,344 @@ module Opcode {
}
}
+ /**
+ * The `Opcode` for an `AddInstruction`.
+ *
+ * See the `AddInstruction` documentation for more details.
+ */
class Add extends BinaryArithmeticOpcode, TAdd {
final override string toString() { result = "Add" }
}
+ /**
+ * The `Opcode` for a `SubInstruction`.
+ *
+ * See the `SubInstruction` documentation for more details.
+ */
class Sub extends BinaryArithmeticOpcode, TSub {
final override string toString() { result = "Sub" }
}
+ /**
+ * The `Opcode` for a `MulInstruction`.
+ *
+ * See the `MulInstruction` documentation for more details.
+ */
class Mul extends BinaryArithmeticOpcode, TMul {
final override string toString() { result = "Mul" }
}
+ /**
+ * The `Opcode` for a `DivInstruction`.
+ *
+ * See the `DivInstruction` documentation for more details.
+ */
class Div extends BinaryArithmeticOpcode, TDiv {
final override string toString() { result = "Div" }
}
+ /**
+ * The `Opcode` for a `RemInstruction`.
+ *
+ * See the `RemInstruction` documentation for more details.
+ */
class Rem extends BinaryArithmeticOpcode, TRem {
final override string toString() { result = "Rem" }
}
+ /**
+ * The `Opcode` for a `NegateInstruction`.
+ *
+ * See the `NegateInstruction` documentation for more details.
+ */
class Negate extends UnaryArithmeticOpcode, TNegate {
final override string toString() { result = "Negate" }
}
+ /**
+ * The `Opcode` for a `ShiftLeftInstruction`.
+ *
+ * See the `ShiftLeftInstruction` documentation for more details.
+ */
class ShiftLeft extends BinaryBitwiseOpcode, TShiftLeft {
final override string toString() { result = "ShiftLeft" }
}
+ /**
+ * The `Opcode` for a `ShiftRightInstruction`.
+ *
+ * See the `ShiftRightInstruction` documentation for more details.
+ */
class ShiftRight extends BinaryBitwiseOpcode, TShiftRight {
final override string toString() { result = "ShiftRight" }
}
+ /**
+ * The `Opcode` for a `BitAndInstruction`.
+ *
+ * See the `BitAndInstruction` documentation for more details.
+ */
class BitAnd extends BinaryBitwiseOpcode, TBitAnd {
final override string toString() { result = "BitAnd" }
}
+ /**
+ * The `Opcode` for a `BitOrInstruction`.
+ *
+ * See the `BitOrInstruction` documentation for more details.
+ */
class BitOr extends BinaryBitwiseOpcode, TBitOr {
final override string toString() { result = "BitOr" }
}
+ /**
+ * The `Opcode` for a `BitXorInstruction`.
+ *
+ * See the `BitXorInstruction` documentation for more details.
+ */
class BitXor extends BinaryBitwiseOpcode, TBitXor {
final override string toString() { result = "BitXor" }
}
+ /**
+ * The `Opcode` for a `BitComplementInstruction`.
+ *
+ * See the `BitComplementInstruction` documentation for more details.
+ */
class BitComplement extends UnaryBitwiseOpcode, TBitComplement {
final override string toString() { result = "BitComplement" }
}
+ /**
+ * The `Opcode` for a `LogicalNotInstruction`.
+ *
+ * See the `LogicalNotInstruction` documentation for more details.
+ */
class LogicalNot extends UnaryOpcode, TLogicalNot {
final override string toString() { result = "LogicalNot" }
}
+ /**
+ * The `Opcode` for a `CompareEQInstruction`.
+ *
+ * See the `CompareEQInstruction` documentation for more details.
+ */
class CompareEQ extends CompareOpcode, TCompareEQ {
final override string toString() { result = "CompareEQ" }
}
+ /**
+ * The `Opcode` for a `CompareNEInstruction`.
+ *
+ * See the `CompareNEInstruction` documentation for more details.
+ */
class CompareNE extends CompareOpcode, TCompareNE {
final override string toString() { result = "CompareNE" }
}
+ /**
+ * The `Opcode` for a `CompareLTInstruction`.
+ *
+ * See the `CompareLTInstruction` documentation for more details.
+ */
class CompareLT extends RelationalOpcode, TCompareLT {
final override string toString() { result = "CompareLT" }
}
+ /**
+ * The `Opcode` for a `CompareGTInstruction`.
+ *
+ * See the `CompareGTInstruction` documentation for more details.
+ */
class CompareGT extends RelationalOpcode, TCompareGT {
final override string toString() { result = "CompareGT" }
}
+ /**
+ * The `Opcode` for a `CompareLEInstruction`.
+ *
+ * See the `CompareLEInstruction` documentation for more details.
+ */
class CompareLE extends RelationalOpcode, TCompareLE {
final override string toString() { result = "CompareLE" }
}
+ /**
+ * The `Opcode` for a `CompareGEInstruction`.
+ *
+ * See the `CompareGEInstruction` documentation for more details.
+ */
class CompareGE extends RelationalOpcode, TCompareGE {
final override string toString() { result = "CompareGE" }
}
+ /**
+ * The `Opcode` for a `PointerAddInstruction`.
+ *
+ * See the `PointerAddInstruction` documentation for more details.
+ */
class PointerAdd extends PointerOffsetOpcode, TPointerAdd {
final override string toString() { result = "PointerAdd" }
}
+ /**
+ * The `Opcode` for a `PointerSubInstruction`.
+ *
+ * See the `PointerSubInstruction` documentation for more details.
+ */
class PointerSub extends PointerOffsetOpcode, TPointerSub {
final override string toString() { result = "PointerSub" }
}
+ /**
+ * The `Opcode` for a `PointerDiffInstruction`.
+ *
+ * See the `PointerDiffInstruction` documentation for more details.
+ */
class PointerDiff extends PointerArithmeticOpcode, TPointerDiff {
final override string toString() { result = "PointerDiff" }
}
+ /**
+ * The `Opcode` for a `ConvertInstruction`.
+ *
+ * See the `ConvertInstruction` documentation for more details.
+ */
class Convert extends UnaryOpcode, TConvert {
final override string toString() { result = "Convert" }
}
+ /**
+ * The `Opcode` for a `ConvertToNonVirtualBaseInstruction`.
+ *
+ * See the `ConvertToNonVirtualBaseInstruction` documentation for more details.
+ */
class ConvertToNonVirtualBase extends ConvertToBaseOpcode, TConvertToNonVirtualBase {
final override string toString() { result = "ConvertToNonVirtualBase" }
}
+ /**
+ * The `Opcode` for a `ConvertToVirtualBaseInstruction`.
+ *
+ * See the `ConvertToVirtualBaseInstruction` documentation for more details.
+ */
class ConvertToVirtualBase extends ConvertToBaseOpcode, TConvertToVirtualBase {
final override string toString() { result = "ConvertToVirtualBase" }
}
+ /**
+ * The `Opcode` for a `ConvertToDerivedInstruction`.
+ *
+ * See the `ConvertToDerivedInstruction` documentation for more details.
+ */
class ConvertToDerived extends UnaryOpcode, TConvertToDerived {
final override string toString() { result = "ConvertToDerived" }
}
+ /**
+ * The `Opcode` for a `CheckedConvertOrNullInstruction`.
+ *
+ * See the `CheckedConvertOrNullInstruction` documentation for more details.
+ */
class CheckedConvertOrNull extends UnaryOpcode, TCheckedConvertOrNull {
final override string toString() { result = "CheckedConvertOrNull" }
}
+ /**
+ * The `Opcode` for a `CheckedConvertOrThrowInstruction`.
+ *
+ * See the `CheckedConvertOrThrowInstruction` documentation for more details.
+ */
class CheckedConvertOrThrow extends UnaryOpcode, TCheckedConvertOrThrow {
final override string toString() { result = "CheckedConvertOrThrow" }
}
- class DynamicCastToVoid extends UnaryOpcode, TDynamicCastToVoid {
- final override string toString() { result = "DynamicCastToVoid" }
+ /**
+ * The `Opcode` for a `CompleteObjectAddressInstruction`.
+ *
+ * See the `CompleteObjectAddressInstruction` documentation for more details.
+ */
+ class CompleteObjectAddress extends UnaryOpcode, TCompleteObjectAddress {
+ final override string toString() { result = "CompleteObjectAddress" }
}
+ /**
+ * The `Opcode` for a `VariableAddressInstruction`.
+ *
+ * See the `VariableAddressInstruction` documentation for more details.
+ */
class VariableAddress extends Opcode, TVariableAddress {
final override string toString() { result = "VariableAddress" }
}
+ /**
+ * The `Opcode` for a `FieldAddressInstruction`.
+ *
+ * See the `FieldAddressInstruction` documentation for more details.
+ */
class FieldAddress extends UnaryOpcode, TFieldAddress {
final override string toString() { result = "FieldAddress" }
}
+ /**
+ * The `Opcode` for an `ElementsAddressInstruction`.
+ *
+ * See the `ElementsAddressInstruction` documentation for more details.
+ */
class ElementsAddress extends UnaryOpcode, TElementsAddress {
final override string toString() { result = "ElementsAddress" }
}
+ /**
+ * The `Opcode` for a `FunctionAddressInstruction`.
+ *
+ * See the `FunctionAddressInstruction` documentation for more details.
+ */
class FunctionAddress extends Opcode, TFunctionAddress {
final override string toString() { result = "FunctionAddress" }
}
+ /**
+ * The `Opcode` for a `ConstantInstruction`.
+ *
+ * See the `ConstantInstruction` documentation for more details.
+ */
class Constant extends Opcode, TConstant {
final override string toString() { result = "Constant" }
}
+ /**
+ * The `Opcode` for a `StringConstantInstruction`.
+ *
+ * See the `StringConstantInstruction` documentation for more details.
+ */
class StringConstant extends Opcode, TStringConstant {
final override string toString() { result = "StringConstant" }
}
+ /**
+ * The `Opcode` for a `ConditionalBranchInstruction`.
+ *
+ * See the `ConditionalBranchInstruction` documentation for more details.
+ */
class ConditionalBranch extends OpcodeWithCondition, TConditionalBranch {
final override string toString() { result = "ConditionalBranch" }
}
+ /**
+ * The `Opcode` for a `SwitchInstruction`.
+ *
+ * See the `SwitchInstruction` documentation for more details.
+ */
class Switch extends OpcodeWithCondition, TSwitch {
final override string toString() { result = "Switch" }
}
+ /**
+ * The `Opcode` for a `CallInstruction`.
+ *
+ * See the `CallInstruction` documentation for more details.
+ */
class Call extends Opcode, TCall {
final override string toString() { result = "Call" }
@@ -557,32 +926,67 @@ module Opcode {
}
}
+ /**
+ * The `Opcode` for a `CatchByTypeInstruction`.
+ *
+ * See the `CatchByTypeInstruction` documentation for more details.
+ */
class CatchByType extends CatchOpcode, TCatchByType {
final override string toString() { result = "CatchByType" }
}
+ /**
+ * The `Opcode` for a `CatchAnyInstruction`.
+ *
+ * See the `CatchAnyInstruction` documentation for more details.
+ */
class CatchAny extends CatchOpcode, TCatchAny {
final override string toString() { result = "CatchAny" }
}
+ /**
+ * The `Opcode` for a `ThrowValueInstruction`.
+ *
+ * See the `ThrowValueInstruction` documentation for more details.
+ */
class ThrowValue extends ThrowOpcode, OpcodeWithLoad, TThrowValue {
final override string toString() { result = "ThrowValue" }
}
+ /**
+ * The `Opcode` for a `ReThrowInstruction`.
+ *
+ * See the `ReThrowInstruction` documentation for more details.
+ */
class ReThrow extends ThrowOpcode, TReThrow {
final override string toString() { result = "ReThrow" }
}
+ /**
+ * The `Opcode` for an `UnwindInstruction`.
+ *
+ * See the `UnwindInstruction` documentation for more details.
+ */
class Unwind extends Opcode, TUnwind {
final override string toString() { result = "Unwind" }
}
+ /**
+ * The `Opcode` for an `AliasedDefinitionInstruction`.
+ *
+ * See the `AliasedDefinitionInstruction` documentation for more details.
+ */
class AliasedDefinition extends Opcode, TAliasedDefinition {
final override string toString() { result = "AliasedDefinition" }
final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess }
}
+ /**
+ * The `Opcode` for an `InitializeNonLocalInstruction`.
+ *
+ * See the `InitializeNonLocalInstruction` documentation for more details.
+ */
class InitializeNonLocal extends Opcode, TInitializeNonLocal {
final override string toString() { result = "InitializeNonLocal" }
@@ -591,6 +995,11 @@ module Opcode {
}
}
+ /**
+ * The `Opcode` for an `AliasedUseInstruction`.
+ *
+ * See the `AliasedUseInstruction` documentation for more details.
+ */
class AliasedUse extends Opcode, TAliasedUse {
final override string toString() { result = "AliasedUse" }
@@ -601,92 +1010,187 @@ module Opcode {
}
}
+ /**
+ * The `Opcode` for a `PhiInstruction`.
+ *
+ * See the `PhiInstruction` documentation for more details.
+ */
class Phi extends Opcode, TPhi {
final override string toString() { result = "Phi" }
final override MemoryAccessKind getWriteMemoryAccess() { result instanceof PhiMemoryAccess }
}
+ /**
+ * The `Opcode` for a `BuiltInInstruction`.
+ *
+ * See the `BuiltInInstruction` documentation for more details.
+ */
class BuiltIn extends BuiltInOperationOpcode, TBuiltIn {
final override string toString() { result = "BuiltIn" }
}
+ /**
+ * The `Opcode` for a `VarArgsStartInstruction`.
+ *
+ * See the `VarArgsStartInstruction` documentation for more details.
+ */
class VarArgsStart extends UnaryOpcode, TVarArgsStart {
final override string toString() { result = "VarArgsStart" }
}
+ /**
+ * The `Opcode` for a `VarArgsEndInstruction`.
+ *
+ * See the `VarArgsEndInstruction` documentation for more details.
+ */
class VarArgsEnd extends UnaryOpcode, TVarArgsEnd {
final override string toString() { result = "VarArgsEnd" }
}
+ /**
+ * The `Opcode` for a `VarArgInstruction`.
+ *
+ * See the `VarArgInstruction` documentation for more details.
+ */
class VarArg extends UnaryOpcode, TVarArg {
final override string toString() { result = "VarArg" }
}
+ /**
+ * The `Opcode` for a `NextVarArgInstruction`.
+ *
+ * See the `NextVarArgInstruction` documentation for more details.
+ */
class NextVarArg extends UnaryOpcode, TNextVarArg {
final override string toString() { result = "NextVarArg" }
}
+ /**
+ * The `Opcode` for a `CallSideEffectInstruction`.
+ *
+ * See the `CallSideEffectInstruction` documentation for more details.
+ */
class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode,
ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallSideEffect {
final override string toString() { result = "CallSideEffect" }
}
+ /**
+ * The `Opcode` for a `CallReadSideEffectInstruction`.
+ *
+ * See the `CallReadSideEffectInstruction` documentation for more details.
+ */
class CallReadSideEffect extends ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode,
TCallReadSideEffect {
final override string toString() { result = "CallReadSideEffect" }
}
+ /**
+ * The `Opcode` for an `IndirectReadSideEffectInstruction`.
+ *
+ * See the `IndirectReadSideEffectInstruction` documentation for more details.
+ */
class IndirectReadSideEffect extends ReadSideEffectOpcode, IndirectReadOpcode,
TIndirectReadSideEffect {
final override string toString() { result = "IndirectReadSideEffect" }
}
+ /**
+ * The `Opcode` for an `IndirectMustWriteSideEffectInstruction`.
+ *
+ * See the `IndirectMustWriteSideEffectInstruction` documentation for more details.
+ */
class IndirectMustWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode,
TIndirectMustWriteSideEffect {
final override string toString() { result = "IndirectMustWriteSideEffect" }
}
+ /**
+ * The `Opcode` for an `IndirectMayWriteSideEffectInstruction`.
+ *
+ * See the `IndirectMayWriteSideEffectInstruction` documentation for more details.
+ */
class IndirectMayWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode,
MayWriteOpcode, TIndirectMayWriteSideEffect {
final override string toString() { result = "IndirectMayWriteSideEffect" }
}
+ /**
+ * The `Opcode` for a `BufferReadSideEffectInstruction`.
+ *
+ * See the `BufferReadSideEffectInstruction` documentation for more details.
+ */
class BufferReadSideEffect extends ReadSideEffectOpcode, UnsizedBufferReadOpcode,
TBufferReadSideEffect {
final override string toString() { result = "BufferReadSideEffect" }
}
+ /**
+ * The `Opcode` for a `BufferMustWriteSideEffectInstruction`.
+ *
+ * See the `BufferMustWriteSideEffectInstruction` documentation for more details.
+ */
class BufferMustWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode,
TBufferMustWriteSideEffect {
final override string toString() { result = "BufferMustWriteSideEffect" }
}
+ /**
+ * The `Opcode` for a `BufferMayWriteSideEffectInstruction`.
+ *
+ * See the `BufferMayWriteSideEffectInstruction` documentation for more details.
+ */
class BufferMayWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode,
MayWriteOpcode, TBufferMayWriteSideEffect {
final override string toString() { result = "BufferMayWriteSideEffect" }
}
+ /**
+ * The `Opcode` for a `SizedBufferReadSideEffectInstruction`.
+ *
+ * See the `SizedBufferReadSideEffectInstruction` documentation for more details.
+ */
class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferReadOpcode,
TSizedBufferReadSideEffect {
final override string toString() { result = "SizedBufferReadSideEffect" }
}
+ /**
+ * The `Opcode` for a `SizedBufferMustWriteSideEffectInstruction`.
+ *
+ * See the `SizedBufferMustWriteSideEffectInstruction` documentation for more details.
+ */
class SizedBufferMustWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode,
TSizedBufferMustWriteSideEffect {
final override string toString() { result = "SizedBufferMustWriteSideEffect" }
}
+ /**
+ * The `Opcode` for a `SizedBufferMayWriteSideEffectInstruction`.
+ *
+ * See the `SizedBufferMayWriteSideEffectInstruction` documentation for more details.
+ */
class SizedBufferMayWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode,
MayWriteOpcode, TSizedBufferMayWriteSideEffect {
final override string toString() { result = "SizedBufferMayWriteSideEffect" }
}
+ /**
+ * The `Opcode` for an `InitializeDynamicAllocationInstruction`.
+ *
+ * See the `InitializeDynamicAllocationInstruction` documentation for more details.
+ */
class InitializeDynamicAllocation extends SideEffectOpcode, EntireAllocationWriteOpcode,
TInitializeDynamicAllocation {
final override string toString() { result = "InitializeDynamicAllocation" }
}
+ /**
+ * The `Opcode` for a `ChiInstruction`.
+ *
+ * See the `ChiInstruction` documentation for more details.
+ */
class Chi extends Opcode, TChi {
final override string toString() { result = "Chi" }
@@ -701,6 +1205,11 @@ module Opcode {
}
}
+ /**
+ * The `Opcode` for an `InlineAsmInstruction`.
+ *
+ * See the `InlineAsmInstruction` documentation for more details.
+ */
class InlineAsm extends Opcode, EscapedWriteOpcode, MayWriteOpcode, EscapedReadOpcode,
MayReadOpcode, TInlineAsm {
final override string toString() { result = "InlineAsm" }
@@ -710,10 +1219,20 @@ module Opcode {
}
}
+ /**
+ * The `Opcode` for an `UnreachedInstruction`.
+ *
+ * See the `UnreachedInstruction` documentation for more details.
+ */
class Unreached extends Opcode, TUnreached {
final override string toString() { result = "Unreached" }
}
+ /**
+ * The `Opcode` for a `NewObjInstruction`.
+ *
+ * See the `NewObjInstruction` documentation for more details.
+ */
class NewObj extends Opcode, TNewObj {
final override string toString() { result = "NewObj" }
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll
index a0c0ca675307..5f230de560d6 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/TempVariableTag.qll
@@ -12,5 +12,6 @@ private import Imports::TempVariableTag
* computed on each branch. The set of possible `TempVariableTag`s is language-dependent.
*/
class TempVariableTag extends TTempVariableTag {
+ /** Gets a textual representation of this tag. */
string toString() { result = getTempVariableTagId(this) }
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll
index badd48552a5f..c96783fe6e81 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll
@@ -1,3 +1,47 @@
+/**
+ * Provides classes that describe the Intermediate Representation (IR) of the program.
+ *
+ * The IR is a representation of the semantics of the program, with very little dependence on the
+ * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`,
+ * and `++i` all have the same semantic effect, but appear in the AST as three different types of
+ * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental
+ * operations similar to:
+ *
+ * ```
+ * r1(int*) = VariableAddress[i] // Compute the address of variable `i`
+ * r2(int) = Load &:r1, m0 // Load the value of `i`
+ * r3(int) = Constant[1] // An integer constant with the value `1`
+ * r4(int) = Add r2, r3 // Add `1` to the value of `i`
+ * r5(int) = Store &r1, r4 // Store the new value back into the variable `i`
+ * ```
+ *
+ * This allows IR-based analysis to focus on the fundamental operations, rather than having to be
+ * concerned with the various ways of expressing those operations in source code.
+ *
+ * The key classes in the IR are:
+ *
+ * - `IRFunction` - Contains the IR for an entire function definition, including all of that
+ * function's `Instruction`s, `IRBlock`s, and `IRVariables`.
+ * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be
+ * performed, the operands that produce the inputs to that operation, and the type of the result
+ * of the operation. Control flows from an `Instruction` to one of a set of successor
+ * `Instruction`s.
+ * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly
+ * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has
+ * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction`
+ * that produces its value (its "definition").
+ * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is
+ * created for each variable directly accessed by the function. In addition, `IRVariable`s are
+ * created to represent certain temporary storage locations that do not have explicitly declared
+ * variables in the source code, such as the return value of the function.
+ * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a
+ * sequence of instructions such that control flow can only enter the block at the first
+ * instruction, and can only leave the block from the last instruction.
+ * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType`
+ * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all
+ * be represented as the `IRType` `uint4`, a four-byte unsigned integer.
+ */
+
import IRFunction
import Instruction
import IRBlock
@@ -11,11 +55,12 @@ import Imports::MemoryAccessKind
private newtype TIRPropertyProvider = MkIRPropertyProvider()
/**
- * Class that provides additional properties to be dumped for IR instructions and blocks when using
+ * A class that provides additional properties to be dumped for IR instructions and blocks when using
* the PrintIR module. Libraries that compute additional facts about IR elements can extend the
* single instance of this class to specify the additional properties computed by the library.
*/
class IRPropertyProvider extends TIRPropertyProvider {
+ /** Gets a textual representation of this element. */
string toString() { result = "IRPropertyProvider" }
/**
@@ -27,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/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll
index 94ef73b27692..d827ed3cf82d 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes describing basic blocks in the IR of a function.
+ */
+
private import internal.IRInternal
import Instruction
private import internal.IRBlockImports as Imports
@@ -16,15 +20,27 @@ private import Cached
* Most consumers should use the class `IRBlock`.
*/
class IRBlockBase extends TIRBlock {
+ /** Gets a textual representation of this block. */
final string toString() { result = getFirstInstruction(this).toString() }
+ /** Gets the source location of the first non-`Phi` instruction in this block. */
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
+ /**
+ * INTERNAL: Do not use.
+ *
+ * Gets a string that uniquely identifies this block within its enclosing function.
+ *
+ * This predicate is used by debugging and printing code only.
+ */
final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() }
/**
- * Gets the zero-based index of the block within its function. This is used
- * by debugging and printing code only.
+ * INTERNAL: Do not use.
+ *
+ * Gets the zero-based index of the block within its function.
+ *
+ * This predicate is used by debugging and printing code only.
*/
int getDisplayIndex() {
exists(IRConfiguration::IRConfiguration config |
@@ -42,27 +58,51 @@ class IRBlockBase extends TIRBlock {
)
}
+ /**
+ * Gets the `index`th non-`Phi` instruction in this block.
+ */
final Instruction getInstruction(int index) { result = getInstruction(this, index) }
+ /**
+ * Get the `Phi` instructions that appear at the start of this block.
+ */
final PhiInstruction getAPhiInstruction() {
Construction::getPhiInstructionBlockStart(result) = getFirstInstruction()
}
+ /**
+ * Gets an instruction in this block. This includes `Phi` instructions.
+ */
final Instruction getAnInstruction() {
result = getInstruction(_) or
result = getAPhiInstruction()
}
+ /**
+ * Gets the first non-`Phi` instruction in this block.
+ */
final Instruction getFirstInstruction() { result = getFirstInstruction(this) }
+ /**
+ * Gets the last instruction in this block.
+ */
final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) }
+ /**
+ * Gets the number of non-`Phi` instructions in this block.
+ */
final int getInstructionCount() { result = getInstructionCount(this) }
+ /**
+ * Gets the `IRFunction` that contains this block.
+ */
final IRFunction getEnclosingIRFunction() {
result = getFirstInstruction(this).getEnclosingIRFunction()
}
+ /**
+ * Gets the `Function` that contains this block.
+ */
final Language::Function getEnclosingFunction() {
result = getFirstInstruction(this).getEnclosingFunction()
}
@@ -74,20 +114,57 @@ class IRBlockBase extends TIRBlock {
* instruction of another block.
*/
class IRBlock extends IRBlockBase {
+ /**
+ * Gets a block to which control flows directly from this block.
+ */
final IRBlock getASuccessor() { blockSuccessor(this, result) }
+ /**
+ * Gets a block from which control flows directly to this block.
+ */
final IRBlock getAPredecessor() { blockSuccessor(result, this) }
+ /**
+ * Gets the block to which control flows directly from this block along an edge of kind `kind`.
+ */
final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) }
+ /**
+ * Gets the block to which control flows directly from this block along a back edge of kind
+ * `kind`.
+ */
final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) }
+ /**
+ * Holds if this block immediately dominates `block`.
+ *
+ * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B`
+ * is a direct successor of block `A`.
+ */
final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) }
+ /**
+ * Holds if this block strictly dominates `block`.
+ *
+ * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B`
+ * are not the same block.
+ */
final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) }
+ /**
+ * Holds if this block dominates `block`.
+ *
+ * Block `A` dominates block `B` if any control flow path from the entry block of the function to
+ * block `B` must pass through block `A`. A block always dominates itself.
+ */
final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block }
+ /**
+ * Gets a block on the dominance frontier of this block.
+ *
+ * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
+ * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`.
+ */
pragma[noinline]
final IRBlock dominanceFrontier() {
dominates(result.getAPredecessor()) and
@@ -95,7 +172,7 @@ class IRBlock extends IRBlockBase {
}
/**
- * Holds if this block is reachable from the entry point of its function
+ * Holds if this block is reachable from the entry block of its function.
*/
final predicate isReachableFromFunctionEntry() {
this = getEnclosingIRFunction().getEntryBlock() or
@@ -210,4 +287,4 @@ private module Cached {
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
}
-Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
+private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll
index 65af34942b6b..6a87b9b4b5fd 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll
@@ -8,10 +8,79 @@ module InstructionConsistency {
private import Imports::Overlap
private import internal.IRInternal
+ private newtype TOptionalIRFunction =
+ TPresentIRFunction(IRFunction irFunc) or
+ TMissingIRFunction()
+
+ /**
+ * An `IRFunction` that might not exist. This is used so that we can produce consistency failures
+ * for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
+ */
+ abstract private class OptionalIRFunction extends TOptionalIRFunction {
+ abstract string toString();
+
+ abstract Language::Location getLocation();
+ }
+
+ private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
+ private IRFunction irFunc;
+
+ PresentIRFunction() { this = TPresentIRFunction(irFunc) }
+
+ override string toString() {
+ result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
+ }
+
+ override Language::Location getLocation() {
+ // To avoid an overwhelming number of results when the extractor merges functions with the
+ // same name, just pick a single location.
+ result =
+ rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
+ }
+ }
+
+ private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
+ override string toString() { result = "" }
+
+ override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
+ }
+
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
+ result = TPresentIRFunction(instr.getEnclosingIRFunction())
+ or
+ not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
+ result = getInstructionIRFunction(instr) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getOperandIRFunction(Operand operand) {
+ result = TPresentIRFunction(operand.getEnclosingIRFunction())
+ or
+ not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
+ result = getOperandIRFunction(operand) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getBlockIRFunction(IRBlock block) {
+ result = TPresentIRFunction(block.getEnclosingIRFunction())
+ or
+ not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
/**
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
*/
- query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
+ query predicate missingOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
exists(OperandTag tag |
instr.getOpcode().hasOperand(tag) and
not exists(NonPhiOperand operand |
@@ -21,32 +90,39 @@ module InstructionConsistency {
message =
"Instruction '" + instr.getOpcode().toString() +
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
/**
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
*/
- query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
- exists(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- not instr.getOpcode().hasOperand(tag) and
- not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
- not (
- instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
- ) and
- not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
+ query predicate unexpectedOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag |
+ exists(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ not instr.getOpcode().hasOperand(tag) and
+ not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
+ not (
+ instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
+ ) and
+ not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
+ message =
+ "Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
+ "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
/**
* Holds if instruction `instr` has multiple operands with tag `tag`.
*/
query predicate duplicateOperand(
- Instruction instr, string message, IRFunction func, string funcText
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(OperandTag tag, int operandCount |
operandCount =
@@ -58,8 +134,7 @@ module InstructionConsistency {
message =
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
" in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
@@ -67,100 +142,136 @@ module InstructionConsistency {
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
* the predecessor block `pred`.
*/
- query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
- pred = instr.getBlock().getAPredecessor() and
- not exists(PhiInputOperand operand |
- operand = instr.getAnOperand() and
- operand.getPredecessorBlock() = pred
+ query predicate missingPhiOperand(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRBlock pred |
+ pred = instr.getBlock().getAPredecessor() and
+ not exists(PhiInputOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getPredecessorBlock() = pred
+ ) and
+ message =
+ "Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
+ pred.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
- query predicate missingOperandType(Operand operand, string message) {
- exists(Language::Function func, Instruction use |
+ query predicate missingOperandType(
+ Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Instruction use |
not exists(operand.getType()) and
use = operand.getUse() and
- func = use.getEnclosingFunction() and
message =
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
- "' missing type in function '" + Language::getIdentityString(func) + "'."
+ "' is missing a type in function '$@'." and
+ irFunc = getOperandIRFunction(operand, irFuncText)
)
}
query predicate duplicateChiOperand(
- ChiInstruction chi, string message, IRFunction func, string funcText
+ ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
) {
chi.getTotal() = chi.getPartial() and
message =
"Chi instruction for " + chi.getPartial().toString() +
- " has duplicate operands in function $@" and
- func = chi.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ " has duplicate operands in function '$@'." and
+ irFunc = getInstructionIRFunction(chi, irFuncText)
}
query predicate sideEffectWithoutPrimary(
- SideEffectInstruction instr, string message, IRFunction func, string funcText
+ SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
not exists(instr.getPrimaryInstruction()) and
- message = "Side effect instruction missing primary instruction in function $@" and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ message =
+ "Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
/**
* Holds if an instruction, other than `ExitFunction`, has no successors.
*/
- query predicate instructionWithoutSuccessor(Instruction instr) {
+ query predicate instructionWithoutSuccessor(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
not exists(instr.getASuccessor()) and
not instr instanceof ExitFunctionInstruction and
// Phi instructions aren't linked into the instruction-level flow graph.
not instr instanceof PhiInstruction and
- not instr instanceof UnreachedInstruction
+ not instr instanceof UnreachedInstruction and
+ message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
/**
- * Holds if there are multiple (`n`) edges of kind `kind` from `source`,
- * where `target` is among the targets of those edges.
+ * Holds if there are multiple edges of the same kind from `source`.
*/
- query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
- n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
- n > 1 and
- source.getSuccessor(kind) = target
+ query predicate ambiguousSuccessors(
+ Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(EdgeKind kind, int n |
+ n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
+ n > 1 and
+ message =
+ "Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
+ kind.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(source, irFuncText)
+ )
}
/**
- * Holds if `instr` in `f` is part of a loop even though the AST of `f`
+ * Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
* contains no element that can cause loops.
*/
- query predicate unexplainedLoop(Language::Function f, Instruction instr) {
- exists(IRBlock block |
- instr.getBlock() = block and
- block.getEnclosingFunction() = f and
- block.getASuccessor+() = block
- ) and
- not Language::hasPotentialLoop(f)
+ query predicate unexplainedLoop(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Language::Function f |
+ exists(IRBlock block |
+ instr.getBlock() = block and
+ block.getEnclosingFunction() = f and
+ block.getASuccessor+() = block
+ ) and
+ not Language::hasPotentialLoop(f) and
+ message =
+ "Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
/**
* Holds if a `Phi` instruction is present in a block with fewer than two
* predecessors.
*/
- query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
- count(instr.getBlock().getAPredecessor()) < 2
+ query predicate unnecessaryPhiInstruction(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int n |
+ n = count(instr.getBlock().getAPredecessor()) and
+ n < 2 and
+ message =
+ "Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
+ " predecessors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
/**
* Holds if a memory operand is connected to a definition with an unmodeled result.
*/
query predicate memoryOperandDefinitionIsUnmodeled(
- Instruction instr, string message, IRFunction func, string funcText
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(MemoryOperand operand, Instruction def |
operand = instr.getAnOperand() and
def = operand.getAnyDef() and
not def.isResultModeled() and
- message = "Memory operand definition has unmodeled result in function '$@'" and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ message =
+ "Memory operand definition on instruction '" + instr.toString() +
+ "' has unmodeled result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
@@ -168,18 +279,37 @@ module InstructionConsistency {
* Holds if operand `operand` consumes a value that was defined in
* a different function.
*/
- query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
- operand.getUse() = instr and
- operand.getAnyDef() = defInstr and
- instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
+ query predicate operandAcrossFunctions(
+ Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
+ OptionalIRFunction defIRFunc, string defIRFuncText
+ ) {
+ exists(Instruction useInstr, Instruction defInstr |
+ operand.getUse() = useInstr and
+ operand.getAnyDef() = defInstr and
+ useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
+ defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
+ useIRFunc != defIRFunc and
+ message =
+ "Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
+ "' in function '$@', but is defined on instruction '" + defInstr.toString() +
+ "' in function '$@'."
+ )
}
/**
* Holds if instruction `instr` is not in exactly one block.
*/
- query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
- blockCount = count(instr.getBlock()) and
- blockCount != 1
+ query predicate instructionWithoutUniqueBlock(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int blockCount |
+ blockCount = count(instr.getBlock()) and
+ blockCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
+ " blocks in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
@@ -192,10 +322,11 @@ module InstructionConsistency {
*
* This check ensures we don't have too _few_ back edges.
*/
- query predicate containsLoopOfForwardEdges(IRFunction f) {
+ query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
exists(IRBlock block |
forwardEdge+(block, block) and
- block.getEnclosingIRFunction() = f
+ block.getEnclosingIRFunction() = f and
+ message = "Function contains a loop consisting of only forward edges."
)
}
@@ -207,12 +338,19 @@ module InstructionConsistency {
*
* This check ensures we don't have too _many_ back edges.
*/
- query predicate lostReachability(IRBlock block) {
+ query predicate lostReachability(
+ IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
exists(IRFunction f, IRBlock entry |
entry = f.getEntryBlock() and
entry.getASuccessor+() = block and
not forwardEdge+(entry, block) and
- not Language::hasGoto(f.getFunction())
+ not Language::hasGoto(f.getFunction()) and
+ message =
+ "Block '" + block.toString() +
+ "' is not reachable by traversing only forward edges in function '$@'." and
+ irFunc = TPresentIRFunction(f) and
+ irFuncText = irFunc.toString()
)
}
@@ -220,16 +358,22 @@ module InstructionConsistency {
* Holds if the number of back edges differs between the `Instruction` graph
* and the `IRBlock` graph.
*/
- query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
- fromInstr =
- count(Instruction i1, Instruction i2 |
- i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
- ) and
- fromBlock =
- count(IRBlock b1, IRBlock b2 |
- b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
- ) and
- fromInstr != fromBlock
+ query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
+ exists(int fromInstr, int fromBlock |
+ fromInstr =
+ count(Instruction i1, Instruction i2 |
+ getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
+ ) and
+ fromBlock =
+ count(IRBlock b1, IRBlock b2 |
+ getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
+ ) and
+ fromInstr != fromBlock and
+ message =
+ "The instruction graph for function '" + irFunc.toString() + "' contains " +
+ fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
+ + " back edges."
+ )
}
/**
@@ -251,7 +395,7 @@ module InstructionConsistency {
* Holds if `useOperand` has a definition that does not dominate the use.
*/
query predicate useNotDominatedByDefinition(
- Operand useOperand, string message, IRFunction func, string funcText
+ Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
pointOfEvaluation(useOperand, useBlock, useIndex) and
@@ -272,19 +416,17 @@ module InstructionConsistency {
message =
"Operand '" + useOperand.toString() +
"' is not dominated by its definition in function '$@'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
)
}
query predicate switchInstructionWithoutDefaultEdge(
- SwitchInstruction switchInstr, string message, IRFunction func, string funcText
+ SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
) {
not exists(switchInstr.getDefaultSuccessor()) and
message =
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
- func = switchInstr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getInstructionIRFunction(switchInstr, irFuncText)
}
/**
@@ -305,18 +447,30 @@ module InstructionConsistency {
instr.getOpcode() instanceof Opcode::InitializeNonLocal
}
- query predicate notMarkedAsConflated(Instruction instr) {
+ query predicate notMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
shouldBeConflated(instr) and
- not instr.isResultConflated()
+ not instr.isResultConflated() and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
- query predicate wronglyMarkedAsConflated(Instruction instr) {
+ query predicate wronglyMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
instr.isResultConflated() and
- not shouldBeConflated(instr)
+ not shouldBeConflated(instr) and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should not be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
query predicate invalidOverlap(
- MemoryOperand useOperand, string message, IRFunction func, string funcText
+ MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(Overlap overlap |
overlap = useOperand.getDefinitionOverlap() and
@@ -324,8 +478,20 @@ module InstructionConsistency {
message =
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
overlap.toString() + "'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
+ )
+ }
+
+ query predicate nonUniqueEnclosingIRFunction(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int irFuncCount |
+ irFuncCount = count(instr.getEnclosingIRFunction()) and
+ irFuncCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
+ " results for `getEnclosingIRFunction()` in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll
index 9aea3e00d666..5968e58f90bf 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll
@@ -1,29 +1,17 @@
+/**
+ * Provides the class `IRFunction`, which represents the Intermediate Representation for the
+ * definition of a function.
+ */
+
private import internal.IRInternal
+private import internal.IRFunctionImports as Imports
+import Imports::IRFunctionBase
import Instruction
-private newtype TIRFunction =
- MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
-
/**
- * Represents the IR for a function.
+ * The IR for a function.
*/
-class IRFunction extends TIRFunction {
- Language::Function func;
-
- IRFunction() { this = MkIRFunction(func) }
-
- final string toString() { result = "IR: " + func.toString() }
-
- /**
- * Gets the function whose IR is represented.
- */
- final Language::Function getFunction() { result = func }
-
- /**
- * Gets the location of the function.
- */
- final Language::Location getLocation() { result = func.getLocation() }
-
+class IRFunction extends IRFunctionBase {
/**
* Gets the entry point for this function.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll
index 9f2a0d4ea281..146fc2707383 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes that represent variables accessed by the IR.
+ */
+
private import internal.IRInternal
import IRFunction
private import internal.IRVariableImports as Imports
@@ -7,15 +11,11 @@ private import Imports::TTempVariableTag
private import Imports::TIRVariable
private import Imports::IRType
-IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
- result.getVariable() = var and
- result.getEnclosingFunction() = func
-}
-
/**
- * A variable referenced by the IR for a function. The variable may be a user-declared variable
- * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation
- * (`IRTempVariable`).
+ * A variable referenced by the IR for a function.
+ *
+ * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
+ * by the AST-to-IR translation (`IRTempVariable`).
*/
class IRVariable extends TIRVariable {
Language::Function func;
@@ -27,6 +27,7 @@ class IRVariable extends TIRVariable {
this = TIRDynamicInitializationFlag(func, _, _)
}
+ /** Gets a textual representation of this element. */
string toString() { none() }
/**
@@ -162,20 +163,30 @@ class IRGeneratedVariable extends IRVariable {
override string getUniqueId() { none() }
+ /**
+ * INTERNAL: Do not use.
+ *
+ * Gets a string containing the source code location of the AST that generated this variable.
+ *
+ * This is used by debugging and printing code only.
+ */
final string getLocationString() {
result =
ast.getLocation().getStartLine().toString() + ":" +
ast.getLocation().getStartColumn().toString()
}
+ /**
+ * INTERNAL: Do not use.
+ *
+ * Gets the string that is combined with the location of the variable to generate the string
+ * representation of this variable.
+ *
+ * This is used by debugging and printing code only.
+ */
string getBaseString() { none() }
}
-IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
- result.getAST() = ast and
- result.getTag() = tag
-}
-
/**
* A temporary variable introduced by IR construction. The most common examples are the variable
* generated to hold the return value of a function, or the variable generated to hold the result of
@@ -190,6 +201,10 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa
result = "Temp: " + Construction::getTempVariableUniqueId(this)
}
+ /**
+ * Gets the "tag" object that differentiates this temporary variable from other temporary
+ * variables generated for the same AST.
+ */
final TempVariableTag getTag() { result = tag }
override string getBaseString() { result = "#temp" }
@@ -217,19 +232,23 @@ class IRThrowVariable extends IRTempVariable {
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
-class IREllipsisVariable extends IRTempVariable {
+class IREllipsisVariable extends IRTempVariable, IRParameter {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
+
+ final override int getIndex() { result = func.getNumberOfParameters() }
}
/**
* A temporary variable generated to hold the `this` pointer.
*/
-class IRThisVariable extends IRTempVariable {
+class IRThisVariable extends IRTempVariable, IRParameter {
IRThisVariable() { tag = ThisTempVar() }
final override string toString() { result = "#this" }
+
+ final override int getIndex() { result = -1 }
}
/**
@@ -249,6 +268,9 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
final override string getBaseString() { result = "#string" }
+ /**
+ * Gets the AST of the string literal represented by this `IRStringLiteral`.
+ */
final Language::StringLiteral getLiteral() { result = literal }
}
@@ -266,6 +288,9 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
final override string toString() { result = var.toString() + "#init" }
+ /**
+ * Gets variable whose initialization is guarded by this flag.
+ */
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
@@ -274,3 +299,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}
+
+/**
+ * An IR variable which acts like a function parameter, including positional parameters and the
+ * temporary variables generated for `this` and ellipsis parameters.
+ */
+class IRParameter extends IRAutomaticVariable {
+ IRParameter() {
+ this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
+ or
+ this = TIRTempVariable(_, _, ThisTempVar(), _)
+ or
+ this = TIRTempVariable(_, _, EllipsisTempVar(), _)
+ }
+
+ /**
+ * Gets the zero-based index of this parameter. The `this` parameter has index -1.
+ */
+ int getIndex() { none() }
+}
+
+/**
+ * An IR variable representing a positional parameter.
+ */
+class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
+ final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
index 9c83a3d99f0c..620b23b942e0 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes that represent the individual instructions in the IR for a function.
+ */
+
private import internal.IRInternal
import IRFunction
import IRBlock
@@ -27,9 +31,16 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
}
/**
- * Represents a single operation in the IR.
+ * A single instruction in the IR.
*/
-class Instruction extends Construction::TInstruction {
+class Instruction extends Construction::TStageInstruction {
+ Instruction() {
+ // The base `TStageInstruction` type is a superset of the actual instructions appearing in this
+ // stage. This call lets the stage filter out the ones that are not reused from raw IR.
+ Construction::hasInstruction(this)
+ }
+
+ /** Gets a textual representation of this element. */
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
/**
@@ -194,16 +205,25 @@ class Instruction extends Construction::TInstruction {
* conversion.
*/
final Language::Expr getConvertedResultExpression() {
- result = Construction::getInstructionConvertedResultExpression(this)
+ result = Raw::getInstructionConvertedResultExpression(this)
}
/**
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
*/
final Language::Expr getUnconvertedResultExpression() {
- result = Construction::getInstructionUnconvertedResultExpression(this)
+ result = Raw::getInstructionUnconvertedResultExpression(this)
}
+ /**
+ * Gets the language-specific type of the result produced by this instruction.
+ *
+ * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a
+ * less complex, language-neutral type system in which all semantically equivalent types share the
+ * same `IRType` instance. For example, in C++, four different `Instruction`s might have three
+ * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`,
+ * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`.
+ */
final Language::LanguageType getResultLanguageType() {
result = Construction::getInstructionResultType(this)
}
@@ -212,6 +232,7 @@ class Instruction extends Construction::TInstruction {
* Gets the type of the result produced by this instruction. If the instruction does not produce
* a result, its result type will be `IRVoidType`.
*/
+ cached
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
/**
@@ -240,17 +261,19 @@ class Instruction extends Construction::TInstruction {
* given by `getResultType()`.
*
* For example, the statement `y = x;` generates the following IR:
+ * ```
* r1_0(glval: int) = VariableAddress[x]
* r1_1(int) = Load r1_0, mu0_1
* r1_2(glval: int) = VariableAddress[y]
* mu1_3(int) = Store r1_2, r1_1
+ * ```
*
* The result of each `VariableAddress` instruction is a glvalue of type
* `int`, representing the address of the corresponding integer variable. The
* result of the `Load` instruction is a prvalue of type `int`, representing
* the integer value loaded from variable `x`.
*/
- final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
+ final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
/**
* Gets the size of the result produced by this instruction, in bytes. If the
@@ -259,7 +282,7 @@ class Instruction extends Construction::TInstruction {
* If `this.isGLValue()` holds for this instruction, the value of
* `getResultSize()` will always be the size of a pointer.
*/
- final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
+ final int getResultSize() { result = getResultLanguageType().getByteSize() }
/**
* Gets the opcode that specifies the operation performed by this instruction.
@@ -392,13 +415,27 @@ class Instruction extends Construction::TInstruction {
final Instruction getAPredecessor() { result = getPredecessor(_) }
}
+/**
+ * An instruction that refers to a variable.
+ *
+ * This class is used for any instruction whose operation fundamentally depends on a specific
+ * variable. For example, it is used for `VariableAddress`, which returns the address of a specific
+ * variable, and `InitializeParameter`, which returns the value that was passed to the specified
+ * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions
+ * that happen to load from or store to a particular variable; in those cases, the memory location
+ * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be
+ * defined by the result of a `VariableAddress` instruction.
+ */
class VariableInstruction extends Instruction {
IRVariable var;
- VariableInstruction() { var = Construction::getInstructionVariable(this) }
+ VariableInstruction() { var = Raw::getInstructionVariable(this) }
override string getImmediateString() { result = var.toString() }
+ /**
+ * Gets the variable that this instruction references.
+ */
final IRVariable getIRVariable() { result = var }
/**
@@ -407,63 +444,156 @@ class VariableInstruction extends Instruction {
final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
}
+/**
+ * An instruction that refers to a field of a class, struct, or union.
+ *
+ * This class is used for any instruction whose operation fundamentally depends on a specific
+ * field. For example, it is used for `FieldAddress`, which computes the address of a specific
+ * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen
+ * to load from or store to a particular field; in those cases, the memory location being accessed
+ * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the
+ * result of a `FieldAddress` instruction.
+ */
class FieldInstruction extends Instruction {
Language::Field field;
- FieldInstruction() { field = Construction::getInstructionField(this) }
+ FieldInstruction() { field = Raw::getInstructionField(this) }
final override string getImmediateString() { result = field.toString() }
+ /**
+ * Gets the field that this instruction references.
+ */
final Language::Field getField() { result = field }
}
+/**
+ * An instruction that refers to a function.
+ *
+ * This class is used for any instruction whose operation fundamentally depends on a specific
+ * function. For example, it is used for `FunctionAddress`, which returns the address of a specific
+ * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a
+ * particular function; in that case, the function being called is specified by the
+ * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a
+ * `FunctionAddress` instruction.
+ */
class FunctionInstruction extends Instruction {
Language::Function funcSymbol;
- FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
+ FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
final override string getImmediateString() { result = funcSymbol.toString() }
+ /**
+ * Gets the function that this instruction references.
+ */
final Language::Function getFunctionSymbol() { result = funcSymbol }
}
+/**
+ * An instruction whose result is a compile-time constant value.
+ */
class ConstantValueInstruction extends Instruction {
string value;
- ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
+ ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
final override string getImmediateString() { result = value }
+ /**
+ * Gets the constant value of this instruction's result.
+ */
final string getValue() { result = value }
}
+/**
+ * An instruction that refers to an argument of a `Call` instruction.
+ *
+ * This instruction is used for side effects of a `Call` instruction that read or write memory
+ * pointed to by one of the arguments of the call.
+ */
class IndexedInstruction extends Instruction {
int index;
- IndexedInstruction() { index = Construction::getInstructionIndex(this) }
+ IndexedInstruction() { index = Raw::getInstructionIndex(this) }
final override string getImmediateString() { result = index.toString() }
+ /**
+ * Gets the zero-based index of the argument that this instruction references.
+ */
final int getIndex() { result = index }
}
+/**
+ * An instruction representing the entry point to a function.
+ *
+ * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins
+ * at this instruction. This instruction has no predecessors.
+ */
class EnterFunctionInstruction extends Instruction {
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
}
+/**
+ * An instruction that returns the address of a variable.
+ *
+ * This instruction returns the address of a local variable, parameter, static field,
+ * namespace-scope variable, or global variable. For the address of a non-static field of a class,
+ * struct, or union, see `FieldAddressInstruction`.
+ */
class VariableAddressInstruction extends VariableInstruction {
VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress }
}
+/**
+ * An instruction that returns the address of a function.
+ *
+ * This instruction returns the address of a function, including non-member functions, static member
+ * functions, and non-static member functions.
+ *
+ * The result has an `IRFunctionAddress` type.
+ */
+class FunctionAddressInstruction extends FunctionInstruction {
+ FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress }
+}
+
+/**
+ * An instruction that initializes a parameter of the enclosing function with the value of the
+ * corresponding argument passed by the caller.
+ *
+ * Each parameter of a function will have exactly one `InitializeParameter` instruction that
+ * initializes that parameter.
+ */
class InitializeParameterInstruction extends VariableInstruction {
InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter }
+ /**
+ * Gets the parameter initialized by this instruction.
+ */
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
}
+/**
+ * An instruction that initializes all memory that existed before this function was called.
+ *
+ * This instruction provides a definition for memory that, because it was actually allocated and
+ * initialized elsewhere, would not otherwise have a definition in this function.
+ */
+class InitializeNonLocalInstruction extends Instruction {
+ InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal }
+}
+
+/**
+ * An instruction that initializes the memory pointed to by a parameter of the enclosing function
+ * with the value of that memory on entry to the function.
+ */
class InitializeIndirectionInstruction extends VariableInstruction {
InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection }
+ /**
+ * Gets the parameter initialized by this instruction.
+ */
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
}
@@ -474,14 +604,42 @@ class InitializeThisInstruction extends Instruction {
InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis }
}
+/**
+ * An instruction that computes the address of a non-static field of an object.
+ */
class FieldAddressInstruction extends FieldInstruction {
FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress }
+ /**
+ * Gets the operand that provides the address of the object containing the field.
+ */
final UnaryOperand getObjectAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the object containing the field.
+ */
final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() }
}
+/**
+ * An instruction that computes the address of the first element of a managed array.
+ *
+ * This instruction is used for element access to C# arrays.
+ */
+class ElementsAddressInstruction extends UnaryInstruction {
+ ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress }
+
+ /**
+ * Gets the operand that provides the address of the array object.
+ */
+ final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the address of the array object.
+ */
+ final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() }
+}
+
/**
* An instruction that produces a well-defined but unknown result and has
* unknown side effects, including side effects that are not conservatively
@@ -496,6 +654,12 @@ class ErrorInstruction extends Instruction {
ErrorInstruction() { getOpcode() instanceof Opcode::Error }
}
+/**
+ * An instruction that returns an uninitialized value.
+ *
+ * This instruction is used to provide an initial definition for a stack variable that does not have
+ * an initializer, or whose initializer only partially initializes the variable.
+ */
class UninitializedInstruction extends VariableInstruction {
UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized }
@@ -505,35 +669,94 @@ class UninitializedInstruction extends VariableInstruction {
final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() }
}
+/**
+ * An instruction that has no effect.
+ *
+ * This instruction is typically inserted to ensure that a particular AST is associated with at
+ * least one instruction, even when the AST has no semantic effect.
+ */
class NoOpInstruction extends Instruction {
NoOpInstruction() { getOpcode() instanceof Opcode::NoOp }
}
+/**
+ * An instruction that returns control to the caller of the function.
+ *
+ * This instruction represents the normal (non-exception) return from a function, either from an
+ * explicit `return` statement or from control flow reaching the end of the function's body.
+ *
+ * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is
+ * represented as an initialization of the temporary variable that holds the return value, with
+ * control then flowing to the common `ReturnInstruction` for that function. Exception: A function
+ * that never returns will not have a `ReturnInstruction`.
+ *
+ * The `ReturnInstruction` for a function will have a control-flow successor edge to a block
+ * containing the `ExitFunction` instruction for that function.
+ *
+ * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from
+ * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a
+ * `void`-returning function.
+ */
class ReturnInstruction extends Instruction {
ReturnInstruction() { getOpcode() instanceof ReturnOpcode }
}
+/**
+ * An instruction that returns control to the caller of the function, without returning a value.
+ */
class ReturnVoidInstruction extends ReturnInstruction {
ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid }
}
+/**
+ * An instruction that returns control to the caller of the function, including a return value.
+ */
class ReturnValueInstruction extends ReturnInstruction {
ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue }
+ /**
+ * Gets the operand that provides the value being returned by the function.
+ */
final LoadOperand getReturnValueOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the value being returned by the function, if an
+ * exact definition is available.
+ */
final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
}
+/**
+ * An instruction that represents the use of the value pointed to by a parameter of the function
+ * after the function returns control to its caller.
+ *
+ * This instruction does not itself return control to the caller. It merely represents the potential
+ * for a caller to use the memory pointed to by the parameter sometime after the call returns. This
+ * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility
+ * that the caller initialized the memory pointed to by the parameter before the call.
+ */
class ReturnIndirectionInstruction extends VariableInstruction {
ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection }
+ /**
+ * Gets the operand that provides the value of the pointed-to memory.
+ */
final SideEffectOperand getSideEffectOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the value of the pointed-to memory, if an exact
+ * definition is available.
+ */
final Instruction getSideEffect() { result = getSideEffectOperand().getDef() }
+ /**
+ * Gets the operand that provides the address of the pointed-to memory.
+ */
final AddressOperand getSourceAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the pointed-to memory.
+ */
final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() }
/**
@@ -541,87 +764,191 @@ class ReturnIndirectionInstruction extends VariableInstruction {
* function.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
+
+ /**
+ * Holds if this instruction is the return indirection for `this`.
+ */
+ final predicate isThisIndirection() { var instanceof IRThisVariable }
}
+/**
+ * An instruction that returns a copy of its operand.
+ *
+ * There are several different copy instructions, depending on the source and destination of the
+ * copy operation:
+ * - `CopyInstruction` - Copies a register operand to a register result.
+ * - `LoadInstruction` - Copies a memory operand to a register result.
+ * - `StoreInstruction` - Copies a register operand to a memory result.
+ */
class CopyInstruction extends Instruction {
CopyInstruction() { getOpcode() instanceof CopyOpcode }
+ /**
+ * Gets the operand that provides the input value of the copy.
+ */
Operand getSourceValueOperand() { none() }
+ /**
+ * Gets the instruction whose result provides the input value of the copy, if an exact definition
+ * is available.
+ */
final Instruction getSourceValue() { result = getSourceValueOperand().getDef() }
}
+/**
+ * An instruction that returns a register result containing a copy of its register operand.
+ */
class CopyValueInstruction extends CopyInstruction, UnaryInstruction {
CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue }
final override UnaryOperand getSourceValueOperand() { result = getAnOperand() }
}
+/**
+ * An instruction that returns a register result containing a copy of its memory operand.
+ */
class LoadInstruction extends CopyInstruction {
LoadInstruction() { getOpcode() instanceof Opcode::Load }
+ /**
+ * Gets the operand that provides the address of the value being loaded.
+ */
final AddressOperand getSourceAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the value being loaded.
+ */
final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() }
final override LoadOperand getSourceValueOperand() { result = getAnOperand() }
}
+/**
+ * An instruction that returns a memory result containing a copy of its register operand.
+ */
class StoreInstruction extends CopyInstruction {
StoreInstruction() { getOpcode() instanceof Opcode::Store }
+ /**
+ * Gets the operand that provides the address of the location to which the value will be stored.
+ */
final AddressOperand getDestinationAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the location to which the value will
+ * be stored, if an exact definition is available.
+ */
final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() }
final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() }
}
+/**
+ * An instruction that branches to one of two successor instructions based on the value of a Boolean
+ * operand.
+ */
class ConditionalBranchInstruction extends Instruction {
ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch }
+ /**
+ * Gets the operand that provides the Boolean condition controlling the branch.
+ */
final ConditionOperand getConditionOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the Boolean condition controlling the branch.
+ */
final Instruction getCondition() { result = getConditionOperand().getDef() }
+ /**
+ * Gets the instruction to which control will flow if the condition is true.
+ */
final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) }
+ /**
+ * Gets the instruction to which control will flow if the condition is false.
+ */
final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) }
}
+/**
+ * An instruction representing the exit point of a function.
+ *
+ * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns
+ * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns
+ * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no
+ * successors.
+ */
class ExitFunctionInstruction extends Instruction {
ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction }
}
+/**
+ * An instruction whose result is a constant value.
+ */
class ConstantInstruction extends ConstantValueInstruction {
ConstantInstruction() { getOpcode() instanceof Opcode::Constant }
}
+/**
+ * An instruction whose result is a constant value of integer or Boolean type.
+ */
class IntegerConstantInstruction extends ConstantInstruction {
- IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
+ IntegerConstantInstruction() {
+ exists(IRType resultType |
+ resultType = getResultIRType() and
+ (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
+ )
+ }
}
+/**
+ * An instruction whose result is a constant value of floating-point type.
+ */
class FloatConstantInstruction extends ConstantInstruction {
- FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
+ FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
}
+/**
+ * An instruction whose result is the address of a string literal.
+ */
class StringConstantInstruction extends VariableInstruction {
override IRStringLiteral var;
final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) }
+ /**
+ * Gets the string literal whose address is returned by this instruction.
+ */
final Language::StringLiteral getValue() { result = var.getLiteral() }
}
+/**
+ * An instruction whose result is computed from two operands.
+ */
class BinaryInstruction extends Instruction {
BinaryInstruction() { getOpcode() instanceof BinaryOpcode }
+ /**
+ * Gets the left operand of this binary instruction.
+ */
final LeftOperand getLeftOperand() { result = getAnOperand() }
+ /**
+ * Gets the right operand of this binary instruction.
+ */
final RightOperand getRightOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the value of the left operand of this binary
+ * instruction.
+ */
final Instruction getLeft() { result = getLeftOperand().getDef() }
+ /**
+ * Gets the instruction whose result provides the value of the right operand of this binary
+ * instruction.
+ */
final Instruction getRight() { result = getRightOperand().getDef() }
/**
@@ -634,121 +961,301 @@ class BinaryInstruction extends Instruction {
}
}
+/**
+ * An instruction that computes the result of an arithmetic operation.
+ */
class ArithmeticInstruction extends Instruction {
ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode }
}
+/**
+ * An instruction that performs an arithmetic operation on two numeric operands.
+ */
class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { }
+/**
+ * An instruction whose result is computed by performing an arithmetic operation on a single
+ * numeric operand.
+ */
class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { }
+/**
+ * An instruction that computes the sum of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is
+ * performed according to IEEE-754.
+ */
class AddInstruction extends BinaryArithmeticInstruction {
AddInstruction() { getOpcode() instanceof Opcode::Add }
}
+/**
+ * An instruction that computes the difference of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed
+ * according to IEEE-754.
+ */
class SubInstruction extends BinaryArithmeticInstruction {
SubInstruction() { getOpcode() instanceof Opcode::Sub }
}
+/**
+ * An instruction that computes the product of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is
+ * performed according to IEEE-754.
+ */
class MulInstruction extends BinaryArithmeticInstruction {
MulInstruction() { getOpcode() instanceof Opcode::Mul }
}
+/**
+ * An instruction that computes the quotient of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * division by zero or integer overflow is undefined. Floating-point division is performed according
+ * to IEEE-754.
+ */
class DivInstruction extends BinaryArithmeticInstruction {
DivInstruction() { getOpcode() instanceof Opcode::Div }
}
+/**
+ * An instruction that computes the remainder of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type. The result of
+ * division by zero or integer overflow is undefined.
+ */
class RemInstruction extends BinaryArithmeticInstruction {
RemInstruction() { getOpcode() instanceof Opcode::Rem }
}
+/**
+ * An instruction that negates a single numeric operand.
+ *
+ * The operand must have a numeric type, which will also be the result type. The result of integer
+ * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation
+ * is performed according to IEEE-754.
+ */
class NegateInstruction extends UnaryArithmeticInstruction {
NegateInstruction() { getOpcode() instanceof Opcode::Negate }
}
+/**
+ * An instruction that computes the result of a bitwise operation.
+ */
class BitwiseInstruction extends Instruction {
BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode }
}
+/**
+ * An instruction that performs a bitwise operation on two integer operands.
+ */
class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { }
+/**
+ * An instruction that performs a bitwise operation on a single integer operand.
+ */
class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { }
+/**
+ * An instruction that computes the bitwise "and" of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type.
+ */
class BitAndInstruction extends BinaryBitwiseInstruction {
BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd }
}
+/**
+ * An instruction that computes the bitwise "or" of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type.
+ */
class BitOrInstruction extends BinaryBitwiseInstruction {
BitOrInstruction() { getOpcode() instanceof Opcode::BitOr }
}
+/**
+ * An instruction that computes the bitwise "xor" of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type.
+ */
class BitXorInstruction extends BinaryBitwiseInstruction {
BitXorInstruction() { getOpcode() instanceof Opcode::BitXor }
}
+/**
+ * An instruction that shifts its left operand to the left by the number of bits specified by its
+ * right operand.
+ *
+ * Both operands must have an integer type. The result has the same type as the left operand. The
+ * rightmost bits are zero-filled.
+ */
class ShiftLeftInstruction extends BinaryBitwiseInstruction {
ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft }
}
+/**
+ * An instruction that shifts its left operand to the right by the number of bits specified by its
+ * right operand.
+ *
+ * Both operands must have an integer type. The result has the same type as the left operand. If the
+ * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand
+ * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit
+ * of the left operand.
+ */
class ShiftRightInstruction extends BinaryBitwiseInstruction {
ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight }
}
+/**
+ * An instruction that performs a binary arithmetic operation involving at least one pointer
+ * operand.
+ */
class PointerArithmeticInstruction extends BinaryInstruction {
int elementSize;
PointerArithmeticInstruction() {
getOpcode() instanceof PointerArithmeticOpcode and
- elementSize = Construction::getInstructionElementSize(this)
+ elementSize = Raw::getInstructionElementSize(this)
}
final override string getImmediateString() { result = elementSize.toString() }
+ /**
+ * Gets the size of the elements pointed to by the pointer operands, in bytes.
+ *
+ * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer
+ * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the
+ * element size to compute the actual number of bytes added to or subtracted from the pointer
+ * address. When computing the integer difference between two pointers (`PointerDiffInstruction`),
+ * the result is computed by computing the difference between the two pointer byte addresses, then
+ * dividing that byte count by the element size.
+ */
final int getElementSize() { result = elementSize }
}
+/**
+ * An instruction that adds or subtracts an integer offset from a pointer.
+ */
class PointerOffsetInstruction extends PointerArithmeticInstruction {
PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode }
}
+/**
+ * An instruction that adds an integer offset to a pointer.
+ *
+ * The result is the byte address computed by adding the value of the right (integer) operand,
+ * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer
+ * overflow is undefined.
+ */
class PointerAddInstruction extends PointerOffsetInstruction {
PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd }
}
+/**
+ * An instruction that subtracts an integer offset from a pointer.
+ *
+ * The result is the byte address computed by subtracting the value of the right (integer) operand,
+ * multiplied by the element size, from the value of the left (pointer) operand. The result of
+ * pointer underflow is undefined.
+ */
class PointerSubInstruction extends PointerOffsetInstruction {
PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub }
}
+/**
+ * An instruction that computes the difference between two pointers.
+ *
+ * Both operands must have the same pointer type. The result must have an integer type whose size is
+ * the same as that of the pointer operands. The result is computed by subtracting the byte address
+ * in the right operand from the byte address in the left operand, and dividing by the element size.
+ * If the difference in byte addresses is not divisible by the element size, the result is
+ * undefined.
+ */
class PointerDiffInstruction extends PointerArithmeticInstruction {
PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff }
}
+/**
+ * An instruction whose result is computed from a single operand.
+ */
class UnaryInstruction extends Instruction {
UnaryInstruction() { getOpcode() instanceof UnaryOpcode }
+ /**
+ * Gets the sole operand of this instruction.
+ */
final UnaryOperand getUnaryOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the sole operand of this instruction.
+ */
final Instruction getUnary() { result = getUnaryOperand().getDef() }
}
+/**
+ * An instruction that converts the value of its operand to a value of a different type.
+ */
class ConvertInstruction extends UnaryInstruction {
ConvertInstruction() { getOpcode() instanceof Opcode::Convert }
}
+/**
+ * An instruction that converts the address of a polymorphic object to the address of a different
+ * subobject of the same polymorphic object, returning a null address if the dynamic type of the
+ * object is not compatible with the result type.
+ *
+ * If the operand holds a null address, the result is a null address.
+ *
+ * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or
+ * `as` expression.
+ */
class CheckedConvertOrNullInstruction extends UnaryInstruction {
CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull }
}
/**
- * Represents an instruction that converts between two addresses
- * related by inheritance.
+ * An instruction that converts the address of a polymorphic object to the address of a different
+ * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object
+ * is not compatible with the result type.
+ *
+ * If the operand holds a null address, the result is a null address.
+ *
+ * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast
+ * expression.
+ */
+class CheckedConvertOrThrowInstruction extends UnaryInstruction {
+ CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow }
+}
+
+/**
+ * An instruction that returns the address of the complete object that contains the subobject
+ * pointed to by its operand.
+ *
+ * If the operand holds a null address, the result is a null address.
+ *
+ * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to
+ * the most-derived object.
+ */
+class CompleteObjectAddressInstruction extends UnaryInstruction {
+ CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress }
+}
+
+/**
+ * An instruction that converts the address of an object to the address of a different subobject of
+ * the same object, without any type checking at runtime.
*/
class InheritanceConversionInstruction extends UnaryInstruction {
Language::Class baseClass;
Language::Class derivedClass;
InheritanceConversionInstruction() {
- Construction::getInstructionInheritance(this, baseClass, derivedClass)
+ Raw::getInstructionInheritance(this, baseClass, derivedClass)
}
final override string getImmediateString() {
@@ -778,59 +1285,91 @@ class InheritanceConversionInstruction extends UnaryInstruction {
}
/**
- * Represents an instruction that converts from the address of a derived class
- * to the address of a base class.
+ * An instruction that converts from the address of a derived class to the address of a base class.
*/
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode }
}
/**
- * Represents an instruction that converts from the address of a derived class
- * to the address of a direct non-virtual base class.
+ * An instruction that converts from the address of a derived class to the address of a direct
+ * non-virtual base class.
+ *
+ * If the operand holds a null address, the result is a null address.
*/
class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase }
}
/**
- * Represents an instruction that converts from the address of a derived class
- * to the address of a virtual base class.
+ * An instruction that converts from the address of a derived class to the address of a virtual base
+ * class.
+ *
+ * If the operand holds a null address, the result is a null address.
*/
class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
}
/**
- * Represents an instruction that converts from the address of a base class
- * to the address of a direct non-virtual derived class.
+ * An instruction that converts from the address of a base class to the address of a direct
+ * non-virtual derived class.
+ *
+ * If the operand holds a null address, the result is a null address.
*/
class ConvertToDerivedInstruction extends InheritanceConversionInstruction {
ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived }
}
+/**
+ * An instruction that computes the bitwise complement of its operand.
+ *
+ * The operand must have an integer type, which will also be the result type.
+ */
class BitComplementInstruction extends UnaryBitwiseInstruction {
BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement }
}
+/**
+ * An instruction that computes the logical complement of its operand.
+ *
+ * The operand must have a Boolean type, which will also be the result type.
+ */
class LogicalNotInstruction extends UnaryInstruction {
LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot }
}
+/**
+ * An instruction that compares two numeric operands.
+ */
class CompareInstruction extends BinaryInstruction {
CompareInstruction() { getOpcode() instanceof CompareOpcode }
}
+/**
+ * An instruction that returns a `true` result if its operands are equal.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are
+ * unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareEQInstruction extends CompareInstruction {
CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ }
}
+/**
+ * An instruction that returns a `true` result if its operands are not equal.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if `left != right` or if the two operands are unordered, and `false` if
+ * `left == right`. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareNEInstruction extends CompareInstruction {
CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE }
}
/**
- * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`.
+ * An instruction that does a relative comparison of two values, such as `<` or `>=`.
*/
class RelationalInstruction extends CompareInstruction {
RelationalInstruction() { getOpcode() instanceof RelationalOpcode }
@@ -857,6 +1396,13 @@ class RelationalInstruction extends CompareInstruction {
predicate isStrict() { none() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is less than its right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareLTInstruction extends RelationalInstruction {
CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT }
@@ -867,6 +1413,13 @@ class CompareLTInstruction extends RelationalInstruction {
override predicate isStrict() { any() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is greater than its right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareGTInstruction extends RelationalInstruction {
CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT }
@@ -877,6 +1430,14 @@ class CompareGTInstruction extends RelationalInstruction {
override predicate isStrict() { any() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is less than or equal to its
+ * right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareLEInstruction extends RelationalInstruction {
CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE }
@@ -887,6 +1448,14 @@ class CompareLEInstruction extends RelationalInstruction {
override predicate isStrict() { none() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is greater than or equal to its
+ * right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareGEInstruction extends RelationalInstruction {
CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE }
@@ -897,15 +1466,32 @@ class CompareGEInstruction extends RelationalInstruction {
override predicate isStrict() { none() }
}
+/**
+ * An instruction that branches to one of multiple successor instructions based on the value of an
+ * integer operand.
+ *
+ * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each
+ * representing the branch that will be taken if the controlling expression is within the range
+ * specified for that case edge. The range of a case edge must be disjoint from the range of each
+ * other case edge.
+ *
+ * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`,
+ * representing the branch that will be taken if the controlling expression is not within the range
+ * of any case edge.
+ */
class SwitchInstruction extends Instruction {
SwitchInstruction() { getOpcode() instanceof Opcode::Switch }
+ /** Gets the operand that provides the integer value controlling the switch. */
final ConditionOperand getExpressionOperand() { result = getAnOperand() }
+ /** Gets the instruction whose result provides the integer value controlling the switch. */
final Instruction getExpression() { result = getExpressionOperand().getDef() }
+ /** Gets the successor instructions along the case edges of the switch. */
final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) }
+ /** Gets the successor instruction along the default edge of the switch, if any. */
final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) }
}
@@ -936,7 +1522,7 @@ class CallInstruction extends Instruction {
* Gets the `Function` that the call targets, if this is statically known.
*/
final Language::Function getStaticCallTarget() {
- result = getCallTarget().(FunctionInstruction).getFunctionSymbol()
+ result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol()
}
/**
@@ -981,6 +1567,9 @@ class CallInstruction extends Instruction {
class SideEffectInstruction extends Instruction {
SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode }
+ /**
+ * Gets the instruction whose execution causes this side effect.
+ */
final Instruction getPrimaryInstruction() {
result = Construction::getPrimaryInstructionForSideEffect(this)
}
@@ -996,9 +1585,10 @@ class CallSideEffectInstruction extends SideEffectInstruction {
/**
* An instruction representing the side effect of a function call on any memory
- * that might be read by that call. This instruction is emitted instead of
- * `CallSideEffectInstruction` when it's certain that the call target cannot
- * write to escaped memory.
+ * that might be read by that call.
+ *
+ * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the
+ * call target cannot write to escaped memory.
*/
class CallReadSideEffectInstruction extends SideEffectInstruction {
CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect }
@@ -1046,7 +1636,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction {
getOpcode() instanceof Opcode::SizedBufferReadSideEffect
}
- Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+ /**
+ * Gets the operand that holds the number of bytes read from the buffer.
+ */
+ final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the number of bytes read from the buffer.
+ */
+ final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() }
}
/**
@@ -1056,7 +1654,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction {
class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction {
WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
- Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
+ /**
+ * Get the operand that holds the address of the memory to be written.
+ */
+ final AddressOperand getDestinationAddressOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the address of the memory to be written.
+ */
+ Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() }
}
/**
@@ -1087,11 +1693,20 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
}
- Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+ /**
+ * Gets the operand that holds the number of bytes written to the buffer.
+ */
+ final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the number of bytes written to the buffer.
+ */
+ final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() }
}
/**
* An instruction representing the potential write of an indirect parameter within a function call.
+ *
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
* written.
*/
@@ -1103,6 +1718,7 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
/**
* An instruction representing the write of an indirect buffer parameter within a function call.
+ *
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
*/
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
@@ -1111,6 +1727,7 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
/**
* An instruction representing the write of an indirect buffer parameter within a function call.
+ *
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
*/
class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
@@ -1118,11 +1735,19 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
}
- Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+ /**
+ * Gets the operand that holds the number of bytes written to the buffer.
+ */
+ final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the number of bytes written to the buffer.
+ */
+ final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() }
}
/**
- * An instruction representing the initial value of newly allocated memory, e.g. the result of a
+ * An instruction representing the initial value of newly allocated memory, such as the result of a
* call to `malloc`.
*/
class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
@@ -1211,7 +1836,7 @@ class CatchByTypeInstruction extends CatchInstruction {
CatchByTypeInstruction() {
getOpcode() instanceof Opcode::CatchByType and
- exceptionType = Construction::getInstructionExceptionType(this)
+ exceptionType = Raw::getInstructionExceptionType(this)
}
final override string getImmediateString() { result = exceptionType.toString() }
@@ -1337,29 +1962,43 @@ class ChiInstruction extends Instruction {
* Gets the operand that represents the new value written by the memory write.
*/
final Instruction getPartial() { result = getPartialOperand().getDef() }
+
+ /**
+ * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand.
+ */
+ final predicate getUpdatedInterval(int startBit, int endBit) {
+ Construction::getIntervalUpdatedByChi(this, startBit, endBit)
+ }
}
/**
- * An instruction representing unreachable code. Inserted in place of the original target
- * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
- * infeasible.
+ * An instruction representing unreachable code.
+ *
+ * This instruction is inserted in place of the original target instruction of a `ConditionalBranch`
+ * or `Switch` instruction where that particular edge is infeasible.
*/
class UnreachedInstruction extends Instruction {
UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached }
}
/**
- * An instruction representing a built-in operation. This is used to represent
- * operations such as access to variable argument lists.
+ * An instruction representing a built-in operation.
+ *
+ * This is used to represent a variety of intrinsic operations provided by the compiler
+ * implementation, such as vector arithmetic.
*/
class BuiltInOperationInstruction extends Instruction {
Language::BuiltInOperation operation;
BuiltInOperationInstruction() {
getOpcode() instanceof BuiltInOperationOpcode and
- operation = Construction::getInstructionBuiltInOperation(this)
+ operation = Raw::getInstructionBuiltInOperation(this)
}
+ /**
+ * Gets the language-specific `BuiltInOperation` object that specifies the operation that is
+ * performed by this instruction.
+ */
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
}
@@ -1372,3 +2011,59 @@ class BuiltInInstruction extends BuiltInOperationInstruction {
final override string getImmediateString() { result = getBuiltInOperation().toString() }
}
+
+/**
+ * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter.
+ *
+ * The operand specifies the address of the `IREllipsisVariable` used to represent the `...`
+ * parameter. The result is a `va_list` that initially refers to the first argument that was passed
+ * to the `...` parameter.
+ */
+class VarArgsStartInstruction extends UnaryInstruction {
+ VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart }
+}
+
+/**
+ * An instruction that cleans up a `va_list` after it is no longer in use.
+ *
+ * The operand specifies the address of the `va_list` to clean up. This instruction does not return
+ * a result.
+ */
+class VarArgsEndInstruction extends UnaryInstruction {
+ VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd }
+}
+
+/**
+ * An instruction that returns the address of the argument currently pointed to by a `va_list`.
+ *
+ * The operand is the `va_list` that points to the argument. The result is the address of the
+ * argument.
+ */
+class VarArgInstruction extends UnaryInstruction {
+ VarArgInstruction() { getOpcode() instanceof Opcode::VarArg }
+}
+
+/**
+ * An instruction that modifies a `va_list` to point to the next argument that was passed to the
+ * `...` parameter.
+ *
+ * The operand is the current `va_list`. The result is an updated `va_list` that points to the next
+ * argument of the `...` parameter.
+ */
+class NextVarArgInstruction extends UnaryInstruction {
+ NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg }
+}
+
+/**
+ * An instruction that allocates a new object on the managed heap.
+ *
+ * This instruction is used to represent the allocation of a new object in C# using the `new`
+ * expression. This instruction does not invoke a constructor for the object. Instead, there will be
+ * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the
+ * result of the `NewObj` as the `this` argument.
+ *
+ * The result is the address of the newly allocated object.
+ */
+class NewObjInstruction extends Instruction {
+ NewObjInstruction() { getOpcode() instanceof Opcode::NewObj }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll
index f82704094c8e..a12e35d471b8 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes that represent the input values of IR instructions.
+ */
+
private import internal.IRInternal
private import Instruction
private import IRBlock
@@ -75,13 +79,21 @@ private PhiOperandBase phiOperand(
}
/**
- * A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
+ * An operand of an `Instruction`. The operand represents a use of the result of one instruction
+ * (the defining instruction) in another instruction (the use instruction)
*/
class Operand extends TOperand {
+ /** Gets a textual representation of this element. */
string toString() { result = "Operand" }
+ /**
+ * Gets the location of the source code for this operand.
+ */
final Language::Location getLocation() { result = getUse().getLocation() }
+ /**
+ * Gets the function that contains this operand.
+ */
final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() }
/**
@@ -139,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
@@ -268,8 +285,13 @@ 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() }
+ /**
+ * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`.
+ */
final OperandTag getOperandTag() { result = tag }
}
@@ -292,6 +314,9 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
}
}
+/**
+ * A memory operand other than the operand of a `Phi` instruction.
+ */
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
override MemoryOperandTag tag;
@@ -311,8 +336,19 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper
not Construction::isInCycle(useInstr) and
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
}
+
+ /**
+ * Holds if the operand totally overlaps with its definition and consumes the
+ * bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition.
+ */
+ predicate getUsedInterval(int startBitOffset, int endBitOffset) {
+ Construction::getUsedInterval(this, startBitOffset, endBitOffset)
+ }
}
+/**
+ * A memory operand whose type may be different from the type of the result of its definition.
+ */
class TypedOperand extends NonPhiMemoryOperand {
override TypedOperandTag tag;
@@ -416,6 +452,9 @@ class PositionalArgumentOperand extends ArgumentOperand {
final int getIndex() { result = tag.getArgIndex() }
}
+/**
+ * An operand representing memory read as a side effect of evaluating another instruction.
+ */
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
}
@@ -445,6 +484,8 @@ 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 d9c0df44e12e..59dadee71545 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll
@@ -1,3 +1,13 @@
+/**
+ * Outputs a representation of the IR as a control flow graph.
+ *
+ * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
+ * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
+ * uses, however, it is better to write a query that imports `PrintIR.qll`, extends
+ * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to
+ * dump.
+ */
+
private import internal.IRInternal
private import IR
private import internal.PrintIRImports as Imports
@@ -9,6 +19,7 @@ private newtype TPrintIRConfiguration = MkPrintIRConfiguration()
* The query can extend this class to control which functions are printed.
*/
class PrintIRConfiguration extends TPrintIRConfiguration {
+ /** Gets a textual representation of this configuration. */
string toString() { result = "PrintIRConfiguration" }
/**
@@ -39,6 +50,37 @@ 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
@@ -47,7 +89,7 @@ private newtype TPrintableIRNode =
/**
* A node to be emitted in the IR graph.
*/
-abstract class PrintableIRNode extends TPrintableIRNode {
+abstract private class PrintableIRNode extends TPrintableIRNode {
abstract string toString();
/**
@@ -98,7 +140,7 @@ abstract class PrintableIRNode extends TPrintableIRNode {
/**
* An IR graph node representing a `IRFunction` object.
*/
-class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction {
+private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction {
IRFunction irFunc;
PrintableIRFunction() { this = TPrintableIRFunction(irFunc) }
@@ -129,7 +171,7 @@ class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction {
/**
* An IR graph node representing an `IRBlock` object.
*/
-class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
+private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
IRBlock block;
PrintableIRBlock() { this = TPrintableIRBlock(block) }
@@ -161,7 +203,7 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
/**
* An IR graph node representing an `Instruction`.
*/
-class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
+private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
Instruction instr;
PrintableInstruction() { this = TPrintableInstruction(instr) }
@@ -179,7 +221,7 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
|
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()) + " = " +
@@ -199,6 +241,22 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
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) {
@@ -224,6 +282,9 @@ private string getPaddingString(int n) {
n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " "
}
+/**
+ * Holds if `node` belongs to the output graph, and its property `key` has the given `value`.
+ */
query predicate nodes(PrintableIRNode node, string key, string value) {
value = node.getProperty(key)
}
@@ -237,6 +298,10 @@ private int getSuccessorIndex(IRBlock pred, IRBlock succ) {
)
}
+/**
+ * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key`
+ * has the given `value`.
+ */
query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) {
exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock |
predBlock = pred.getBlock() and
@@ -256,6 +321,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key,
)
}
+/**
+ * Holds if `parent` is the parent node of `child` in the output graph.
+ */
query predicate parents(PrintableIRNode child, PrintableIRNode parent) {
parent = child.getParent()
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll
index 1612e0065b77..19fb0490f808 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll
@@ -196,16 +196,17 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) {
bitOffset = Ints::unknown()
}
-private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) {
+private predicate isArgumentForParameter(
+ CallInstruction ci, Operand operand, InitializeParameterInstruction init
+) {
exists(Language::Function f |
ci = operand.getUse() and
f = ci.getStaticCallTarget() and
(
- init.(InitializeParameterInstruction).getParameter() =
- f.getParameter(operand.(PositionalArgumentOperand).getIndex())
+ init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex())
or
- init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and
- init.getEnclosingFunction() = f and
+ init.getIRVariable() instanceof IRThisVariable and
+ unique( | | init.getEnclosingFunction()) = f and
operand instanceof ThisArgumentOperand
) and
not Language::isFunctionVirtual(f) and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll
index 9cd0384f924c..1e034051f05e 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll
@@ -133,6 +133,12 @@ abstract class MemoryLocation extends TMemoryLocation {
predicate isAlwaysAllocatedOnStack() { none() }
}
+/**
+ * Represents a set of `MemoryLocation`s that cannot overlap with
+ * `MemoryLocation`s outside of the set. The `VirtualVariable` will be
+ * represented by a `MemoryLocation` that totally overlaps all other
+ * `MemoryLocations` in the set.
+ */
abstract class VirtualVariable extends MemoryLocation { }
abstract class AllocationMemoryLocation extends MemoryLocation {
@@ -617,3 +623,9 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
)
)
}
+
+/** Gets the start bit offset of a `MemoryLocation`, if any. */
+int getStartBitOffset(VariableMemoryLocation location) { result = location.getStartBitOffset() }
+
+/** Gets the end bit offset of a `MemoryLocation`, if any. */
+int getEndBitOffset(VariableMemoryLocation location) { result = location.getEndBitOffset() }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll
new file mode 100644
index 000000000000..8ec63b7c1cbd
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRFunctionImports.qll
@@ -0,0 +1 @@
+import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll
index 4cc52d3bbf99..3a7a08accc0d 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRInternal.qll
@@ -1,3 +1,4 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import SSAConstruction as Construction
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll
index 30414bb5db3a..a6cb78b2b3a7 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll
@@ -1,5 +1,11 @@
import SSAConstructionInternal
-private import SSAConstructionImports
+private import SSAConstructionImports as Imports
+private import Imports::Opcode
+private import Imports::OperandTag
+private import Imports::Overlap
+private import Imports::TInstruction
+private import Imports::RawIR as RawIR
+private import SSAInstructions
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -10,52 +16,45 @@ import Cached
cached
private module Cached {
- private IRBlock getNewBlock(OldBlock oldBlock) {
- result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
+ cached
+ predicate hasPhiInstructionCached(
+ OldInstruction blockStartInstr, Alias::MemoryLocation defLocation
+ ) {
+ exists(OldBlock oldBlock |
+ definitionHasPhiNode(defLocation, oldBlock) and
+ blockStartInstr = oldBlock.getFirstInstruction()
+ )
}
cached
- predicate functionHasIR(Language::Function func) {
- exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
+ predicate hasChiInstructionCached(OldInstruction primaryInstruction) {
+ hasChiNode(_, primaryInstruction)
}
cached
- OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) }
-
- private IRVariable getNewIRVariable(OldIR::IRVariable var) {
- // This is just a type cast. Both classes derive from the same newtype.
- result = var
+ predicate hasUnreachedInstructionCached(IRFunction irFunc) {
+ exists(OldInstruction oldInstruction |
+ irFunc = oldInstruction.getEnclosingIRFunction() and
+ Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
+ )
}
- cached
- newtype TInstruction =
- WrappedInstruction(OldInstruction oldInstruction) {
- not oldInstruction instanceof OldIR::PhiInstruction
- } or
- Phi(OldBlock block, Alias::MemoryLocation defLocation) {
- definitionHasPhiNode(defLocation, block)
- } or
- Chi(OldInstruction oldInstruction) {
- not oldInstruction instanceof OldIR::PhiInstruction and
- hasChiNode(_, oldInstruction)
- } or
- Unreached(Language::Function function) {
- exists(OldInstruction oldInstruction |
- function = oldInstruction.getEnclosingFunction() and
- Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
- )
- }
+ class TStageInstruction =
+ TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
cached
- predicate hasTempVariable(
- Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
- ) {
- exists(OldIR::IRTempVariable var |
- var.getEnclosingFunction() = func and
- var.getAST() = ast and
- var.getTag() = tag and
- var.getLanguageType() = type
- )
+ predicate hasInstruction(TStageInstruction instr) {
+ instr instanceof TRawInstruction and instr instanceof OldInstruction
+ or
+ instr instanceof TPhiInstruction
+ or
+ instr instanceof TChiInstruction
+ or
+ instr instanceof TUnreachedInstruction
+ }
+
+ private IRBlock getNewBlock(OldBlock oldBlock) {
+ result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
}
cached
@@ -73,7 +72,7 @@ private module Cached {
or
// Chi instructions track virtual variables, and therefore a chi instruction is
// conflated if it's associated with the aliased virtual variable.
- exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
+ exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
Alias::AliasedVirtualVariable
)
@@ -81,7 +80,7 @@ private module Cached {
// Phi instructions track locations, and therefore a phi instruction is
// conflated if it's associated with a conflated location.
exists(Alias::MemoryLocation location |
- instruction = Phi(_, location) and
+ instruction = getPhi(_, location) and
not exists(location.getAllocation())
)
}
@@ -128,7 +127,7 @@ private module Cached {
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
)
or
- instruction = Chi(getOldInstruction(result)) and
+ instruction = getChi(getOldInstruction(result)) and
tag instanceof ChiPartialOperandTag and
overlap instanceof MustExactlyOverlap
or
@@ -150,6 +149,34 @@ private module Cached {
)
}
+ /**
+ * Holds if the partial operand of this `ChiInstruction` updates the bit range
+ * `[startBitOffset, endBitOffset)` of the total operand.
+ */
+ cached
+ predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBitOffset, int endBitOffset) {
+ exists(Alias::MemoryLocation location, OldInstruction oldInstruction |
+ oldInstruction = getOldInstruction(chi.getPartial()) and
+ location = Alias::getResultMemoryLocation(oldInstruction) and
+ startBitOffset = Alias::getStartBitOffset(location) and
+ endBitOffset = Alias::getEndBitOffset(location)
+ )
+ }
+
+ /**
+ * Holds if `operand` totally overlaps with its definition and consumes the bit range
+ * `[startBitOffset, endBitOffset)`.
+ */
+ cached
+ predicate getUsedInterval(NonPhiMemoryOperand operand, int startBitOffset, int endBitOffset) {
+ exists(Alias::MemoryLocation location, OldIR::NonPhiMemoryOperand oldOperand |
+ oldOperand = operand.getUse().(OldInstruction).getAnOperand() and
+ location = Alias::getOperandMemoryLocation(oldOperand) and
+ startBitOffset = Alias::getStartBitOffset(location) and
+ endBitOffset = Alias::getEndBitOffset(location)
+ )
+ }
+
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.
@@ -172,13 +199,15 @@ private module Cached {
pragma[noopt]
cached
- Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) {
+ Instruction getPhiOperandDefinition(
+ PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
+ ) {
exists(
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
- instr = Phi(phiBlock, useLocation) and
+ instr = getPhi(phiBlock, useLocation) and
newPredecessorBlock = getNewBlock(predBlock) and
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
overlap = Alias::getOverlap(actualDefLocation, useLocation)
@@ -191,7 +220,7 @@ private module Cached {
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
- chiInstr = Chi(oldInstr) and
+ chiInstr = getChi(oldInstr) and
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
@@ -203,21 +232,11 @@ private module Cached {
cached
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
exists(OldBlock oldBlock |
- instr = Phi(oldBlock, _) and
+ instr = getPhi(oldBlock, _) and
result = getNewInstruction(oldBlock.getFirstInstruction())
)
}
- cached
- Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
- result = getOldInstruction(instruction).getConvertedResultExpression()
- }
-
- cached
- Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
- result = getOldInstruction(instruction).getUnconvertedResultExpression()
- }
-
/*
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
* that node is its successor in the new successor relation, and the Chi node's successors are
@@ -228,20 +247,20 @@ private module Cached {
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
if hasChiNode(_, getOldInstruction(instruction))
then
- result = Chi(getOldInstruction(instruction)) and
+ result = getChi(getOldInstruction(instruction)) and
kind instanceof GotoEdge
else (
exists(OldInstruction oldInstruction |
oldInstruction = getOldInstruction(instruction) and
(
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
- then result = Unreached(instruction.getEnclosingFunction())
+ then result = unreachedInstruction(instruction.getEnclosingIRFunction())
else result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
)
or
exists(OldInstruction oldInstruction |
- instruction = Chi(oldInstruction) and
+ instruction = getChi(oldInstruction) and
result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
)
@@ -260,137 +279,73 @@ private module Cached {
// `oldInstruction`, in which case the back edge should come out of the
// chi node instead.
if hasChiNode(_, oldInstruction)
- then instruction = Chi(oldInstruction)
+ then instruction = getChi(oldInstruction)
else instruction = getNewInstruction(oldInstruction)
)
}
cached
- Language::AST getInstructionAST(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction)
- or
- instruction = Chi(oldInstruction)
- |
- result = oldInstruction.getAST()
+ Language::AST getInstructionAST(Instruction instr) {
+ result = getOldInstruction(instr).getAST()
+ or
+ exists(RawIR::Instruction blockStartInstr |
+ instr = phiInstruction(blockStartInstr, _) and
+ result = blockStartInstr.getAST()
)
or
- exists(OldBlock block |
- instruction = Phi(block, _) and
- result = block.getFirstInstruction().getAST()
+ exists(RawIR::Instruction primaryInstr |
+ instr = chiInstruction(primaryInstr) and
+ result = primaryInstr.getAST()
)
or
- instruction = Unreached(result)
+ exists(IRFunctionBase irFunc |
+ instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
+ )
}
cached
- Language::LanguageType getInstructionResultType(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction) and
- result = oldInstruction.getResultLanguageType()
- )
+ Language::LanguageType getInstructionResultType(Instruction instr) {
+ result = instr.(RawIR::Instruction).getResultLanguageType()
or
- exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
- instruction = Chi(oldInstruction) and
- hasChiNode(vvar, oldInstruction) and
- result = vvar.getType()
+ exists(Alias::MemoryLocation defLocation |
+ instr = phiInstruction(_, defLocation) and
+ result = defLocation.getType()
)
or
- exists(Alias::MemoryLocation location |
- instruction = Phi(_, location) and
- result = location.getType()
+ exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
+ instr = chiInstruction(primaryInstr) and
+ hasChiNode(vvar, primaryInstr) and
+ result = vvar.getType()
)
or
- instruction = Unreached(_) and
- result = Language::getVoidType()
+ instr = unreachedInstruction(_) and result = Language::getVoidType()
}
cached
- Opcode getInstructionOpcode(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction) and
- result = oldInstruction.getOpcode()
- )
+ Opcode getInstructionOpcode(Instruction instr) {
+ result = getOldInstruction(instr).getOpcode()
or
- instruction instanceof Chi and
- result instanceof Opcode::Chi
+ instr = phiInstruction(_, _) and result instanceof Opcode::Phi
or
- instruction instanceof Phi and
- result instanceof Opcode::Phi
+ instr = chiInstruction(_) and result instanceof Opcode::Chi
or
- instruction instanceof Unreached and
- result instanceof Opcode::Unreached
+ instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
}
cached
- IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction)
- or
- instruction = Chi(oldInstruction)
- |
- result.getFunction() = oldInstruction.getEnclosingFunction()
- )
+ IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) {
+ result = getOldInstruction(instr).getEnclosingIRFunction()
or
- exists(OldBlock block |
- instruction = Phi(block, _) and
- result.getFunction() = block.getEnclosingFunction()
+ exists(OldInstruction blockStartInstr |
+ instr = phiInstruction(blockStartInstr, _) and
+ result = blockStartInstr.getEnclosingIRFunction()
)
or
- instruction = Unreached(result.getFunction())
- }
-
- cached
- IRVariable getInstructionVariable(Instruction instruction) {
- result =
- getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable())
- }
-
- cached
- Language::Field getInstructionField(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
- }
-
- cached
- int getInstructionIndex(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
- }
-
- cached
- Language::Function getInstructionFunction(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
- }
-
- cached
- string getInstructionConstantValue(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
- }
-
- cached
- Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
- result =
- getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
- }
-
- cached
- Language::LanguageType getInstructionExceptionType(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
- }
-
- cached
- int getInstructionElementSize(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize()
- }
-
- cached
- predicate getInstructionInheritance(
- Instruction instruction, Language::Class baseClass, Language::Class derivedClass
- ) {
- exists(OldIR::InheritanceConversionInstruction oldInstr |
- oldInstr = getOldInstruction(instruction) and
- baseClass = oldInstr.getBaseClass() and
- derivedClass = oldInstr.getDerivedClass()
+ exists(OldInstruction primaryInstr |
+ instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
)
+ or
+ instr = unreachedInstruction(result)
}
cached
@@ -401,7 +356,7 @@ private module Cached {
)
or
exists(OldIR::Instruction oldInstruction |
- instruction = Chi(oldInstruction) and
+ instruction = getChi(oldInstruction) and
result = getNewInstruction(oldInstruction)
)
}
@@ -409,6 +364,14 @@ private module Cached {
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
+private OldInstruction getOldInstruction(Instruction instr) { instr = result }
+
+private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
+
+private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
+ result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
+}
+
/**
* Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition
* of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the
@@ -588,7 +551,7 @@ module DefUse {
|
// An odd offset corresponds to the `Chi` instruction.
defOffset = oldOffset * 2 + 1 and
- result = Chi(oldInstr) and
+ result = getChi(oldInstr) and
(
defLocation = Alias::getResultMemoryLocation(oldInstr) or
defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable()
@@ -607,7 +570,7 @@ module DefUse {
or
defOffset = -1 and
hasDefinition(_, defLocation, defBlock, defOffset) and
- result = Phi(defBlock, defLocation) and
+ result = getPhi(defBlock, defLocation) and
actualDefLocation = defLocation
}
@@ -891,7 +854,7 @@ private module CachedForDebugging {
)
or
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
- instr = Phi(phiBlock, location) and
+ instr = getPhi(phiBlock, location) and
result =
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
if location instanceof Alias::VirtualVariable
@@ -901,7 +864,7 @@ private module CachedForDebugging {
else specificity = "s"
)
or
- instr = Unreached(_) and
+ instr = unreachedInstruction(_) and
result = "Unreached"
}
@@ -961,3 +924,19 @@ module SSAConsistency {
)
}
}
+
+/**
+ * Provides the portion of the parameterized IR interface that is used to construct the SSA stages
+ * of the IR. The raw stage of the IR does not expose these predicates.
+ * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures
+ * that all of SSA construction will be evaluated in the same stage.
+ */
+module SSA {
+ class MemoryLocation = Alias::MemoryLocation;
+
+ predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2;
+
+ predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
+
+ predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll
index 00f12020a29d..f347df86ba1e 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll
@@ -1,3 +1,5 @@
-import semmle.code.cpp.ir.implementation.Opcode
-import semmle.code.cpp.ir.implementation.internal.OperandTag
-import semmle.code.cpp.ir.internal.Overlap
+import semmle.code.cpp.ir.implementation.Opcode as Opcode
+import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
+import semmle.code.cpp.ir.internal.Overlap as Overlap
+import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
+import semmle.code.cpp.ir.implementation.raw.IR as RawIR
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll
index c0922aff8917..bb068bdd489c 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll
@@ -2,5 +2,6 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR
+import semmle.code.cpp.ir.implementation.internal.TInstruction::AliasedSSAInstructions as SSAInstructions
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import AliasedSSA as Alias
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll
new file mode 100644
index 000000000000..60895ce3d266
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll
@@ -0,0 +1,27 @@
+/**
+ * Provides a base class, `IRFunctionBase`, for the stage-independent portions of `IRFunction`.
+ */
+
+private import IRFunctionBaseInternal
+
+private newtype TIRFunction =
+ MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) }
+
+/**
+ * The IR for a function. This base class contains only the predicates that are the same between all
+ * phases of the IR. Each instantiation of `IRFunction` extends this class.
+ */
+class IRFunctionBase extends TIRFunction {
+ Language::Function func;
+
+ IRFunctionBase() { this = MkIRFunction(func) }
+
+ /** Gets a textual representation of this element. */
+ final string toString() { result = "IR: " + func.toString() }
+
+ /** Gets the function whose IR is represented. */
+ final Language::Function getFunction() { result = func }
+
+ /** Gets the location of the function. */
+ final Language::Location getLocation() { result = func.getLocation() }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll
new file mode 100644
index 000000000000..cc1bdb6444b6
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRFunctionBaseInternal.qll
@@ -0,0 +1,2 @@
+import semmle.code.cpp.ir.internal.IRCppLanguage as Language
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll
index ac284440648d..21dfedd95cd4 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,17 @@ 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 +79,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 +94,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 +107,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 +121,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 +134,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 +147,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 +160,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 +173,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 +186,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 +200,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 +225,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 +244,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 +262,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 +274,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 +290,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/internal/TIRVariableInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll
index 362274f387c0..7984c4883fd5 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariableInternal.qll
@@ -1,5 +1,5 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
-import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as Construction
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Construction
private import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag_
module Imports {
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll
new file mode 100644
index 000000000000..e16b71733b5a
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstruction.qll
@@ -0,0 +1,97 @@
+private import TInstructionInternal
+private import IRFunctionBase
+private import TInstructionImports as Imports
+private import Imports::IRType
+private import Imports::Opcode
+
+/**
+ * An IR instruction. `TInstruction` is shared across all phases of the IR. There are individual
+ * branches of this type for instructions created directly from the AST (`TRawInstruction`) and for
+ * instructions added by each stage of SSA construction (`T*PhiInstruction`, `T*ChiInstruction`,
+ * `T*UnreachedInstruction`). Each stage then defines a `TStageInstruction` type that is a union of
+ * all of the branches that can appear in that particular stage. The public `Instruction` class for
+ * each phase extends the `TStageInstruction` type for that stage.
+ */
+cached
+newtype TInstruction =
+ TRawInstruction(
+ IRConstruction::Raw::InstructionTag1 tag1, IRConstruction::Raw::InstructionTag2 tag2
+ ) {
+ IRConstruction::Raw::hasInstruction(tag1, tag2)
+ } or
+ TUnaliasedSSAPhiInstruction(
+ TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
+ ) {
+ UnaliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
+ } or
+ TUnaliasedSSAChiInstruction(TRawInstruction primaryInstruction) { none() } or
+ TUnaliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
+ UnaliasedSSA::SSA::hasUnreachedInstruction(irFunc)
+ } or
+ TAliasedSSAPhiInstruction(
+ TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
+ ) {
+ AliasedSSA::SSA::hasPhiInstruction(blockStartInstr, memoryLocation)
+ } or
+ TAliasedSSAChiInstruction(TRawInstruction primaryInstruction) {
+ AliasedSSA::SSA::hasChiInstruction(primaryInstruction)
+ } or
+ TAliasedSSAUnreachedInstruction(IRFunctionBase irFunc) {
+ AliasedSSA::SSA::hasUnreachedInstruction(irFunc)
+ }
+
+/**
+ * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
+ * unaliased SSA stage.
+ * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
+ * a class alias.
+ */
+module UnaliasedSSAInstructions {
+ class TPhiInstruction = TUnaliasedSSAPhiInstruction;
+
+ TPhiInstruction phiInstruction(
+ TRawInstruction blockStartInstr, UnaliasedSSA::SSA::MemoryLocation memoryLocation
+ ) {
+ result = TUnaliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
+ }
+
+ class TChiInstruction = TUnaliasedSSAChiInstruction;
+
+ TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
+ result = TUnaliasedSSAChiInstruction(primaryInstruction)
+ }
+
+ class TUnreachedInstruction = TUnaliasedSSAUnreachedInstruction;
+
+ TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
+ result = TUnaliasedSSAUnreachedInstruction(irFunc)
+ }
+}
+
+/**
+ * Provides wrappers for the constructors of each branch of `TInstruction` that is used by the
+ * aliased SSA stage.
+ * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
+ * a class alias.
+ */
+module AliasedSSAInstructions {
+ class TPhiInstruction = TAliasedSSAPhiInstruction;
+
+ TPhiInstruction phiInstruction(
+ TRawInstruction blockStartInstr, AliasedSSA::SSA::MemoryLocation memoryLocation
+ ) {
+ result = TAliasedSSAPhiInstruction(blockStartInstr, memoryLocation)
+ }
+
+ class TChiInstruction = TAliasedSSAChiInstruction;
+
+ TChiInstruction chiInstruction(TRawInstruction primaryInstruction) {
+ result = TAliasedSSAChiInstruction(primaryInstruction)
+ }
+
+ class TUnreachedInstruction = TAliasedSSAUnreachedInstruction;
+
+ TUnreachedInstruction unreachedInstruction(IRFunctionBase irFunc) {
+ result = TAliasedSSAUnreachedInstruction(irFunc)
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll
new file mode 100644
index 000000000000..e008ce7d8d34
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionImports.qll
@@ -0,0 +1,2 @@
+import semmle.code.cpp.ir.implementation.IRType as IRType
+import semmle.code.cpp.ir.implementation.Opcode as Opcode
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll
new file mode 100644
index 000000000000..adaaaca9cd82
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TInstructionInternal.qll
@@ -0,0 +1,4 @@
+import semmle.code.cpp.ir.internal.IRCppLanguage as Language
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
+import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.SSAConstruction as UnaliasedSSA
+import semmle.code.cpp.ir.implementation.aliased_ssa.internal.SSAConstruction as AliasedSSA
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll
index badd48552a5f..c96783fe6e81 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll
@@ -1,3 +1,47 @@
+/**
+ * Provides classes that describe the Intermediate Representation (IR) of the program.
+ *
+ * The IR is a representation of the semantics of the program, with very little dependence on the
+ * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`,
+ * and `++i` all have the same semantic effect, but appear in the AST as three different types of
+ * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental
+ * operations similar to:
+ *
+ * ```
+ * r1(int*) = VariableAddress[i] // Compute the address of variable `i`
+ * r2(int) = Load &:r1, m0 // Load the value of `i`
+ * r3(int) = Constant[1] // An integer constant with the value `1`
+ * r4(int) = Add r2, r3 // Add `1` to the value of `i`
+ * r5(int) = Store &r1, r4 // Store the new value back into the variable `i`
+ * ```
+ *
+ * This allows IR-based analysis to focus on the fundamental operations, rather than having to be
+ * concerned with the various ways of expressing those operations in source code.
+ *
+ * The key classes in the IR are:
+ *
+ * - `IRFunction` - Contains the IR for an entire function definition, including all of that
+ * function's `Instruction`s, `IRBlock`s, and `IRVariables`.
+ * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be
+ * performed, the operands that produce the inputs to that operation, and the type of the result
+ * of the operation. Control flows from an `Instruction` to one of a set of successor
+ * `Instruction`s.
+ * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly
+ * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has
+ * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction`
+ * that produces its value (its "definition").
+ * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is
+ * created for each variable directly accessed by the function. In addition, `IRVariable`s are
+ * created to represent certain temporary storage locations that do not have explicitly declared
+ * variables in the source code, such as the return value of the function.
+ * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a
+ * sequence of instructions such that control flow can only enter the block at the first
+ * instruction, and can only leave the block from the last instruction.
+ * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType`
+ * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all
+ * be represented as the `IRType` `uint4`, a four-byte unsigned integer.
+ */
+
import IRFunction
import Instruction
import IRBlock
@@ -11,11 +55,12 @@ import Imports::MemoryAccessKind
private newtype TIRPropertyProvider = MkIRPropertyProvider()
/**
- * Class that provides additional properties to be dumped for IR instructions and blocks when using
+ * A class that provides additional properties to be dumped for IR instructions and blocks when using
* the PrintIR module. Libraries that compute additional facts about IR elements can extend the
* single instance of this class to specify the additional properties computed by the library.
*/
class IRPropertyProvider extends TIRPropertyProvider {
+ /** Gets a textual representation of this element. */
string toString() { result = "IRPropertyProvider" }
/**
@@ -27,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/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll
index 94ef73b27692..d827ed3cf82d 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRBlock.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes describing basic blocks in the IR of a function.
+ */
+
private import internal.IRInternal
import Instruction
private import internal.IRBlockImports as Imports
@@ -16,15 +20,27 @@ private import Cached
* Most consumers should use the class `IRBlock`.
*/
class IRBlockBase extends TIRBlock {
+ /** Gets a textual representation of this block. */
final string toString() { result = getFirstInstruction(this).toString() }
+ /** Gets the source location of the first non-`Phi` instruction in this block. */
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
+ /**
+ * INTERNAL: Do not use.
+ *
+ * Gets a string that uniquely identifies this block within its enclosing function.
+ *
+ * This predicate is used by debugging and printing code only.
+ */
final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() }
/**
- * Gets the zero-based index of the block within its function. This is used
- * by debugging and printing code only.
+ * INTERNAL: Do not use.
+ *
+ * Gets the zero-based index of the block within its function.
+ *
+ * This predicate is used by debugging and printing code only.
*/
int getDisplayIndex() {
exists(IRConfiguration::IRConfiguration config |
@@ -42,27 +58,51 @@ class IRBlockBase extends TIRBlock {
)
}
+ /**
+ * Gets the `index`th non-`Phi` instruction in this block.
+ */
final Instruction getInstruction(int index) { result = getInstruction(this, index) }
+ /**
+ * Get the `Phi` instructions that appear at the start of this block.
+ */
final PhiInstruction getAPhiInstruction() {
Construction::getPhiInstructionBlockStart(result) = getFirstInstruction()
}
+ /**
+ * Gets an instruction in this block. This includes `Phi` instructions.
+ */
final Instruction getAnInstruction() {
result = getInstruction(_) or
result = getAPhiInstruction()
}
+ /**
+ * Gets the first non-`Phi` instruction in this block.
+ */
final Instruction getFirstInstruction() { result = getFirstInstruction(this) }
+ /**
+ * Gets the last instruction in this block.
+ */
final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) }
+ /**
+ * Gets the number of non-`Phi` instructions in this block.
+ */
final int getInstructionCount() { result = getInstructionCount(this) }
+ /**
+ * Gets the `IRFunction` that contains this block.
+ */
final IRFunction getEnclosingIRFunction() {
result = getFirstInstruction(this).getEnclosingIRFunction()
}
+ /**
+ * Gets the `Function` that contains this block.
+ */
final Language::Function getEnclosingFunction() {
result = getFirstInstruction(this).getEnclosingFunction()
}
@@ -74,20 +114,57 @@ class IRBlockBase extends TIRBlock {
* instruction of another block.
*/
class IRBlock extends IRBlockBase {
+ /**
+ * Gets a block to which control flows directly from this block.
+ */
final IRBlock getASuccessor() { blockSuccessor(this, result) }
+ /**
+ * Gets a block from which control flows directly to this block.
+ */
final IRBlock getAPredecessor() { blockSuccessor(result, this) }
+ /**
+ * Gets the block to which control flows directly from this block along an edge of kind `kind`.
+ */
final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) }
+ /**
+ * Gets the block to which control flows directly from this block along a back edge of kind
+ * `kind`.
+ */
final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) }
+ /**
+ * Holds if this block immediately dominates `block`.
+ *
+ * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B`
+ * is a direct successor of block `A`.
+ */
final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) }
+ /**
+ * Holds if this block strictly dominates `block`.
+ *
+ * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B`
+ * are not the same block.
+ */
final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) }
+ /**
+ * Holds if this block dominates `block`.
+ *
+ * Block `A` dominates block `B` if any control flow path from the entry block of the function to
+ * block `B` must pass through block `A`. A block always dominates itself.
+ */
final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block }
+ /**
+ * Gets a block on the dominance frontier of this block.
+ *
+ * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
+ * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`.
+ */
pragma[noinline]
final IRBlock dominanceFrontier() {
dominates(result.getAPredecessor()) and
@@ -95,7 +172,7 @@ class IRBlock extends IRBlockBase {
}
/**
- * Holds if this block is reachable from the entry point of its function
+ * Holds if this block is reachable from the entry block of its function.
*/
final predicate isReachableFromFunctionEntry() {
this = getEnclosingIRFunction().getEntryBlock() or
@@ -210,4 +287,4 @@ private module Cached {
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
}
-Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
+private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll
index 65af34942b6b..6a87b9b4b5fd 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll
@@ -8,10 +8,79 @@ module InstructionConsistency {
private import Imports::Overlap
private import internal.IRInternal
+ private newtype TOptionalIRFunction =
+ TPresentIRFunction(IRFunction irFunc) or
+ TMissingIRFunction()
+
+ /**
+ * An `IRFunction` that might not exist. This is used so that we can produce consistency failures
+ * for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
+ */
+ abstract private class OptionalIRFunction extends TOptionalIRFunction {
+ abstract string toString();
+
+ abstract Language::Location getLocation();
+ }
+
+ private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
+ private IRFunction irFunc;
+
+ PresentIRFunction() { this = TPresentIRFunction(irFunc) }
+
+ override string toString() {
+ result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
+ }
+
+ override Language::Location getLocation() {
+ // To avoid an overwhelming number of results when the extractor merges functions with the
+ // same name, just pick a single location.
+ result =
+ rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
+ }
+ }
+
+ private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
+ override string toString() { result = "" }
+
+ override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
+ }
+
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
+ result = TPresentIRFunction(instr.getEnclosingIRFunction())
+ or
+ not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
+ result = getInstructionIRFunction(instr) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getOperandIRFunction(Operand operand) {
+ result = TPresentIRFunction(operand.getEnclosingIRFunction())
+ or
+ not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
+ result = getOperandIRFunction(operand) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getBlockIRFunction(IRBlock block) {
+ result = TPresentIRFunction(block.getEnclosingIRFunction())
+ or
+ not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
/**
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
*/
- query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
+ query predicate missingOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
exists(OperandTag tag |
instr.getOpcode().hasOperand(tag) and
not exists(NonPhiOperand operand |
@@ -21,32 +90,39 @@ module InstructionConsistency {
message =
"Instruction '" + instr.getOpcode().toString() +
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
/**
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
*/
- query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
- exists(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- not instr.getOpcode().hasOperand(tag) and
- not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
- not (
- instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
- ) and
- not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
+ query predicate unexpectedOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag |
+ exists(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ not instr.getOpcode().hasOperand(tag) and
+ not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
+ not (
+ instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
+ ) and
+ not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
+ message =
+ "Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
+ "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
/**
* Holds if instruction `instr` has multiple operands with tag `tag`.
*/
query predicate duplicateOperand(
- Instruction instr, string message, IRFunction func, string funcText
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(OperandTag tag, int operandCount |
operandCount =
@@ -58,8 +134,7 @@ module InstructionConsistency {
message =
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
" in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
@@ -67,100 +142,136 @@ module InstructionConsistency {
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
* the predecessor block `pred`.
*/
- query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
- pred = instr.getBlock().getAPredecessor() and
- not exists(PhiInputOperand operand |
- operand = instr.getAnOperand() and
- operand.getPredecessorBlock() = pred
+ query predicate missingPhiOperand(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRBlock pred |
+ pred = instr.getBlock().getAPredecessor() and
+ not exists(PhiInputOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getPredecessorBlock() = pred
+ ) and
+ message =
+ "Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
+ pred.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
- query predicate missingOperandType(Operand operand, string message) {
- exists(Language::Function func, Instruction use |
+ query predicate missingOperandType(
+ Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Instruction use |
not exists(operand.getType()) and
use = operand.getUse() and
- func = use.getEnclosingFunction() and
message =
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
- "' missing type in function '" + Language::getIdentityString(func) + "'."
+ "' is missing a type in function '$@'." and
+ irFunc = getOperandIRFunction(operand, irFuncText)
)
}
query predicate duplicateChiOperand(
- ChiInstruction chi, string message, IRFunction func, string funcText
+ ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
) {
chi.getTotal() = chi.getPartial() and
message =
"Chi instruction for " + chi.getPartial().toString() +
- " has duplicate operands in function $@" and
- func = chi.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ " has duplicate operands in function '$@'." and
+ irFunc = getInstructionIRFunction(chi, irFuncText)
}
query predicate sideEffectWithoutPrimary(
- SideEffectInstruction instr, string message, IRFunction func, string funcText
+ SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
not exists(instr.getPrimaryInstruction()) and
- message = "Side effect instruction missing primary instruction in function $@" and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ message =
+ "Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
/**
* Holds if an instruction, other than `ExitFunction`, has no successors.
*/
- query predicate instructionWithoutSuccessor(Instruction instr) {
+ query predicate instructionWithoutSuccessor(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
not exists(instr.getASuccessor()) and
not instr instanceof ExitFunctionInstruction and
// Phi instructions aren't linked into the instruction-level flow graph.
not instr instanceof PhiInstruction and
- not instr instanceof UnreachedInstruction
+ not instr instanceof UnreachedInstruction and
+ message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
/**
- * Holds if there are multiple (`n`) edges of kind `kind` from `source`,
- * where `target` is among the targets of those edges.
+ * Holds if there are multiple edges of the same kind from `source`.
*/
- query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
- n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
- n > 1 and
- source.getSuccessor(kind) = target
+ query predicate ambiguousSuccessors(
+ Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(EdgeKind kind, int n |
+ n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
+ n > 1 and
+ message =
+ "Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
+ kind.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(source, irFuncText)
+ )
}
/**
- * Holds if `instr` in `f` is part of a loop even though the AST of `f`
+ * Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
* contains no element that can cause loops.
*/
- query predicate unexplainedLoop(Language::Function f, Instruction instr) {
- exists(IRBlock block |
- instr.getBlock() = block and
- block.getEnclosingFunction() = f and
- block.getASuccessor+() = block
- ) and
- not Language::hasPotentialLoop(f)
+ query predicate unexplainedLoop(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Language::Function f |
+ exists(IRBlock block |
+ instr.getBlock() = block and
+ block.getEnclosingFunction() = f and
+ block.getASuccessor+() = block
+ ) and
+ not Language::hasPotentialLoop(f) and
+ message =
+ "Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
/**
* Holds if a `Phi` instruction is present in a block with fewer than two
* predecessors.
*/
- query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
- count(instr.getBlock().getAPredecessor()) < 2
+ query predicate unnecessaryPhiInstruction(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int n |
+ n = count(instr.getBlock().getAPredecessor()) and
+ n < 2 and
+ message =
+ "Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
+ " predecessors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
/**
* Holds if a memory operand is connected to a definition with an unmodeled result.
*/
query predicate memoryOperandDefinitionIsUnmodeled(
- Instruction instr, string message, IRFunction func, string funcText
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(MemoryOperand operand, Instruction def |
operand = instr.getAnOperand() and
def = operand.getAnyDef() and
not def.isResultModeled() and
- message = "Memory operand definition has unmodeled result in function '$@'" and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ message =
+ "Memory operand definition on instruction '" + instr.toString() +
+ "' has unmodeled result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
@@ -168,18 +279,37 @@ module InstructionConsistency {
* Holds if operand `operand` consumes a value that was defined in
* a different function.
*/
- query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
- operand.getUse() = instr and
- operand.getAnyDef() = defInstr and
- instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
+ query predicate operandAcrossFunctions(
+ Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
+ OptionalIRFunction defIRFunc, string defIRFuncText
+ ) {
+ exists(Instruction useInstr, Instruction defInstr |
+ operand.getUse() = useInstr and
+ operand.getAnyDef() = defInstr and
+ useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
+ defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
+ useIRFunc != defIRFunc and
+ message =
+ "Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
+ "' in function '$@', but is defined on instruction '" + defInstr.toString() +
+ "' in function '$@'."
+ )
}
/**
* Holds if instruction `instr` is not in exactly one block.
*/
- query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
- blockCount = count(instr.getBlock()) and
- blockCount != 1
+ query predicate instructionWithoutUniqueBlock(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int blockCount |
+ blockCount = count(instr.getBlock()) and
+ blockCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
+ " blocks in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
@@ -192,10 +322,11 @@ module InstructionConsistency {
*
* This check ensures we don't have too _few_ back edges.
*/
- query predicate containsLoopOfForwardEdges(IRFunction f) {
+ query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
exists(IRBlock block |
forwardEdge+(block, block) and
- block.getEnclosingIRFunction() = f
+ block.getEnclosingIRFunction() = f and
+ message = "Function contains a loop consisting of only forward edges."
)
}
@@ -207,12 +338,19 @@ module InstructionConsistency {
*
* This check ensures we don't have too _many_ back edges.
*/
- query predicate lostReachability(IRBlock block) {
+ query predicate lostReachability(
+ IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
exists(IRFunction f, IRBlock entry |
entry = f.getEntryBlock() and
entry.getASuccessor+() = block and
not forwardEdge+(entry, block) and
- not Language::hasGoto(f.getFunction())
+ not Language::hasGoto(f.getFunction()) and
+ message =
+ "Block '" + block.toString() +
+ "' is not reachable by traversing only forward edges in function '$@'." and
+ irFunc = TPresentIRFunction(f) and
+ irFuncText = irFunc.toString()
)
}
@@ -220,16 +358,22 @@ module InstructionConsistency {
* Holds if the number of back edges differs between the `Instruction` graph
* and the `IRBlock` graph.
*/
- query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
- fromInstr =
- count(Instruction i1, Instruction i2 |
- i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
- ) and
- fromBlock =
- count(IRBlock b1, IRBlock b2 |
- b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
- ) and
- fromInstr != fromBlock
+ query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
+ exists(int fromInstr, int fromBlock |
+ fromInstr =
+ count(Instruction i1, Instruction i2 |
+ getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
+ ) and
+ fromBlock =
+ count(IRBlock b1, IRBlock b2 |
+ getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
+ ) and
+ fromInstr != fromBlock and
+ message =
+ "The instruction graph for function '" + irFunc.toString() + "' contains " +
+ fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
+ + " back edges."
+ )
}
/**
@@ -251,7 +395,7 @@ module InstructionConsistency {
* Holds if `useOperand` has a definition that does not dominate the use.
*/
query predicate useNotDominatedByDefinition(
- Operand useOperand, string message, IRFunction func, string funcText
+ Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
pointOfEvaluation(useOperand, useBlock, useIndex) and
@@ -272,19 +416,17 @@ module InstructionConsistency {
message =
"Operand '" + useOperand.toString() +
"' is not dominated by its definition in function '$@'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
)
}
query predicate switchInstructionWithoutDefaultEdge(
- SwitchInstruction switchInstr, string message, IRFunction func, string funcText
+ SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
) {
not exists(switchInstr.getDefaultSuccessor()) and
message =
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
- func = switchInstr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getInstructionIRFunction(switchInstr, irFuncText)
}
/**
@@ -305,18 +447,30 @@ module InstructionConsistency {
instr.getOpcode() instanceof Opcode::InitializeNonLocal
}
- query predicate notMarkedAsConflated(Instruction instr) {
+ query predicate notMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
shouldBeConflated(instr) and
- not instr.isResultConflated()
+ not instr.isResultConflated() and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
- query predicate wronglyMarkedAsConflated(Instruction instr) {
+ query predicate wronglyMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
instr.isResultConflated() and
- not shouldBeConflated(instr)
+ not shouldBeConflated(instr) and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should not be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
query predicate invalidOverlap(
- MemoryOperand useOperand, string message, IRFunction func, string funcText
+ MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(Overlap overlap |
overlap = useOperand.getDefinitionOverlap() and
@@ -324,8 +478,20 @@ module InstructionConsistency {
message =
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
overlap.toString() + "'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
+ )
+ }
+
+ query predicate nonUniqueEnclosingIRFunction(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int irFuncCount |
+ irFuncCount = count(instr.getEnclosingIRFunction()) and
+ irFuncCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
+ " results for `getEnclosingIRFunction()` in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll
index 9aea3e00d666..5968e58f90bf 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRFunction.qll
@@ -1,29 +1,17 @@
+/**
+ * Provides the class `IRFunction`, which represents the Intermediate Representation for the
+ * definition of a function.
+ */
+
private import internal.IRInternal
+private import internal.IRFunctionImports as Imports
+import Imports::IRFunctionBase
import Instruction
-private newtype TIRFunction =
- MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
-
/**
- * Represents the IR for a function.
+ * The IR for a function.
*/
-class IRFunction extends TIRFunction {
- Language::Function func;
-
- IRFunction() { this = MkIRFunction(func) }
-
- final string toString() { result = "IR: " + func.toString() }
-
- /**
- * Gets the function whose IR is represented.
- */
- final Language::Function getFunction() { result = func }
-
- /**
- * Gets the location of the function.
- */
- final Language::Location getLocation() { result = func.getLocation() }
-
+class IRFunction extends IRFunctionBase {
/**
* Gets the entry point for this function.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll
index 9f2a0d4ea281..146fc2707383 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes that represent variables accessed by the IR.
+ */
+
private import internal.IRInternal
import IRFunction
private import internal.IRVariableImports as Imports
@@ -7,15 +11,11 @@ private import Imports::TTempVariableTag
private import Imports::TIRVariable
private import Imports::IRType
-IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
- result.getVariable() = var and
- result.getEnclosingFunction() = func
-}
-
/**
- * A variable referenced by the IR for a function. The variable may be a user-declared variable
- * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation
- * (`IRTempVariable`).
+ * A variable referenced by the IR for a function.
+ *
+ * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
+ * by the AST-to-IR translation (`IRTempVariable`).
*/
class IRVariable extends TIRVariable {
Language::Function func;
@@ -27,6 +27,7 @@ class IRVariable extends TIRVariable {
this = TIRDynamicInitializationFlag(func, _, _)
}
+ /** Gets a textual representation of this element. */
string toString() { none() }
/**
@@ -162,20 +163,30 @@ class IRGeneratedVariable extends IRVariable {
override string getUniqueId() { none() }
+ /**
+ * INTERNAL: Do not use.
+ *
+ * Gets a string containing the source code location of the AST that generated this variable.
+ *
+ * This is used by debugging and printing code only.
+ */
final string getLocationString() {
result =
ast.getLocation().getStartLine().toString() + ":" +
ast.getLocation().getStartColumn().toString()
}
+ /**
+ * INTERNAL: Do not use.
+ *
+ * Gets the string that is combined with the location of the variable to generate the string
+ * representation of this variable.
+ *
+ * This is used by debugging and printing code only.
+ */
string getBaseString() { none() }
}
-IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
- result.getAST() = ast and
- result.getTag() = tag
-}
-
/**
* A temporary variable introduced by IR construction. The most common examples are the variable
* generated to hold the return value of a function, or the variable generated to hold the result of
@@ -190,6 +201,10 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa
result = "Temp: " + Construction::getTempVariableUniqueId(this)
}
+ /**
+ * Gets the "tag" object that differentiates this temporary variable from other temporary
+ * variables generated for the same AST.
+ */
final TempVariableTag getTag() { result = tag }
override string getBaseString() { result = "#temp" }
@@ -217,19 +232,23 @@ class IRThrowVariable extends IRTempVariable {
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
-class IREllipsisVariable extends IRTempVariable {
+class IREllipsisVariable extends IRTempVariable, IRParameter {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
+
+ final override int getIndex() { result = func.getNumberOfParameters() }
}
/**
* A temporary variable generated to hold the `this` pointer.
*/
-class IRThisVariable extends IRTempVariable {
+class IRThisVariable extends IRTempVariable, IRParameter {
IRThisVariable() { tag = ThisTempVar() }
final override string toString() { result = "#this" }
+
+ final override int getIndex() { result = -1 }
}
/**
@@ -249,6 +268,9 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
final override string getBaseString() { result = "#string" }
+ /**
+ * Gets the AST of the string literal represented by this `IRStringLiteral`.
+ */
final Language::StringLiteral getLiteral() { result = literal }
}
@@ -266,6 +288,9 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
final override string toString() { result = var.toString() + "#init" }
+ /**
+ * Gets variable whose initialization is guarded by this flag.
+ */
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
@@ -274,3 +299,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}
+
+/**
+ * An IR variable which acts like a function parameter, including positional parameters and the
+ * temporary variables generated for `this` and ellipsis parameters.
+ */
+class IRParameter extends IRAutomaticVariable {
+ IRParameter() {
+ this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
+ or
+ this = TIRTempVariable(_, _, ThisTempVar(), _)
+ or
+ this = TIRTempVariable(_, _, EllipsisTempVar(), _)
+ }
+
+ /**
+ * Gets the zero-based index of this parameter. The `this` parameter has index -1.
+ */
+ int getIndex() { none() }
+}
+
+/**
+ * An IR variable representing a positional parameter.
+ */
+class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
+ final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll
index 9c83a3d99f0c..620b23b942e0 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes that represent the individual instructions in the IR for a function.
+ */
+
private import internal.IRInternal
import IRFunction
import IRBlock
@@ -27,9 +31,16 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
}
/**
- * Represents a single operation in the IR.
+ * A single instruction in the IR.
*/
-class Instruction extends Construction::TInstruction {
+class Instruction extends Construction::TStageInstruction {
+ Instruction() {
+ // The base `TStageInstruction` type is a superset of the actual instructions appearing in this
+ // stage. This call lets the stage filter out the ones that are not reused from raw IR.
+ Construction::hasInstruction(this)
+ }
+
+ /** Gets a textual representation of this element. */
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
/**
@@ -194,16 +205,25 @@ class Instruction extends Construction::TInstruction {
* conversion.
*/
final Language::Expr getConvertedResultExpression() {
- result = Construction::getInstructionConvertedResultExpression(this)
+ result = Raw::getInstructionConvertedResultExpression(this)
}
/**
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
*/
final Language::Expr getUnconvertedResultExpression() {
- result = Construction::getInstructionUnconvertedResultExpression(this)
+ result = Raw::getInstructionUnconvertedResultExpression(this)
}
+ /**
+ * Gets the language-specific type of the result produced by this instruction.
+ *
+ * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a
+ * less complex, language-neutral type system in which all semantically equivalent types share the
+ * same `IRType` instance. For example, in C++, four different `Instruction`s might have three
+ * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`,
+ * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`.
+ */
final Language::LanguageType getResultLanguageType() {
result = Construction::getInstructionResultType(this)
}
@@ -212,6 +232,7 @@ class Instruction extends Construction::TInstruction {
* Gets the type of the result produced by this instruction. If the instruction does not produce
* a result, its result type will be `IRVoidType`.
*/
+ cached
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
/**
@@ -240,17 +261,19 @@ class Instruction extends Construction::TInstruction {
* given by `getResultType()`.
*
* For example, the statement `y = x;` generates the following IR:
+ * ```
* r1_0(glval: int) = VariableAddress[x]
* r1_1(int) = Load r1_0, mu0_1
* r1_2(glval: int) = VariableAddress[y]
* mu1_3(int) = Store r1_2, r1_1
+ * ```
*
* The result of each `VariableAddress` instruction is a glvalue of type
* `int`, representing the address of the corresponding integer variable. The
* result of the `Load` instruction is a prvalue of type `int`, representing
* the integer value loaded from variable `x`.
*/
- final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
+ final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
/**
* Gets the size of the result produced by this instruction, in bytes. If the
@@ -259,7 +282,7 @@ class Instruction extends Construction::TInstruction {
* If `this.isGLValue()` holds for this instruction, the value of
* `getResultSize()` will always be the size of a pointer.
*/
- final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
+ final int getResultSize() { result = getResultLanguageType().getByteSize() }
/**
* Gets the opcode that specifies the operation performed by this instruction.
@@ -392,13 +415,27 @@ class Instruction extends Construction::TInstruction {
final Instruction getAPredecessor() { result = getPredecessor(_) }
}
+/**
+ * An instruction that refers to a variable.
+ *
+ * This class is used for any instruction whose operation fundamentally depends on a specific
+ * variable. For example, it is used for `VariableAddress`, which returns the address of a specific
+ * variable, and `InitializeParameter`, which returns the value that was passed to the specified
+ * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions
+ * that happen to load from or store to a particular variable; in those cases, the memory location
+ * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be
+ * defined by the result of a `VariableAddress` instruction.
+ */
class VariableInstruction extends Instruction {
IRVariable var;
- VariableInstruction() { var = Construction::getInstructionVariable(this) }
+ VariableInstruction() { var = Raw::getInstructionVariable(this) }
override string getImmediateString() { result = var.toString() }
+ /**
+ * Gets the variable that this instruction references.
+ */
final IRVariable getIRVariable() { result = var }
/**
@@ -407,63 +444,156 @@ class VariableInstruction extends Instruction {
final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
}
+/**
+ * An instruction that refers to a field of a class, struct, or union.
+ *
+ * This class is used for any instruction whose operation fundamentally depends on a specific
+ * field. For example, it is used for `FieldAddress`, which computes the address of a specific
+ * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen
+ * to load from or store to a particular field; in those cases, the memory location being accessed
+ * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the
+ * result of a `FieldAddress` instruction.
+ */
class FieldInstruction extends Instruction {
Language::Field field;
- FieldInstruction() { field = Construction::getInstructionField(this) }
+ FieldInstruction() { field = Raw::getInstructionField(this) }
final override string getImmediateString() { result = field.toString() }
+ /**
+ * Gets the field that this instruction references.
+ */
final Language::Field getField() { result = field }
}
+/**
+ * An instruction that refers to a function.
+ *
+ * This class is used for any instruction whose operation fundamentally depends on a specific
+ * function. For example, it is used for `FunctionAddress`, which returns the address of a specific
+ * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a
+ * particular function; in that case, the function being called is specified by the
+ * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a
+ * `FunctionAddress` instruction.
+ */
class FunctionInstruction extends Instruction {
Language::Function funcSymbol;
- FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
+ FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
final override string getImmediateString() { result = funcSymbol.toString() }
+ /**
+ * Gets the function that this instruction references.
+ */
final Language::Function getFunctionSymbol() { result = funcSymbol }
}
+/**
+ * An instruction whose result is a compile-time constant value.
+ */
class ConstantValueInstruction extends Instruction {
string value;
- ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
+ ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
final override string getImmediateString() { result = value }
+ /**
+ * Gets the constant value of this instruction's result.
+ */
final string getValue() { result = value }
}
+/**
+ * An instruction that refers to an argument of a `Call` instruction.
+ *
+ * This instruction is used for side effects of a `Call` instruction that read or write memory
+ * pointed to by one of the arguments of the call.
+ */
class IndexedInstruction extends Instruction {
int index;
- IndexedInstruction() { index = Construction::getInstructionIndex(this) }
+ IndexedInstruction() { index = Raw::getInstructionIndex(this) }
final override string getImmediateString() { result = index.toString() }
+ /**
+ * Gets the zero-based index of the argument that this instruction references.
+ */
final int getIndex() { result = index }
}
+/**
+ * An instruction representing the entry point to a function.
+ *
+ * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins
+ * at this instruction. This instruction has no predecessors.
+ */
class EnterFunctionInstruction extends Instruction {
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
}
+/**
+ * An instruction that returns the address of a variable.
+ *
+ * This instruction returns the address of a local variable, parameter, static field,
+ * namespace-scope variable, or global variable. For the address of a non-static field of a class,
+ * struct, or union, see `FieldAddressInstruction`.
+ */
class VariableAddressInstruction extends VariableInstruction {
VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress }
}
+/**
+ * An instruction that returns the address of a function.
+ *
+ * This instruction returns the address of a function, including non-member functions, static member
+ * functions, and non-static member functions.
+ *
+ * The result has an `IRFunctionAddress` type.
+ */
+class FunctionAddressInstruction extends FunctionInstruction {
+ FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress }
+}
+
+/**
+ * An instruction that initializes a parameter of the enclosing function with the value of the
+ * corresponding argument passed by the caller.
+ *
+ * Each parameter of a function will have exactly one `InitializeParameter` instruction that
+ * initializes that parameter.
+ */
class InitializeParameterInstruction extends VariableInstruction {
InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter }
+ /**
+ * Gets the parameter initialized by this instruction.
+ */
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
}
+/**
+ * An instruction that initializes all memory that existed before this function was called.
+ *
+ * This instruction provides a definition for memory that, because it was actually allocated and
+ * initialized elsewhere, would not otherwise have a definition in this function.
+ */
+class InitializeNonLocalInstruction extends Instruction {
+ InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal }
+}
+
+/**
+ * An instruction that initializes the memory pointed to by a parameter of the enclosing function
+ * with the value of that memory on entry to the function.
+ */
class InitializeIndirectionInstruction extends VariableInstruction {
InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection }
+ /**
+ * Gets the parameter initialized by this instruction.
+ */
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
}
@@ -474,14 +604,42 @@ class InitializeThisInstruction extends Instruction {
InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis }
}
+/**
+ * An instruction that computes the address of a non-static field of an object.
+ */
class FieldAddressInstruction extends FieldInstruction {
FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress }
+ /**
+ * Gets the operand that provides the address of the object containing the field.
+ */
final UnaryOperand getObjectAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the object containing the field.
+ */
final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() }
}
+/**
+ * An instruction that computes the address of the first element of a managed array.
+ *
+ * This instruction is used for element access to C# arrays.
+ */
+class ElementsAddressInstruction extends UnaryInstruction {
+ ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress }
+
+ /**
+ * Gets the operand that provides the address of the array object.
+ */
+ final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the address of the array object.
+ */
+ final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() }
+}
+
/**
* An instruction that produces a well-defined but unknown result and has
* unknown side effects, including side effects that are not conservatively
@@ -496,6 +654,12 @@ class ErrorInstruction extends Instruction {
ErrorInstruction() { getOpcode() instanceof Opcode::Error }
}
+/**
+ * An instruction that returns an uninitialized value.
+ *
+ * This instruction is used to provide an initial definition for a stack variable that does not have
+ * an initializer, or whose initializer only partially initializes the variable.
+ */
class UninitializedInstruction extends VariableInstruction {
UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized }
@@ -505,35 +669,94 @@ class UninitializedInstruction extends VariableInstruction {
final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() }
}
+/**
+ * An instruction that has no effect.
+ *
+ * This instruction is typically inserted to ensure that a particular AST is associated with at
+ * least one instruction, even when the AST has no semantic effect.
+ */
class NoOpInstruction extends Instruction {
NoOpInstruction() { getOpcode() instanceof Opcode::NoOp }
}
+/**
+ * An instruction that returns control to the caller of the function.
+ *
+ * This instruction represents the normal (non-exception) return from a function, either from an
+ * explicit `return` statement or from control flow reaching the end of the function's body.
+ *
+ * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is
+ * represented as an initialization of the temporary variable that holds the return value, with
+ * control then flowing to the common `ReturnInstruction` for that function. Exception: A function
+ * that never returns will not have a `ReturnInstruction`.
+ *
+ * The `ReturnInstruction` for a function will have a control-flow successor edge to a block
+ * containing the `ExitFunction` instruction for that function.
+ *
+ * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from
+ * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a
+ * `void`-returning function.
+ */
class ReturnInstruction extends Instruction {
ReturnInstruction() { getOpcode() instanceof ReturnOpcode }
}
+/**
+ * An instruction that returns control to the caller of the function, without returning a value.
+ */
class ReturnVoidInstruction extends ReturnInstruction {
ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid }
}
+/**
+ * An instruction that returns control to the caller of the function, including a return value.
+ */
class ReturnValueInstruction extends ReturnInstruction {
ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue }
+ /**
+ * Gets the operand that provides the value being returned by the function.
+ */
final LoadOperand getReturnValueOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the value being returned by the function, if an
+ * exact definition is available.
+ */
final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
}
+/**
+ * An instruction that represents the use of the value pointed to by a parameter of the function
+ * after the function returns control to its caller.
+ *
+ * This instruction does not itself return control to the caller. It merely represents the potential
+ * for a caller to use the memory pointed to by the parameter sometime after the call returns. This
+ * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility
+ * that the caller initialized the memory pointed to by the parameter before the call.
+ */
class ReturnIndirectionInstruction extends VariableInstruction {
ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection }
+ /**
+ * Gets the operand that provides the value of the pointed-to memory.
+ */
final SideEffectOperand getSideEffectOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the value of the pointed-to memory, if an exact
+ * definition is available.
+ */
final Instruction getSideEffect() { result = getSideEffectOperand().getDef() }
+ /**
+ * Gets the operand that provides the address of the pointed-to memory.
+ */
final AddressOperand getSourceAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the pointed-to memory.
+ */
final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() }
/**
@@ -541,87 +764,191 @@ class ReturnIndirectionInstruction extends VariableInstruction {
* function.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
+
+ /**
+ * Holds if this instruction is the return indirection for `this`.
+ */
+ final predicate isThisIndirection() { var instanceof IRThisVariable }
}
+/**
+ * An instruction that returns a copy of its operand.
+ *
+ * There are several different copy instructions, depending on the source and destination of the
+ * copy operation:
+ * - `CopyInstruction` - Copies a register operand to a register result.
+ * - `LoadInstruction` - Copies a memory operand to a register result.
+ * - `StoreInstruction` - Copies a register operand to a memory result.
+ */
class CopyInstruction extends Instruction {
CopyInstruction() { getOpcode() instanceof CopyOpcode }
+ /**
+ * Gets the operand that provides the input value of the copy.
+ */
Operand getSourceValueOperand() { none() }
+ /**
+ * Gets the instruction whose result provides the input value of the copy, if an exact definition
+ * is available.
+ */
final Instruction getSourceValue() { result = getSourceValueOperand().getDef() }
}
+/**
+ * An instruction that returns a register result containing a copy of its register operand.
+ */
class CopyValueInstruction extends CopyInstruction, UnaryInstruction {
CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue }
final override UnaryOperand getSourceValueOperand() { result = getAnOperand() }
}
+/**
+ * An instruction that returns a register result containing a copy of its memory operand.
+ */
class LoadInstruction extends CopyInstruction {
LoadInstruction() { getOpcode() instanceof Opcode::Load }
+ /**
+ * Gets the operand that provides the address of the value being loaded.
+ */
final AddressOperand getSourceAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the value being loaded.
+ */
final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() }
final override LoadOperand getSourceValueOperand() { result = getAnOperand() }
}
+/**
+ * An instruction that returns a memory result containing a copy of its register operand.
+ */
class StoreInstruction extends CopyInstruction {
StoreInstruction() { getOpcode() instanceof Opcode::Store }
+ /**
+ * Gets the operand that provides the address of the location to which the value will be stored.
+ */
final AddressOperand getDestinationAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the location to which the value will
+ * be stored, if an exact definition is available.
+ */
final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() }
final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() }
}
+/**
+ * An instruction that branches to one of two successor instructions based on the value of a Boolean
+ * operand.
+ */
class ConditionalBranchInstruction extends Instruction {
ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch }
+ /**
+ * Gets the operand that provides the Boolean condition controlling the branch.
+ */
final ConditionOperand getConditionOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the Boolean condition controlling the branch.
+ */
final Instruction getCondition() { result = getConditionOperand().getDef() }
+ /**
+ * Gets the instruction to which control will flow if the condition is true.
+ */
final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) }
+ /**
+ * Gets the instruction to which control will flow if the condition is false.
+ */
final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) }
}
+/**
+ * An instruction representing the exit point of a function.
+ *
+ * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns
+ * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns
+ * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no
+ * successors.
+ */
class ExitFunctionInstruction extends Instruction {
ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction }
}
+/**
+ * An instruction whose result is a constant value.
+ */
class ConstantInstruction extends ConstantValueInstruction {
ConstantInstruction() { getOpcode() instanceof Opcode::Constant }
}
+/**
+ * An instruction whose result is a constant value of integer or Boolean type.
+ */
class IntegerConstantInstruction extends ConstantInstruction {
- IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
+ IntegerConstantInstruction() {
+ exists(IRType resultType |
+ resultType = getResultIRType() and
+ (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
+ )
+ }
}
+/**
+ * An instruction whose result is a constant value of floating-point type.
+ */
class FloatConstantInstruction extends ConstantInstruction {
- FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
+ FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
}
+/**
+ * An instruction whose result is the address of a string literal.
+ */
class StringConstantInstruction extends VariableInstruction {
override IRStringLiteral var;
final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) }
+ /**
+ * Gets the string literal whose address is returned by this instruction.
+ */
final Language::StringLiteral getValue() { result = var.getLiteral() }
}
+/**
+ * An instruction whose result is computed from two operands.
+ */
class BinaryInstruction extends Instruction {
BinaryInstruction() { getOpcode() instanceof BinaryOpcode }
+ /**
+ * Gets the left operand of this binary instruction.
+ */
final LeftOperand getLeftOperand() { result = getAnOperand() }
+ /**
+ * Gets the right operand of this binary instruction.
+ */
final RightOperand getRightOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the value of the left operand of this binary
+ * instruction.
+ */
final Instruction getLeft() { result = getLeftOperand().getDef() }
+ /**
+ * Gets the instruction whose result provides the value of the right operand of this binary
+ * instruction.
+ */
final Instruction getRight() { result = getRightOperand().getDef() }
/**
@@ -634,121 +961,301 @@ class BinaryInstruction extends Instruction {
}
}
+/**
+ * An instruction that computes the result of an arithmetic operation.
+ */
class ArithmeticInstruction extends Instruction {
ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode }
}
+/**
+ * An instruction that performs an arithmetic operation on two numeric operands.
+ */
class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { }
+/**
+ * An instruction whose result is computed by performing an arithmetic operation on a single
+ * numeric operand.
+ */
class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { }
+/**
+ * An instruction that computes the sum of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is
+ * performed according to IEEE-754.
+ */
class AddInstruction extends BinaryArithmeticInstruction {
AddInstruction() { getOpcode() instanceof Opcode::Add }
}
+/**
+ * An instruction that computes the difference of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed
+ * according to IEEE-754.
+ */
class SubInstruction extends BinaryArithmeticInstruction {
SubInstruction() { getOpcode() instanceof Opcode::Sub }
}
+/**
+ * An instruction that computes the product of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is
+ * performed according to IEEE-754.
+ */
class MulInstruction extends BinaryArithmeticInstruction {
MulInstruction() { getOpcode() instanceof Opcode::Mul }
}
+/**
+ * An instruction that computes the quotient of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * division by zero or integer overflow is undefined. Floating-point division is performed according
+ * to IEEE-754.
+ */
class DivInstruction extends BinaryArithmeticInstruction {
DivInstruction() { getOpcode() instanceof Opcode::Div }
}
+/**
+ * An instruction that computes the remainder of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type. The result of
+ * division by zero or integer overflow is undefined.
+ */
class RemInstruction extends BinaryArithmeticInstruction {
RemInstruction() { getOpcode() instanceof Opcode::Rem }
}
+/**
+ * An instruction that negates a single numeric operand.
+ *
+ * The operand must have a numeric type, which will also be the result type. The result of integer
+ * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation
+ * is performed according to IEEE-754.
+ */
class NegateInstruction extends UnaryArithmeticInstruction {
NegateInstruction() { getOpcode() instanceof Opcode::Negate }
}
+/**
+ * An instruction that computes the result of a bitwise operation.
+ */
class BitwiseInstruction extends Instruction {
BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode }
}
+/**
+ * An instruction that performs a bitwise operation on two integer operands.
+ */
class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { }
+/**
+ * An instruction that performs a bitwise operation on a single integer operand.
+ */
class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { }
+/**
+ * An instruction that computes the bitwise "and" of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type.
+ */
class BitAndInstruction extends BinaryBitwiseInstruction {
BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd }
}
+/**
+ * An instruction that computes the bitwise "or" of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type.
+ */
class BitOrInstruction extends BinaryBitwiseInstruction {
BitOrInstruction() { getOpcode() instanceof Opcode::BitOr }
}
+/**
+ * An instruction that computes the bitwise "xor" of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type.
+ */
class BitXorInstruction extends BinaryBitwiseInstruction {
BitXorInstruction() { getOpcode() instanceof Opcode::BitXor }
}
+/**
+ * An instruction that shifts its left operand to the left by the number of bits specified by its
+ * right operand.
+ *
+ * Both operands must have an integer type. The result has the same type as the left operand. The
+ * rightmost bits are zero-filled.
+ */
class ShiftLeftInstruction extends BinaryBitwiseInstruction {
ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft }
}
+/**
+ * An instruction that shifts its left operand to the right by the number of bits specified by its
+ * right operand.
+ *
+ * Both operands must have an integer type. The result has the same type as the left operand. If the
+ * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand
+ * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit
+ * of the left operand.
+ */
class ShiftRightInstruction extends BinaryBitwiseInstruction {
ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight }
}
+/**
+ * An instruction that performs a binary arithmetic operation involving at least one pointer
+ * operand.
+ */
class PointerArithmeticInstruction extends BinaryInstruction {
int elementSize;
PointerArithmeticInstruction() {
getOpcode() instanceof PointerArithmeticOpcode and
- elementSize = Construction::getInstructionElementSize(this)
+ elementSize = Raw::getInstructionElementSize(this)
}
final override string getImmediateString() { result = elementSize.toString() }
+ /**
+ * Gets the size of the elements pointed to by the pointer operands, in bytes.
+ *
+ * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer
+ * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the
+ * element size to compute the actual number of bytes added to or subtracted from the pointer
+ * address. When computing the integer difference between two pointers (`PointerDiffInstruction`),
+ * the result is computed by computing the difference between the two pointer byte addresses, then
+ * dividing that byte count by the element size.
+ */
final int getElementSize() { result = elementSize }
}
+/**
+ * An instruction that adds or subtracts an integer offset from a pointer.
+ */
class PointerOffsetInstruction extends PointerArithmeticInstruction {
PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode }
}
+/**
+ * An instruction that adds an integer offset to a pointer.
+ *
+ * The result is the byte address computed by adding the value of the right (integer) operand,
+ * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer
+ * overflow is undefined.
+ */
class PointerAddInstruction extends PointerOffsetInstruction {
PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd }
}
+/**
+ * An instruction that subtracts an integer offset from a pointer.
+ *
+ * The result is the byte address computed by subtracting the value of the right (integer) operand,
+ * multiplied by the element size, from the value of the left (pointer) operand. The result of
+ * pointer underflow is undefined.
+ */
class PointerSubInstruction extends PointerOffsetInstruction {
PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub }
}
+/**
+ * An instruction that computes the difference between two pointers.
+ *
+ * Both operands must have the same pointer type. The result must have an integer type whose size is
+ * the same as that of the pointer operands. The result is computed by subtracting the byte address
+ * in the right operand from the byte address in the left operand, and dividing by the element size.
+ * If the difference in byte addresses is not divisible by the element size, the result is
+ * undefined.
+ */
class PointerDiffInstruction extends PointerArithmeticInstruction {
PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff }
}
+/**
+ * An instruction whose result is computed from a single operand.
+ */
class UnaryInstruction extends Instruction {
UnaryInstruction() { getOpcode() instanceof UnaryOpcode }
+ /**
+ * Gets the sole operand of this instruction.
+ */
final UnaryOperand getUnaryOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the sole operand of this instruction.
+ */
final Instruction getUnary() { result = getUnaryOperand().getDef() }
}
+/**
+ * An instruction that converts the value of its operand to a value of a different type.
+ */
class ConvertInstruction extends UnaryInstruction {
ConvertInstruction() { getOpcode() instanceof Opcode::Convert }
}
+/**
+ * An instruction that converts the address of a polymorphic object to the address of a different
+ * subobject of the same polymorphic object, returning a null address if the dynamic type of the
+ * object is not compatible with the result type.
+ *
+ * If the operand holds a null address, the result is a null address.
+ *
+ * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or
+ * `as` expression.
+ */
class CheckedConvertOrNullInstruction extends UnaryInstruction {
CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull }
}
/**
- * Represents an instruction that converts between two addresses
- * related by inheritance.
+ * An instruction that converts the address of a polymorphic object to the address of a different
+ * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object
+ * is not compatible with the result type.
+ *
+ * If the operand holds a null address, the result is a null address.
+ *
+ * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast
+ * expression.
+ */
+class CheckedConvertOrThrowInstruction extends UnaryInstruction {
+ CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow }
+}
+
+/**
+ * An instruction that returns the address of the complete object that contains the subobject
+ * pointed to by its operand.
+ *
+ * If the operand holds a null address, the result is a null address.
+ *
+ * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to
+ * the most-derived object.
+ */
+class CompleteObjectAddressInstruction extends UnaryInstruction {
+ CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress }
+}
+
+/**
+ * An instruction that converts the address of an object to the address of a different subobject of
+ * the same object, without any type checking at runtime.
*/
class InheritanceConversionInstruction extends UnaryInstruction {
Language::Class baseClass;
Language::Class derivedClass;
InheritanceConversionInstruction() {
- Construction::getInstructionInheritance(this, baseClass, derivedClass)
+ Raw::getInstructionInheritance(this, baseClass, derivedClass)
}
final override string getImmediateString() {
@@ -778,59 +1285,91 @@ class InheritanceConversionInstruction extends UnaryInstruction {
}
/**
- * Represents an instruction that converts from the address of a derived class
- * to the address of a base class.
+ * An instruction that converts from the address of a derived class to the address of a base class.
*/
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode }
}
/**
- * Represents an instruction that converts from the address of a derived class
- * to the address of a direct non-virtual base class.
+ * An instruction that converts from the address of a derived class to the address of a direct
+ * non-virtual base class.
+ *
+ * If the operand holds a null address, the result is a null address.
*/
class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase }
}
/**
- * Represents an instruction that converts from the address of a derived class
- * to the address of a virtual base class.
+ * An instruction that converts from the address of a derived class to the address of a virtual base
+ * class.
+ *
+ * If the operand holds a null address, the result is a null address.
*/
class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
}
/**
- * Represents an instruction that converts from the address of a base class
- * to the address of a direct non-virtual derived class.
+ * An instruction that converts from the address of a base class to the address of a direct
+ * non-virtual derived class.
+ *
+ * If the operand holds a null address, the result is a null address.
*/
class ConvertToDerivedInstruction extends InheritanceConversionInstruction {
ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived }
}
+/**
+ * An instruction that computes the bitwise complement of its operand.
+ *
+ * The operand must have an integer type, which will also be the result type.
+ */
class BitComplementInstruction extends UnaryBitwiseInstruction {
BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement }
}
+/**
+ * An instruction that computes the logical complement of its operand.
+ *
+ * The operand must have a Boolean type, which will also be the result type.
+ */
class LogicalNotInstruction extends UnaryInstruction {
LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot }
}
+/**
+ * An instruction that compares two numeric operands.
+ */
class CompareInstruction extends BinaryInstruction {
CompareInstruction() { getOpcode() instanceof CompareOpcode }
}
+/**
+ * An instruction that returns a `true` result if its operands are equal.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are
+ * unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareEQInstruction extends CompareInstruction {
CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ }
}
+/**
+ * An instruction that returns a `true` result if its operands are not equal.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if `left != right` or if the two operands are unordered, and `false` if
+ * `left == right`. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareNEInstruction extends CompareInstruction {
CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE }
}
/**
- * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`.
+ * An instruction that does a relative comparison of two values, such as `<` or `>=`.
*/
class RelationalInstruction extends CompareInstruction {
RelationalInstruction() { getOpcode() instanceof RelationalOpcode }
@@ -857,6 +1396,13 @@ class RelationalInstruction extends CompareInstruction {
predicate isStrict() { none() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is less than its right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareLTInstruction extends RelationalInstruction {
CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT }
@@ -867,6 +1413,13 @@ class CompareLTInstruction extends RelationalInstruction {
override predicate isStrict() { any() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is greater than its right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareGTInstruction extends RelationalInstruction {
CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT }
@@ -877,6 +1430,14 @@ class CompareGTInstruction extends RelationalInstruction {
override predicate isStrict() { any() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is less than or equal to its
+ * right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareLEInstruction extends RelationalInstruction {
CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE }
@@ -887,6 +1448,14 @@ class CompareLEInstruction extends RelationalInstruction {
override predicate isStrict() { none() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is greater than or equal to its
+ * right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareGEInstruction extends RelationalInstruction {
CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE }
@@ -897,15 +1466,32 @@ class CompareGEInstruction extends RelationalInstruction {
override predicate isStrict() { none() }
}
+/**
+ * An instruction that branches to one of multiple successor instructions based on the value of an
+ * integer operand.
+ *
+ * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each
+ * representing the branch that will be taken if the controlling expression is within the range
+ * specified for that case edge. The range of a case edge must be disjoint from the range of each
+ * other case edge.
+ *
+ * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`,
+ * representing the branch that will be taken if the controlling expression is not within the range
+ * of any case edge.
+ */
class SwitchInstruction extends Instruction {
SwitchInstruction() { getOpcode() instanceof Opcode::Switch }
+ /** Gets the operand that provides the integer value controlling the switch. */
final ConditionOperand getExpressionOperand() { result = getAnOperand() }
+ /** Gets the instruction whose result provides the integer value controlling the switch. */
final Instruction getExpression() { result = getExpressionOperand().getDef() }
+ /** Gets the successor instructions along the case edges of the switch. */
final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) }
+ /** Gets the successor instruction along the default edge of the switch, if any. */
final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) }
}
@@ -936,7 +1522,7 @@ class CallInstruction extends Instruction {
* Gets the `Function` that the call targets, if this is statically known.
*/
final Language::Function getStaticCallTarget() {
- result = getCallTarget().(FunctionInstruction).getFunctionSymbol()
+ result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol()
}
/**
@@ -981,6 +1567,9 @@ class CallInstruction extends Instruction {
class SideEffectInstruction extends Instruction {
SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode }
+ /**
+ * Gets the instruction whose execution causes this side effect.
+ */
final Instruction getPrimaryInstruction() {
result = Construction::getPrimaryInstructionForSideEffect(this)
}
@@ -996,9 +1585,10 @@ class CallSideEffectInstruction extends SideEffectInstruction {
/**
* An instruction representing the side effect of a function call on any memory
- * that might be read by that call. This instruction is emitted instead of
- * `CallSideEffectInstruction` when it's certain that the call target cannot
- * write to escaped memory.
+ * that might be read by that call.
+ *
+ * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the
+ * call target cannot write to escaped memory.
*/
class CallReadSideEffectInstruction extends SideEffectInstruction {
CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect }
@@ -1046,7 +1636,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction {
getOpcode() instanceof Opcode::SizedBufferReadSideEffect
}
- Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+ /**
+ * Gets the operand that holds the number of bytes read from the buffer.
+ */
+ final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the number of bytes read from the buffer.
+ */
+ final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() }
}
/**
@@ -1056,7 +1654,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction {
class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction {
WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
- Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
+ /**
+ * Get the operand that holds the address of the memory to be written.
+ */
+ final AddressOperand getDestinationAddressOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the address of the memory to be written.
+ */
+ Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() }
}
/**
@@ -1087,11 +1693,20 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
}
- Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+ /**
+ * Gets the operand that holds the number of bytes written to the buffer.
+ */
+ final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the number of bytes written to the buffer.
+ */
+ final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() }
}
/**
* An instruction representing the potential write of an indirect parameter within a function call.
+ *
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
* written.
*/
@@ -1103,6 +1718,7 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
/**
* An instruction representing the write of an indirect buffer parameter within a function call.
+ *
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
*/
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
@@ -1111,6 +1727,7 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
/**
* An instruction representing the write of an indirect buffer parameter within a function call.
+ *
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
*/
class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
@@ -1118,11 +1735,19 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
}
- Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+ /**
+ * Gets the operand that holds the number of bytes written to the buffer.
+ */
+ final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the number of bytes written to the buffer.
+ */
+ final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() }
}
/**
- * An instruction representing the initial value of newly allocated memory, e.g. the result of a
+ * An instruction representing the initial value of newly allocated memory, such as the result of a
* call to `malloc`.
*/
class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
@@ -1211,7 +1836,7 @@ class CatchByTypeInstruction extends CatchInstruction {
CatchByTypeInstruction() {
getOpcode() instanceof Opcode::CatchByType and
- exceptionType = Construction::getInstructionExceptionType(this)
+ exceptionType = Raw::getInstructionExceptionType(this)
}
final override string getImmediateString() { result = exceptionType.toString() }
@@ -1337,29 +1962,43 @@ class ChiInstruction extends Instruction {
* Gets the operand that represents the new value written by the memory write.
*/
final Instruction getPartial() { result = getPartialOperand().getDef() }
+
+ /**
+ * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand.
+ */
+ final predicate getUpdatedInterval(int startBit, int endBit) {
+ Construction::getIntervalUpdatedByChi(this, startBit, endBit)
+ }
}
/**
- * An instruction representing unreachable code. Inserted in place of the original target
- * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
- * infeasible.
+ * An instruction representing unreachable code.
+ *
+ * This instruction is inserted in place of the original target instruction of a `ConditionalBranch`
+ * or `Switch` instruction where that particular edge is infeasible.
*/
class UnreachedInstruction extends Instruction {
UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached }
}
/**
- * An instruction representing a built-in operation. This is used to represent
- * operations such as access to variable argument lists.
+ * An instruction representing a built-in operation.
+ *
+ * This is used to represent a variety of intrinsic operations provided by the compiler
+ * implementation, such as vector arithmetic.
*/
class BuiltInOperationInstruction extends Instruction {
Language::BuiltInOperation operation;
BuiltInOperationInstruction() {
getOpcode() instanceof BuiltInOperationOpcode and
- operation = Construction::getInstructionBuiltInOperation(this)
+ operation = Raw::getInstructionBuiltInOperation(this)
}
+ /**
+ * Gets the language-specific `BuiltInOperation` object that specifies the operation that is
+ * performed by this instruction.
+ */
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
}
@@ -1372,3 +2011,59 @@ class BuiltInInstruction extends BuiltInOperationInstruction {
final override string getImmediateString() { result = getBuiltInOperation().toString() }
}
+
+/**
+ * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter.
+ *
+ * The operand specifies the address of the `IREllipsisVariable` used to represent the `...`
+ * parameter. The result is a `va_list` that initially refers to the first argument that was passed
+ * to the `...` parameter.
+ */
+class VarArgsStartInstruction extends UnaryInstruction {
+ VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart }
+}
+
+/**
+ * An instruction that cleans up a `va_list` after it is no longer in use.
+ *
+ * The operand specifies the address of the `va_list` to clean up. This instruction does not return
+ * a result.
+ */
+class VarArgsEndInstruction extends UnaryInstruction {
+ VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd }
+}
+
+/**
+ * An instruction that returns the address of the argument currently pointed to by a `va_list`.
+ *
+ * The operand is the `va_list` that points to the argument. The result is the address of the
+ * argument.
+ */
+class VarArgInstruction extends UnaryInstruction {
+ VarArgInstruction() { getOpcode() instanceof Opcode::VarArg }
+}
+
+/**
+ * An instruction that modifies a `va_list` to point to the next argument that was passed to the
+ * `...` parameter.
+ *
+ * The operand is the current `va_list`. The result is an updated `va_list` that points to the next
+ * argument of the `...` parameter.
+ */
+class NextVarArgInstruction extends UnaryInstruction {
+ NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg }
+}
+
+/**
+ * An instruction that allocates a new object on the managed heap.
+ *
+ * This instruction is used to represent the allocation of a new object in C# using the `new`
+ * expression. This instruction does not invoke a constructor for the object. Instead, there will be
+ * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the
+ * result of the `NewObj` as the `this` argument.
+ *
+ * The result is the address of the newly allocated object.
+ */
+class NewObjInstruction extends Instruction {
+ NewObjInstruction() { getOpcode() instanceof Opcode::NewObj }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll
index f82704094c8e..a12e35d471b8 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes that represent the input values of IR instructions.
+ */
+
private import internal.IRInternal
private import Instruction
private import IRBlock
@@ -75,13 +79,21 @@ private PhiOperandBase phiOperand(
}
/**
- * A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
+ * An operand of an `Instruction`. The operand represents a use of the result of one instruction
+ * (the defining instruction) in another instruction (the use instruction)
*/
class Operand extends TOperand {
+ /** Gets a textual representation of this element. */
string toString() { result = "Operand" }
+ /**
+ * Gets the location of the source code for this operand.
+ */
final Language::Location getLocation() { result = getUse().getLocation() }
+ /**
+ * Gets the function that contains this operand.
+ */
final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() }
/**
@@ -139,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
@@ -268,8 +285,13 @@ 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() }
+ /**
+ * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`.
+ */
final OperandTag getOperandTag() { result = tag }
}
@@ -292,6 +314,9 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
}
}
+/**
+ * A memory operand other than the operand of a `Phi` instruction.
+ */
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
override MemoryOperandTag tag;
@@ -311,8 +336,19 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper
not Construction::isInCycle(useInstr) and
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
}
+
+ /**
+ * Holds if the operand totally overlaps with its definition and consumes the
+ * bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition.
+ */
+ predicate getUsedInterval(int startBitOffset, int endBitOffset) {
+ Construction::getUsedInterval(this, startBitOffset, endBitOffset)
+ }
}
+/**
+ * A memory operand whose type may be different from the type of the result of its definition.
+ */
class TypedOperand extends NonPhiMemoryOperand {
override TypedOperandTag tag;
@@ -416,6 +452,9 @@ class PositionalArgumentOperand extends ArgumentOperand {
final int getIndex() { result = tag.getArgIndex() }
}
+/**
+ * An operand representing memory read as a side effect of evaluating another instruction.
+ */
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
}
@@ -445,6 +484,8 @@ 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 d9c0df44e12e..59dadee71545 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/PrintIR.qll
@@ -1,3 +1,13 @@
+/**
+ * Outputs a representation of the IR as a control flow graph.
+ *
+ * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
+ * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
+ * uses, however, it is better to write a query that imports `PrintIR.qll`, extends
+ * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to
+ * dump.
+ */
+
private import internal.IRInternal
private import IR
private import internal.PrintIRImports as Imports
@@ -9,6 +19,7 @@ private newtype TPrintIRConfiguration = MkPrintIRConfiguration()
* The query can extend this class to control which functions are printed.
*/
class PrintIRConfiguration extends TPrintIRConfiguration {
+ /** Gets a textual representation of this configuration. */
string toString() { result = "PrintIRConfiguration" }
/**
@@ -39,6 +50,37 @@ 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
@@ -47,7 +89,7 @@ private newtype TPrintableIRNode =
/**
* A node to be emitted in the IR graph.
*/
-abstract class PrintableIRNode extends TPrintableIRNode {
+abstract private class PrintableIRNode extends TPrintableIRNode {
abstract string toString();
/**
@@ -98,7 +140,7 @@ abstract class PrintableIRNode extends TPrintableIRNode {
/**
* An IR graph node representing a `IRFunction` object.
*/
-class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction {
+private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction {
IRFunction irFunc;
PrintableIRFunction() { this = TPrintableIRFunction(irFunc) }
@@ -129,7 +171,7 @@ class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction {
/**
* An IR graph node representing an `IRBlock` object.
*/
-class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
+private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
IRBlock block;
PrintableIRBlock() { this = TPrintableIRBlock(block) }
@@ -161,7 +203,7 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
/**
* An IR graph node representing an `Instruction`.
*/
-class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
+private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
Instruction instr;
PrintableInstruction() { this = TPrintableInstruction(instr) }
@@ -179,7 +221,7 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
|
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()) + " = " +
@@ -199,6 +241,22 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
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) {
@@ -224,6 +282,9 @@ private string getPaddingString(int n) {
n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " "
}
+/**
+ * Holds if `node` belongs to the output graph, and its property `key` has the given `value`.
+ */
query predicate nodes(PrintableIRNode node, string key, string value) {
value = node.getProperty(key)
}
@@ -237,6 +298,10 @@ private int getSuccessorIndex(IRBlock pred, IRBlock succ) {
)
}
+/**
+ * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key`
+ * has the given `value`.
+ */
query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) {
exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock |
predBlock = pred.getBlock() and
@@ -256,6 +321,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key,
)
}
+/**
+ * Holds if `parent` is the parent node of `child` in the output graph.
+ */
query predicate parents(PrintableIRNode child, PrintableIRNode parent) {
parent = child.getParent()
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
index 5200da91a55c..f6e27e080f7d 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
@@ -1,6 +1,9 @@
private import cpp
import semmle.code.cpp.ir.implementation.raw.IR
private import semmle.code.cpp.ir.implementation.internal.OperandTag
+private import semmle.code.cpp.ir.implementation.internal.IRFunctionBase
+private import semmle.code.cpp.ir.implementation.internal.TInstruction
+private import semmle.code.cpp.ir.implementation.internal.TIRVariable
private import semmle.code.cpp.ir.internal.CppType
private import semmle.code.cpp.ir.internal.Overlap
private import semmle.code.cpp.ir.internal.TempVariableTag
@@ -12,34 +15,36 @@ private import TranslatedStmt
private import TranslatedFunction
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
- instruction = MkInstruction(result, _)
+ instruction = TRawInstruction(result, _)
}
-InstructionTag getInstructionTag(Instruction instruction) { instruction = MkInstruction(_, result) }
-
-import Cached
+InstructionTag getInstructionTag(Instruction instruction) {
+ instruction = TRawInstruction(_, result)
+}
+/**
+ * Provides the portion of the parameterized IR interface that is used to construct the initial
+ * "raw" stage of the IR. The other stages of the IR do not expose these predicates.
+ */
cached
-private module Cached {
+module Raw {
+ class InstructionTag1 = TranslatedElement;
+
+ class InstructionTag2 = InstructionTag;
+
cached
predicate functionHasIR(Function func) { exists(getTranslatedFunction(func)) }
cached
- newtype TInstruction =
- MkInstruction(TranslatedElement element, InstructionTag tag) {
- element.hasInstruction(_, tag, _)
- }
+ predicate hasInstruction(TranslatedElement element, InstructionTag tag) {
+ element.hasInstruction(_, tag, _)
+ }
cached
predicate hasUserVariable(Function func, Variable var, CppType type) {
getTranslatedFunction(func).hasUserVariable(var, type)
}
- cached
- predicate hasThisVariable(Function func, CppType type) {
- type = getTypeForGLValue(getTranslatedFunction(func).getThisType())
- }
-
cached
predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) {
exists(TranslatedElement element |
@@ -64,232 +69,7 @@ private module Cached {
}
cached
- predicate hasModeledMemoryResult(Instruction instruction) { none() }
-
- cached
- predicate hasConflatedMemoryResult(Instruction instruction) {
- instruction instanceof AliasedDefinitionInstruction
- or
- instruction.getOpcode() instanceof Opcode::InitializeNonLocal
- }
-
- cached
- Expr getInstructionConvertedResultExpression(Instruction instruction) {
- exists(TranslatedExpr translatedExpr |
- translatedExpr = getTranslatedExpr(result) and
- instruction = translatedExpr.getResult() and
- // Only associate `instruction` with this expression if the translated
- // expression actually produced the instruction; not if it merely
- // forwarded the result of another translated expression.
- instruction = translatedExpr.getInstruction(_)
- )
- }
-
- cached
- Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
- result = getInstructionConvertedResultExpression(instruction).getUnconverted()
- }
-
- cached
- Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
- result =
- getInstructionTranslatedElement(instruction)
- .getInstructionRegisterOperand(getInstructionTag(instruction), tag)
- }
-
- cached
- Instruction getMemoryOperandDefinition(
- Instruction instruction, MemoryOperandTag tag, Overlap overlap
- ) {
- none()
- }
-
- /** Gets a non-phi instruction that defines an operand of `instr`. */
- private Instruction getNonPhiOperandDef(Instruction instr) {
- result = getRegisterOperandDefinition(instr, _)
- or
- result = getMemoryOperandDefinition(instr, _, _)
- }
-
- /**
- * Gets a non-phi instruction that defines an operand of `instr` but only if
- * both `instr` and the result have neighbor on the other side of the edge
- * between them. This is a necessary condition for being in a cycle, and it
- * removes about two thirds of the tuples that would otherwise be in this
- * predicate.
- */
- private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
- result = getNonPhiOperandDef(instr) and
- exists(getNonPhiOperandDef(result)) and
- instr = getNonPhiOperandDef(_)
- }
-
- /**
- * Holds if `instr` is part of a cycle in the operand graph that doesn't go
- * through a phi instruction and therefore should be impossible.
- *
- * If such cycles are present, either due to a programming error in the IR
- * generation or due to a malformed database, it can cause infinite loops in
- * analyses that assume a cycle-free graph of non-phi operands. Therefore it's
- * better to remove these operands than to leave cycles in the operand graph.
- */
- pragma[noopt]
- cached
- predicate isInCycle(Instruction instr) {
- instr instanceof Instruction and
- getNonPhiOperandDefOfIntermediate+(instr) = instr
- }
-
- cached
- CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
- // For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
- // the result type of the load.
- tag instanceof LoadOperandTag and
- result = instruction.(LoadInstruction).getResultLanguageType()
- or
- not instruction instanceof LoadInstruction and
- result =
- getInstructionTranslatedElement(instruction)
- .getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
- }
-
- cached
- Instruction getPhiOperandDefinition(
- PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
- ) {
- none()
- }
-
- cached
- Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
-
- cached
- Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
- result =
- getInstructionTranslatedElement(instruction)
- .getInstructionSuccessor(getInstructionTag(instruction), kind)
- }
-
- /**
- * Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`-->
- * `targetInstruction` is a back edge under the condition that
- * `requiredAncestor` is an ancestor of `sourceElement`.
- */
- private predicate backEdgeCandidate(
- TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor,
- Instruction targetInstruction, EdgeKind kind
- ) {
- // While loop:
- // Any edge from within the body of the loop to the condition of the loop
- // is a back edge. This includes edges from `continue` and the fall-through
- // edge(s) after the last instruction(s) in the body.
- exists(TranslatedWhileStmt s |
- targetInstruction = s.getFirstConditionInstruction() and
- targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
- requiredAncestor = s.getBody()
- )
- or
- // Do-while loop:
- // The back edge should be the edge(s) from the condition to the
- // body. This ensures that it's the back edge that will be pruned in a `do
- // { ... } while (0)` statement. Note that all `continue` statements in a
- // do-while loop produce forward edges.
- exists(TranslatedDoStmt s |
- targetInstruction = s.getBody().getFirstInstruction() and
- targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
- requiredAncestor = s.getCondition()
- )
- or
- // For loop:
- // Any edge from within the body or update of the loop to the condition of
- // the loop is a back edge. When there is no loop update expression, this
- // includes edges from `continue` and the fall-through edge(s) after the
- // last instruction(s) in the body. A for loop may not have a condition, in
- // which case `getFirstConditionInstruction` returns the body instead.
- exists(TranslatedForStmt s |
- targetInstruction = s.getFirstConditionInstruction() and
- targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
- (
- requiredAncestor = s.getUpdate()
- or
- not exists(s.getUpdate()) and
- requiredAncestor = s.getBody()
- )
- )
- or
- // Range-based for loop:
- // Any edge from within the update of the loop to the condition of
- // the loop is a back edge.
- exists(TranslatedRangeBasedForStmt s |
- targetInstruction = s.getCondition().getFirstInstruction() and
- targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
- requiredAncestor = s.getUpdate()
- )
- }
-
- private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) {
- backEdgeCandidate(jumpSource, _, _, _, _) and
- ancestor = jumpSource
- or
- // For performance, we don't want a fastTC here
- jumpSourceHasAncestor(jumpSource, ancestor.getAChild())
- }
-
- cached
- Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
- exists(
- TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor
- |
- backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and
- jumpSourceHasAncestor(sourceElement, requiredAncestor) and
- instruction = sourceElement.getInstruction(sourceTag)
- )
- or
- // Goto statement:
- // As a conservative approximation, any edge out of `goto` is a back edge
- // unless it goes strictly forward in the program text. A `goto` whose
- // source and target are both inside a macro will be seen as having the
- // same location for source and target, so we conservatively assume that
- // such a `goto` creates a back edge.
- exists(TranslatedElement s, GotoStmt goto |
- not isStrictlyForwardGoto(goto) and
- goto = s.getAST() and
- exists(InstructionTag tag |
- result = s.getInstructionSuccessor(tag, kind) and
- instruction = s.getInstruction(tag)
- )
- )
- }
-
- /** Holds if `goto` jumps strictly forward in the program text. */
- private predicate isStrictlyForwardGoto(GotoStmt goto) {
- goto.getLocation().isBefore(goto.getTarget().getLocation())
- }
-
- cached
- Locatable getInstructionAST(Instruction instruction) {
- result = getInstructionTranslatedElement(instruction).getAST()
- }
-
- cached
- CppType getInstructionResultType(Instruction instruction) {
- getInstructionTranslatedElement(instruction)
- .hasInstruction(_, getInstructionTag(instruction), result)
- }
-
- cached
- Opcode getInstructionOpcode(Instruction instruction) {
- getInstructionTranslatedElement(instruction)
- .hasInstruction(result, getInstructionTag(instruction), _)
- }
-
- cached
- IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
- result.getFunction() = getInstructionTranslatedElement(instruction).getFunction()
- }
-
- cached
- IRVariable getInstructionVariable(Instruction instruction) {
+ TIRVariable getInstructionVariable(Instruction instruction) {
exists(TranslatedElement element, InstructionTag tag |
element = getInstructionTranslatedElement(instruction) and
tag = getInstructionTag(instruction) and
@@ -302,10 +82,9 @@ private module Cached {
cached
Field getInstructionField(Instruction instruction) {
- exists(TranslatedElement element, InstructionTag tag |
- instructionOrigin(instruction, element, tag) and
- result = element.getInstructionField(tag)
- )
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionField(getInstructionTag(instruction))
}
cached
@@ -324,10 +103,9 @@ private module Cached {
cached
int getInstructionIndex(Instruction instruction) {
- exists(TranslatedElement element, InstructionTag tag |
- instructionOrigin(instruction, element, tag) and
- result = element.getInstructionIndex(tag)
- )
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionIndex(getInstructionTag(instruction))
}
cached
@@ -350,20 +128,11 @@ private module Cached {
.getInstructionInheritance(getInstructionTag(instruction), baseClass, derivedClass)
}
- pragma[noinline]
- private predicate instructionOrigin(
- Instruction instruction, TranslatedElement element, InstructionTag tag
- ) {
- element = getInstructionTranslatedElement(instruction) and
- tag = getInstructionTag(instruction)
- }
-
cached
int getInstructionElementSize(Instruction instruction) {
- exists(TranslatedElement element, InstructionTag tag |
- instructionOrigin(instruction, element, tag) and
- result = element.getInstructionElementSize(tag)
- )
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionElementSize(getInstructionTag(instruction))
}
cached
@@ -372,22 +141,237 @@ private module Cached {
}
cached
- int getInstructionResultSize(Instruction instruction) {
- exists(TranslatedElement element, InstructionTag tag |
- instructionOrigin(instruction, element, tag) and
- result = element.getInstructionResultSize(tag)
+ Expr getInstructionConvertedResultExpression(Instruction instruction) {
+ exists(TranslatedExpr translatedExpr |
+ translatedExpr = getTranslatedExpr(result) and
+ instruction = translatedExpr.getResult() and
+ // Only associate `instruction` with this expression if the translated
+ // expression actually produced the instruction; not if it merely
+ // forwarded the result of another translated expression.
+ instruction = translatedExpr.getInstruction(_)
)
}
cached
- Instruction getPrimaryInstructionForSideEffect(Instruction instruction) {
- exists(TranslatedElement element, InstructionTag tag |
- instructionOrigin(instruction, element, tag) and
- result = element.getPrimaryInstructionForSideEffect(tag)
- )
+ Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
+ result = getInstructionConvertedResultExpression(instruction).getUnconverted()
}
}
+class TStageInstruction = TRawInstruction;
+
+predicate hasInstruction(TRawInstruction instr) { any() }
+
+predicate hasModeledMemoryResult(Instruction instruction) { none() }
+
+predicate hasConflatedMemoryResult(Instruction instruction) {
+ instruction instanceof AliasedDefinitionInstruction
+ or
+ instruction.getOpcode() instanceof Opcode::InitializeNonLocal
+}
+
+Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionRegisterOperand(getInstructionTag(instruction), tag)
+}
+
+Instruction getMemoryOperandDefinition(
+ Instruction instruction, MemoryOperandTag tag, Overlap overlap
+) {
+ none()
+}
+
+/**
+ * Holds if the partial operand of this `ChiInstruction` updates the bit range
+ * `[startBitOffset, endBitOffset)` of the total operand.
+ */
+predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBit, int endBit) { none() }
+
+/**
+ * Holds if the operand totally overlaps with its definition and consumes the
+ * bit range `[startBitOffset, endBitOffset)`.
+ */
+predicate getUsedInterval(Operand operand, int startBit, int endBit) { none() }
+
+/** Gets a non-phi instruction that defines an operand of `instr`. */
+private Instruction getNonPhiOperandDef(Instruction instr) {
+ result = getRegisterOperandDefinition(instr, _)
+ or
+ result = getMemoryOperandDefinition(instr, _, _)
+}
+
+/**
+ * Gets a non-phi instruction that defines an operand of `instr` but only if
+ * both `instr` and the result have neighbor on the other side of the edge
+ * between them. This is a necessary condition for being in a cycle, and it
+ * removes about two thirds of the tuples that would otherwise be in this
+ * predicate.
+ */
+private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
+ result = getNonPhiOperandDef(instr) and
+ exists(getNonPhiOperandDef(result)) and
+ instr = getNonPhiOperandDef(_)
+}
+
+/**
+ * Holds if `instr` is part of a cycle in the operand graph that doesn't go
+ * through a phi instruction and therefore should be impossible.
+ *
+ * If such cycles are present, either due to a programming error in the IR
+ * generation or due to a malformed database, it can cause infinite loops in
+ * analyses that assume a cycle-free graph of non-phi operands. Therefore it's
+ * better to remove these operands than to leave cycles in the operand graph.
+ */
+pragma[noopt]
+predicate isInCycle(Instruction instr) {
+ instr instanceof Instruction and
+ getNonPhiOperandDefOfIntermediate+(instr) = instr
+}
+
+CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
+ // For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
+ // the result type of the load.
+ tag instanceof LoadOperandTag and
+ result = instruction.(LoadInstruction).getResultLanguageType()
+ or
+ not instruction instanceof LoadInstruction and
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
+}
+
+Instruction getPhiOperandDefinition(
+ PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
+) {
+ none()
+}
+
+Instruction getPhiInstructionBlockStart(PhiInstruction instr) { none() }
+
+Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getInstructionSuccessor(getInstructionTag(instruction), kind)
+}
+
+/**
+ * Holds if the CFG edge (`sourceElement`, `sourceTag`) ---`kind`-->
+ * `targetInstruction` is a back edge under the condition that
+ * `requiredAncestor` is an ancestor of `sourceElement`.
+ */
+private predicate backEdgeCandidate(
+ TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor,
+ Instruction targetInstruction, EdgeKind kind
+) {
+ // While loop:
+ // Any edge from within the body of the loop to the condition of the loop
+ // is a back edge. This includes edges from `continue` and the fall-through
+ // edge(s) after the last instruction(s) in the body.
+ exists(TranslatedWhileStmt s |
+ targetInstruction = s.getFirstConditionInstruction() and
+ targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
+ requiredAncestor = s.getBody()
+ )
+ or
+ // Do-while loop:
+ // The back edge should be the edge(s) from the condition to the
+ // body. This ensures that it's the back edge that will be pruned in a `do
+ // { ... } while (0)` statement. Note that all `continue` statements in a
+ // do-while loop produce forward edges.
+ exists(TranslatedDoStmt s |
+ targetInstruction = s.getBody().getFirstInstruction() and
+ targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
+ requiredAncestor = s.getCondition()
+ )
+ or
+ // For loop:
+ // Any edge from within the body or update of the loop to the condition of
+ // the loop is a back edge. When there is no loop update expression, this
+ // includes edges from `continue` and the fall-through edge(s) after the
+ // last instruction(s) in the body. A for loop may not have a condition, in
+ // which case `getFirstConditionInstruction` returns the body instead.
+ exists(TranslatedForStmt s |
+ targetInstruction = s.getFirstConditionInstruction() and
+ targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
+ (
+ requiredAncestor = s.getUpdate()
+ or
+ not exists(s.getUpdate()) and
+ requiredAncestor = s.getBody()
+ )
+ )
+ or
+ // Range-based for loop:
+ // Any edge from within the update of the loop to the condition of
+ // the loop is a back edge.
+ exists(TranslatedRangeBasedForStmt s |
+ targetInstruction = s.getCondition().getFirstInstruction() and
+ targetInstruction = sourceElement.getInstructionSuccessor(sourceTag, kind) and
+ requiredAncestor = s.getUpdate()
+ )
+}
+
+private predicate jumpSourceHasAncestor(TranslatedElement jumpSource, TranslatedElement ancestor) {
+ backEdgeCandidate(jumpSource, _, _, _, _) and
+ ancestor = jumpSource
+ or
+ // For performance, we don't want a fastTC here
+ jumpSourceHasAncestor(jumpSource, ancestor.getAChild())
+}
+
+Instruction getInstructionBackEdgeSuccessor(Instruction instruction, EdgeKind kind) {
+ exists(
+ TranslatedElement sourceElement, InstructionTag sourceTag, TranslatedElement requiredAncestor
+ |
+ backEdgeCandidate(sourceElement, sourceTag, requiredAncestor, result, kind) and
+ jumpSourceHasAncestor(sourceElement, requiredAncestor) and
+ instruction = sourceElement.getInstruction(sourceTag)
+ )
+ or
+ // Goto statement:
+ // As a conservative approximation, any edge out of `goto` is a back edge
+ // unless it goes strictly forward in the program text. A `goto` whose
+ // source and target are both inside a macro will be seen as having the
+ // same location for source and target, so we conservatively assume that
+ // such a `goto` creates a back edge.
+ exists(TranslatedElement s, GotoStmt goto |
+ not isStrictlyForwardGoto(goto) and
+ goto = s.getAST() and
+ exists(InstructionTag tag |
+ result = s.getInstructionSuccessor(tag, kind) and
+ instruction = s.getInstruction(tag)
+ )
+ )
+}
+
+/** Holds if `goto` jumps strictly forward in the program text. */
+private predicate isStrictlyForwardGoto(GotoStmt goto) {
+ goto.getLocation().isBefore(goto.getTarget().getLocation())
+}
+
+Locatable getInstructionAST(TStageInstruction instr) {
+ result = getInstructionTranslatedElement(instr).getAST()
+}
+
+CppType getInstructionResultType(TStageInstruction instr) {
+ getInstructionTranslatedElement(instr).hasInstruction(_, getInstructionTag(instr), result)
+}
+
+Opcode getInstructionOpcode(TStageInstruction instr) {
+ getInstructionTranslatedElement(instr).hasInstruction(result, getInstructionTag(instr), _)
+}
+
+IRFunctionBase getInstructionEnclosingIRFunction(TStageInstruction instr) {
+ result.getFunction() = getInstructionTranslatedElement(instr).getFunction()
+}
+
+Instruction getPrimaryInstructionForSideEffect(SideEffectInstruction instruction) {
+ result =
+ getInstructionTranslatedElement(instruction)
+ .getPrimaryInstructionForSideEffect(getInstructionTag(instruction))
+}
+
import CachedForDebugging
cached
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll
new file mode 100644
index 000000000000..8ec63b7c1cbd
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRFunctionImports.qll
@@ -0,0 +1 @@
+import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll
index 8fd2f662f346..82cc38ac0927 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRInternal.qll
@@ -1,3 +1,4 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import IRConstruction as Construction
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
+import IRConstruction::Raw as Raw
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll
index 15bb66940ea1..f3c8816c19d6 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll
@@ -24,6 +24,16 @@ private Element getRealParent(Expr expr) {
result.(Destructor).getADestruction() = expr
}
+IRUserVariable getIRUserVariable(Function func, Variable var) {
+ result.getVariable() = var and
+ result.getEnclosingFunction() = func
+}
+
+IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) {
+ result.getAST() = ast and
+ result.getTag() = tag
+}
+
/**
* Holds if `expr` is a constant of a type that can be replaced directly with
* its value in the IR. This does not include address constants as we have no
@@ -415,8 +425,11 @@ newtype TTranslatedElement =
} or
TTranslatedEllipsisParameter(Function func) { translateFunction(func) and func.isVarargs() } or
TTranslatedReadEffects(Function func) { translateFunction(func) } or
+ TTranslatedThisReadEffect(Function func) {
+ translateFunction(func) and func.isMember() and not func.isStatic()
+ } or
// The read side effects in a function's return block
- TTranslatedReadEffect(Parameter param) {
+ TTranslatedParameterReadEffect(Parameter param) {
translateFunction(param.getFunction()) and
exists(Type t | t = param.getUnspecifiedType() |
t instanceof ArrayType or
@@ -463,7 +476,7 @@ newtype TTranslatedElement =
)
} or
// The side effects of an allocation, i.e. `new`, `new[]` or `malloc`
- TTranslatedAllocationSideEffects(AllocationExpr expr) or
+ TTranslatedAllocationSideEffects(AllocationExpr expr) { not ignoreExpr(expr) } or
// A precise side effect of an argument to a `Call`
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
(
@@ -702,12 +715,8 @@ abstract class TranslatedElement extends TTranslatedElement {
int getInstructionElementSize(InstructionTag tag) { none() }
/**
- * If the instruction specified by `tag` has a result of type `UnknownType`,
- * gets the size of the result in bytes. If the result does not have a knonwn
- * constant size, this predicate does not hold.
+ * Holds if the generated IR refers to an opaque type with size `byteSize`.
*/
- int getInstructionResultSize(InstructionTag tag) { none() }
-
predicate needsUnknownOpaqueType(int byteSize) { none() }
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
index 75e70d1986f4..6da9193fd580 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
@@ -1011,7 +1011,7 @@ class TranslatedDynamicCast extends TranslatedSingleInstructionConversion {
if resultType instanceof PointerType
then
if resultType.(PointerType).getBaseType() instanceof VoidType
- then result instanceof Opcode::DynamicCastToVoid
+ then result instanceof Opcode::CompleteObjectAddress
else result instanceof Opcode::CheckedConvertOrNull
else result instanceof Opcode::CheckedConvertOrThrow
)
@@ -2905,7 +2905,7 @@ predicate exprNeedsCopyIfNotLoaded(Expr expr) {
private predicate exprImmediatelyDiscarded(Expr expr) {
exists(ExprStmt s |
s = expr.getParent() and
- not exists(StmtExpr se | s = se.getStmt().(Block).getLastStmt())
+ not exists(StmtExpr se | s = se.getStmt().(BlockStmt).getLastStmt())
)
or
exists(CommaExpr c | c.getLeftOperand() = expr)
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll
index 6d34830a0bd4..f55d661b202e 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll
@@ -676,14 +676,17 @@ class TranslatedReadEffects extends TranslatedElement, TTranslatedReadEffects {
override string toString() { result = "read effects: " + func.toString() }
override TranslatedElement getChild(int id) {
- result = getTranslatedReadEffect(func.getParameter(id))
+ result = getTranslatedThisReadEffect(func) and
+ id = -1
+ or
+ result = getTranslatedParameterReadEffect(func.getParameter(id))
}
override Instruction getFirstInstruction() {
if exists(getAChild())
then
result =
- min(TranslatedReadEffect child, int id | child = getChild(id) | child order by id)
+ min(TranslatedElement child, int id | child = getChild(id) | child order by id)
.getFirstInstruction()
else result = getParent().getChildSuccessor(this)
}
@@ -709,17 +712,15 @@ class TranslatedReadEffects extends TranslatedElement, TTranslatedReadEffects {
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
}
-private TranslatedReadEffect getTranslatedReadEffect(Parameter param) { result.getAST() = param }
-
-class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect {
- Parameter param;
-
- TranslatedReadEffect() { this = TTranslatedReadEffect(param) }
-
- override Locatable getAST() { result = param }
+private TranslatedThisReadEffect getTranslatedThisReadEffect(Function func) {
+ result.getAST() = func
+}
- override string toString() { result = "read effect: " + param.toString() }
+private TranslatedParameterReadEffect getTranslatedParameterReadEffect(Parameter param) {
+ result.getAST() = param
+}
+abstract class TranslatedReadEffect extends TranslatedElement {
override TranslatedElement getChild(int id) { none() }
override Instruction getChildSuccessor(TranslatedElement child) { none() }
@@ -732,20 +733,12 @@ class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect {
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
- override Function getFunction() { result = param.getFunction() }
-
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
opcode instanceof Opcode::ReturnIndirection and
tag = OnlyInstructionTag() and
resultType = getVoidType()
}
- final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
- tag = OnlyInstructionTag() and
- operandTag = addressOperand() and
- result = getTranslatedParameter(param).getInstruction(InitializerIndirectAddressTag())
- }
-
final override CppType getInstructionMemoryOperandType(
InstructionTag tag, TypedOperandTag operandTag
) {
@@ -753,6 +746,47 @@ class TranslatedReadEffect extends TranslatedElement, TTranslatedReadEffect {
operandTag = sideEffectOperand() and
result = getUnknownType()
}
+}
+
+class TranslatedThisReadEffect extends TranslatedReadEffect, TTranslatedThisReadEffect {
+ Function func;
+
+ TranslatedThisReadEffect() { this = TTranslatedThisReadEffect(func) }
+
+ override Locatable getAST() { result = func }
+
+ override Function getFunction() { result = func }
+
+ override string toString() { result = "read effect: this" }
+
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ tag = OnlyInstructionTag() and
+ operandTag = addressOperand() and
+ result = getTranslatedThisParameter(func).getInstruction(InitializerIndirectAddressTag())
+ }
+
+ final override IRVariable getInstructionVariable(InstructionTag tag) {
+ tag = OnlyInstructionTag() and
+ result = getTranslatedFunction(func).getThisVariable()
+ }
+}
+
+class TranslatedParameterReadEffect extends TranslatedReadEffect, TTranslatedParameterReadEffect {
+ Parameter param;
+
+ TranslatedParameterReadEffect() { this = TTranslatedParameterReadEffect(param) }
+
+ override Locatable getAST() { result = param }
+
+ override string toString() { result = "read effect: " + param.toString() }
+
+ override Function getFunction() { result = param.getFunction() }
+
+ final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
+ tag = OnlyInstructionTag() and
+ operandTag = addressOperand() and
+ result = getTranslatedParameter(param).getInstruction(InitializerIndirectAddressTag())
+ }
final override IRVariable getInstructionVariable(InstructionTag tag) {
tag = OnlyInstructionTag() and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
index 8e2947d709f0..4b6538654db8 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
@@ -415,17 +415,6 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
)
}
- override int getInstructionResultSize(InstructionTag tag) {
- exists(int elementCount |
- zeroInitRange(_, elementCount) and
- (
- tag = ZeroPadStringConstantTag() or
- tag = ZeroPadStringStoreTag()
- ) and
- result = elementCount * getElementType().getSize()
- )
- }
-
private Type getElementType() {
result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType()
}
@@ -772,15 +761,6 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
result = getZeroValue(getElementType())
}
- override int getInstructionResultSize(InstructionTag tag) {
- elementCount > 1 and
- (
- tag = getElementDefaultValueTag() or
- tag = getElementDefaultValueStoreTag()
- ) and
- result = elementCount * getElementType().getSize()
- }
-
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
result = TranslatedElementInitialization.super.getInstructionRegisterOperand(tag, operandTag)
or
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll
index 3339046d3915..ce08fc9367fc 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll
@@ -290,7 +290,7 @@ class TranslatedTryStmt extends TranslatedStmt {
}
class TranslatedBlock extends TranslatedStmt {
- override Block stmt;
+ override BlockStmt stmt;
override TranslatedElement getChild(int id) { result = getStmt(id) }
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 badd48552a5f..c96783fe6e81 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll
@@ -1,3 +1,47 @@
+/**
+ * Provides classes that describe the Intermediate Representation (IR) of the program.
+ *
+ * The IR is a representation of the semantics of the program, with very little dependence on the
+ * syntax that was used to write the program. For example, in C++, the statements `i += 1;`, `i++`,
+ * and `++i` all have the same semantic effect, but appear in the AST as three different types of
+ * `Expr` node. In the IR, all three statements are broken down into a sequence of fundamental
+ * operations similar to:
+ *
+ * ```
+ * r1(int*) = VariableAddress[i] // Compute the address of variable `i`
+ * r2(int) = Load &:r1, m0 // Load the value of `i`
+ * r3(int) = Constant[1] // An integer constant with the value `1`
+ * r4(int) = Add r2, r3 // Add `1` to the value of `i`
+ * r5(int) = Store &r1, r4 // Store the new value back into the variable `i`
+ * ```
+ *
+ * This allows IR-based analysis to focus on the fundamental operations, rather than having to be
+ * concerned with the various ways of expressing those operations in source code.
+ *
+ * The key classes in the IR are:
+ *
+ * - `IRFunction` - Contains the IR for an entire function definition, including all of that
+ * function's `Instruction`s, `IRBlock`s, and `IRVariables`.
+ * - `Instruction` - A single operation in the IR. An instruction specifies the operation to be
+ * performed, the operands that produce the inputs to that operation, and the type of the result
+ * of the operation. Control flows from an `Instruction` to one of a set of successor
+ * `Instruction`s.
+ * - `Operand` - An input value of an `Instruction`. All inputs of an `Instruction` are explicitly
+ * represented as `Operand`s, even if the input was implicit in the source code. An `Operand` has
+ * a link to the `Instruction` that consumes its value (its "use") and a link to the `Instruction`
+ * that produces its value (its "definition").
+ * - `IRVariable` - A variable accessed by the IR for a particular function. An `IRVariable` is
+ * created for each variable directly accessed by the function. In addition, `IRVariable`s are
+ * created to represent certain temporary storage locations that do not have explicitly declared
+ * variables in the source code, such as the return value of the function.
+ * - `IRBlock` - A "basic block" in the control flow graph of a function. An `IRBlock` contains a
+ * sequence of instructions such that control flow can only enter the block at the first
+ * instruction, and can only leave the block from the last instruction.
+ * - `IRType` - The type of a value accessed in the IR. Unlike the `Type` class in the AST, `IRType`
+ * is language-neutral. For example, in C++, `unsigned int`, `char32_t`, and `wchar_t` might all
+ * be represented as the `IRType` `uint4`, a four-byte unsigned integer.
+ */
+
import IRFunction
import Instruction
import IRBlock
@@ -11,11 +55,12 @@ import Imports::MemoryAccessKind
private newtype TIRPropertyProvider = MkIRPropertyProvider()
/**
- * Class that provides additional properties to be dumped for IR instructions and blocks when using
+ * A class that provides additional properties to be dumped for IR instructions and blocks when using
* the PrintIR module. Libraries that compute additional facts about IR elements can extend the
* single instance of this class to specify the additional properties computed by the library.
*/
class IRPropertyProvider extends TIRPropertyProvider {
+ /** Gets a textual representation of this element. */
string toString() { result = "IRPropertyProvider" }
/**
@@ -27,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/IRBlock.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll
index 94ef73b27692..d827ed3cf82d 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes describing basic blocks in the IR of a function.
+ */
+
private import internal.IRInternal
import Instruction
private import internal.IRBlockImports as Imports
@@ -16,15 +20,27 @@ private import Cached
* Most consumers should use the class `IRBlock`.
*/
class IRBlockBase extends TIRBlock {
+ /** Gets a textual representation of this block. */
final string toString() { result = getFirstInstruction(this).toString() }
+ /** Gets the source location of the first non-`Phi` instruction in this block. */
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
+ /**
+ * INTERNAL: Do not use.
+ *
+ * Gets a string that uniquely identifies this block within its enclosing function.
+ *
+ * This predicate is used by debugging and printing code only.
+ */
final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() }
/**
- * Gets the zero-based index of the block within its function. This is used
- * by debugging and printing code only.
+ * INTERNAL: Do not use.
+ *
+ * Gets the zero-based index of the block within its function.
+ *
+ * This predicate is used by debugging and printing code only.
*/
int getDisplayIndex() {
exists(IRConfiguration::IRConfiguration config |
@@ -42,27 +58,51 @@ class IRBlockBase extends TIRBlock {
)
}
+ /**
+ * Gets the `index`th non-`Phi` instruction in this block.
+ */
final Instruction getInstruction(int index) { result = getInstruction(this, index) }
+ /**
+ * Get the `Phi` instructions that appear at the start of this block.
+ */
final PhiInstruction getAPhiInstruction() {
Construction::getPhiInstructionBlockStart(result) = getFirstInstruction()
}
+ /**
+ * Gets an instruction in this block. This includes `Phi` instructions.
+ */
final Instruction getAnInstruction() {
result = getInstruction(_) or
result = getAPhiInstruction()
}
+ /**
+ * Gets the first non-`Phi` instruction in this block.
+ */
final Instruction getFirstInstruction() { result = getFirstInstruction(this) }
+ /**
+ * Gets the last instruction in this block.
+ */
final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) }
+ /**
+ * Gets the number of non-`Phi` instructions in this block.
+ */
final int getInstructionCount() { result = getInstructionCount(this) }
+ /**
+ * Gets the `IRFunction` that contains this block.
+ */
final IRFunction getEnclosingIRFunction() {
result = getFirstInstruction(this).getEnclosingIRFunction()
}
+ /**
+ * Gets the `Function` that contains this block.
+ */
final Language::Function getEnclosingFunction() {
result = getFirstInstruction(this).getEnclosingFunction()
}
@@ -74,20 +114,57 @@ class IRBlockBase extends TIRBlock {
* instruction of another block.
*/
class IRBlock extends IRBlockBase {
+ /**
+ * Gets a block to which control flows directly from this block.
+ */
final IRBlock getASuccessor() { blockSuccessor(this, result) }
+ /**
+ * Gets a block from which control flows directly to this block.
+ */
final IRBlock getAPredecessor() { blockSuccessor(result, this) }
+ /**
+ * Gets the block to which control flows directly from this block along an edge of kind `kind`.
+ */
final IRBlock getSuccessor(EdgeKind kind) { blockSuccessor(this, result, kind) }
+ /**
+ * Gets the block to which control flows directly from this block along a back edge of kind
+ * `kind`.
+ */
final IRBlock getBackEdgeSuccessor(EdgeKind kind) { backEdgeSuccessor(this, result, kind) }
+ /**
+ * Holds if this block immediately dominates `block`.
+ *
+ * Block `A` immediate dominates block `B` if block `A` strictly dominates block `B` and block `B`
+ * is a direct successor of block `A`.
+ */
final predicate immediatelyDominates(IRBlock block) { blockImmediatelyDominates(this, block) }
+ /**
+ * Holds if this block strictly dominates `block`.
+ *
+ * Block `A` strictly dominates block `B` if block `A` dominates block `B` and blocks `A` and `B`
+ * are not the same block.
+ */
final predicate strictlyDominates(IRBlock block) { blockImmediatelyDominates+(this, block) }
+ /**
+ * Holds if this block dominates `block`.
+ *
+ * Block `A` dominates block `B` if any control flow path from the entry block of the function to
+ * block `B` must pass through block `A`. A block always dominates itself.
+ */
final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block }
+ /**
+ * Gets a block on the dominance frontier of this block.
+ *
+ * The dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
+ * dominate block `B`, but block `A` does dominate an immediate predecessor of block `B`.
+ */
pragma[noinline]
final IRBlock dominanceFrontier() {
dominates(result.getAPredecessor()) and
@@ -95,7 +172,7 @@ class IRBlock extends IRBlockBase {
}
/**
- * Holds if this block is reachable from the entry point of its function
+ * Holds if this block is reachable from the entry block of its function.
*/
final predicate isReachableFromFunctionEntry() {
this = getEnclosingIRFunction().getEntryBlock() or
@@ -210,4 +287,4 @@ private module Cached {
idominance(isEntryBlock/1, blockSuccessor/2)(_, dominator, block)
}
-Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
+private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll
index 65af34942b6b..6a87b9b4b5fd 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll
@@ -8,10 +8,79 @@ module InstructionConsistency {
private import Imports::Overlap
private import internal.IRInternal
+ private newtype TOptionalIRFunction =
+ TPresentIRFunction(IRFunction irFunc) or
+ TMissingIRFunction()
+
+ /**
+ * An `IRFunction` that might not exist. This is used so that we can produce consistency failures
+ * for IR that also incorrectly lacks a `getEnclosingIRFunction()`.
+ */
+ abstract private class OptionalIRFunction extends TOptionalIRFunction {
+ abstract string toString();
+
+ abstract Language::Location getLocation();
+ }
+
+ private class PresentIRFunction extends OptionalIRFunction, TPresentIRFunction {
+ private IRFunction irFunc;
+
+ PresentIRFunction() { this = TPresentIRFunction(irFunc) }
+
+ override string toString() {
+ result = concat(Language::getIdentityString(irFunc.getFunction()), "; ")
+ }
+
+ override Language::Location getLocation() {
+ // To avoid an overwhelming number of results when the extractor merges functions with the
+ // same name, just pick a single location.
+ result =
+ rank[1](Language::Location loc | loc = irFunc.getLocation() | loc order by loc.toString())
+ }
+ }
+
+ private class MissingIRFunction extends OptionalIRFunction, TMissingIRFunction {
+ override string toString() { result = "" }
+
+ override Language::Location getLocation() { result instanceof Language::UnknownDefaultLocation }
+ }
+
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr) {
+ result = TPresentIRFunction(instr.getEnclosingIRFunction())
+ or
+ not exists(instr.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getInstructionIRFunction(Instruction instr, string irFuncText) {
+ result = getInstructionIRFunction(instr) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getOperandIRFunction(Operand operand) {
+ result = TPresentIRFunction(operand.getEnclosingIRFunction())
+ or
+ not exists(operand.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
+ pragma[inline]
+ private OptionalIRFunction getOperandIRFunction(Operand operand, string irFuncText) {
+ result = getOperandIRFunction(operand) and
+ irFuncText = result.toString()
+ }
+
+ private OptionalIRFunction getBlockIRFunction(IRBlock block) {
+ result = TPresentIRFunction(block.getEnclosingIRFunction())
+ or
+ not exists(block.getEnclosingIRFunction()) and result = TMissingIRFunction()
+ }
+
/**
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
*/
- query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
+ query predicate missingOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
exists(OperandTag tag |
instr.getOpcode().hasOperand(tag) and
not exists(NonPhiOperand operand |
@@ -21,32 +90,39 @@ module InstructionConsistency {
message =
"Instruction '" + instr.getOpcode().toString() +
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
/**
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
*/
- query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
- exists(NonPhiOperand operand |
- operand = instr.getAnOperand() and
- operand.getOperandTag() = tag
- ) and
- not instr.getOpcode().hasOperand(tag) and
- not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
- not (
- instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
- ) and
- not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
+ query predicate unexpectedOperand(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(OperandTag tag |
+ exists(NonPhiOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getOperandTag() = tag
+ ) and
+ not instr.getOpcode().hasOperand(tag) and
+ not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
+ not (
+ instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
+ ) and
+ not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag) and
+ message =
+ "Instruction '" + instr.toString() + "' has unexpected operand '" + tag.toString() +
+ "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
/**
* Holds if instruction `instr` has multiple operands with tag `tag`.
*/
query predicate duplicateOperand(
- Instruction instr, string message, IRFunction func, string funcText
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(OperandTag tag, int operandCount |
operandCount =
@@ -58,8 +134,7 @@ module InstructionConsistency {
message =
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
" in function '$@'." and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
@@ -67,100 +142,136 @@ module InstructionConsistency {
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
* the predecessor block `pred`.
*/
- query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
- pred = instr.getBlock().getAPredecessor() and
- not exists(PhiInputOperand operand |
- operand = instr.getAnOperand() and
- operand.getPredecessorBlock() = pred
+ query predicate missingPhiOperand(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(IRBlock pred |
+ pred = instr.getBlock().getAPredecessor() and
+ not exists(PhiInputOperand operand |
+ operand = instr.getAnOperand() and
+ operand.getPredecessorBlock() = pred
+ ) and
+ message =
+ "Instruction '" + instr.toString() + "' is missing an operand for predecessor block '" +
+ pred.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
- query predicate missingOperandType(Operand operand, string message) {
- exists(Language::Function func, Instruction use |
+ query predicate missingOperandType(
+ Operand operand, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Instruction use |
not exists(operand.getType()) and
use = operand.getUse() and
- func = use.getEnclosingFunction() and
message =
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
- "' missing type in function '" + Language::getIdentityString(func) + "'."
+ "' is missing a type in function '$@'." and
+ irFunc = getOperandIRFunction(operand, irFuncText)
)
}
query predicate duplicateChiOperand(
- ChiInstruction chi, string message, IRFunction func, string funcText
+ ChiInstruction chi, string message, OptionalIRFunction irFunc, string irFuncText
) {
chi.getTotal() = chi.getPartial() and
message =
"Chi instruction for " + chi.getPartial().toString() +
- " has duplicate operands in function $@" and
- func = chi.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ " has duplicate operands in function '$@'." and
+ irFunc = getInstructionIRFunction(chi, irFuncText)
}
query predicate sideEffectWithoutPrimary(
- SideEffectInstruction instr, string message, IRFunction func, string funcText
+ SideEffectInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
not exists(instr.getPrimaryInstruction()) and
- message = "Side effect instruction missing primary instruction in function $@" and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ message =
+ "Side effect instruction '" + instr + "' is missing a primary instruction in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
/**
* Holds if an instruction, other than `ExitFunction`, has no successors.
*/
- query predicate instructionWithoutSuccessor(Instruction instr) {
+ query predicate instructionWithoutSuccessor(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
not exists(instr.getASuccessor()) and
not instr instanceof ExitFunctionInstruction and
// Phi instructions aren't linked into the instruction-level flow graph.
not instr instanceof PhiInstruction and
- not instr instanceof UnreachedInstruction
+ not instr instanceof UnreachedInstruction and
+ message = "Instruction '" + instr.toString() + "' has no successors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
/**
- * Holds if there are multiple (`n`) edges of kind `kind` from `source`,
- * where `target` is among the targets of those edges.
+ * Holds if there are multiple edges of the same kind from `source`.
*/
- query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
- n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
- n > 1 and
- source.getSuccessor(kind) = target
+ query predicate ambiguousSuccessors(
+ Instruction source, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(EdgeKind kind, int n |
+ n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
+ n > 1 and
+ message =
+ "Instruction '" + source.toString() + "' has " + n.toString() + " successors of kind '" +
+ kind.toString() + "' in function '$@'." and
+ irFunc = getInstructionIRFunction(source, irFuncText)
+ )
}
/**
- * Holds if `instr` in `f` is part of a loop even though the AST of `f`
+ * Holds if `instr` is part of a loop even though the AST of `instr`'s enclosing function
* contains no element that can cause loops.
*/
- query predicate unexplainedLoop(Language::Function f, Instruction instr) {
- exists(IRBlock block |
- instr.getBlock() = block and
- block.getEnclosingFunction() = f and
- block.getASuccessor+() = block
- ) and
- not Language::hasPotentialLoop(f)
+ query predicate unexplainedLoop(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(Language::Function f |
+ exists(IRBlock block |
+ instr.getBlock() = block and
+ block.getEnclosingFunction() = f and
+ block.getASuccessor+() = block
+ ) and
+ not Language::hasPotentialLoop(f) and
+ message =
+ "Instruction '" + instr.toString() + "' is part of an unexplained loop in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
/**
* Holds if a `Phi` instruction is present in a block with fewer than two
* predecessors.
*/
- query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
- count(instr.getBlock().getAPredecessor()) < 2
+ query predicate unnecessaryPhiInstruction(
+ PhiInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int n |
+ n = count(instr.getBlock().getAPredecessor()) and
+ n < 2 and
+ message =
+ "Instruction '" + instr.toString() + "' is in a block with only " + n.toString() +
+ " predecessors in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
/**
* Holds if a memory operand is connected to a definition with an unmodeled result.
*/
query predicate memoryOperandDefinitionIsUnmodeled(
- Instruction instr, string message, IRFunction func, string funcText
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(MemoryOperand operand, Instruction def |
operand = instr.getAnOperand() and
def = operand.getAnyDef() and
not def.isResultModeled() and
- message = "Memory operand definition has unmodeled result in function '$@'" and
- func = instr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ message =
+ "Memory operand definition on instruction '" + instr.toString() +
+ "' has unmodeled result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
@@ -168,18 +279,37 @@ module InstructionConsistency {
* Holds if operand `operand` consumes a value that was defined in
* a different function.
*/
- query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
- operand.getUse() = instr and
- operand.getAnyDef() = defInstr and
- instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
+ query predicate operandAcrossFunctions(
+ Operand operand, string message, OptionalIRFunction useIRFunc, string useIRFuncText,
+ OptionalIRFunction defIRFunc, string defIRFuncText
+ ) {
+ exists(Instruction useInstr, Instruction defInstr |
+ operand.getUse() = useInstr and
+ operand.getAnyDef() = defInstr and
+ useIRFunc = getInstructionIRFunction(useInstr, useIRFuncText) and
+ defIRFunc = getInstructionIRFunction(defInstr, defIRFuncText) and
+ useIRFunc != defIRFunc and
+ message =
+ "Operand '" + operand.toString() + "' is used on instruction '" + useInstr.toString() +
+ "' in function '$@', but is defined on instruction '" + defInstr.toString() +
+ "' in function '$@'."
+ )
}
/**
* Holds if instruction `instr` is not in exactly one block.
*/
- query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
- blockCount = count(instr.getBlock()) and
- blockCount != 1
+ query predicate instructionWithoutUniqueBlock(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int blockCount |
+ blockCount = count(instr.getBlock()) and
+ blockCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' is a member of " + blockCount.toString() +
+ " blocks in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ )
}
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
@@ -192,10 +322,11 @@ module InstructionConsistency {
*
* This check ensures we don't have too _few_ back edges.
*/
- query predicate containsLoopOfForwardEdges(IRFunction f) {
+ query predicate containsLoopOfForwardEdges(IRFunction f, string message) {
exists(IRBlock block |
forwardEdge+(block, block) and
- block.getEnclosingIRFunction() = f
+ block.getEnclosingIRFunction() = f and
+ message = "Function contains a loop consisting of only forward edges."
)
}
@@ -207,12 +338,19 @@ module InstructionConsistency {
*
* This check ensures we don't have too _many_ back edges.
*/
- query predicate lostReachability(IRBlock block) {
+ query predicate lostReachability(
+ IRBlock block, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
exists(IRFunction f, IRBlock entry |
entry = f.getEntryBlock() and
entry.getASuccessor+() = block and
not forwardEdge+(entry, block) and
- not Language::hasGoto(f.getFunction())
+ not Language::hasGoto(f.getFunction()) and
+ message =
+ "Block '" + block.toString() +
+ "' is not reachable by traversing only forward edges in function '$@'." and
+ irFunc = TPresentIRFunction(f) and
+ irFuncText = irFunc.toString()
)
}
@@ -220,16 +358,22 @@ module InstructionConsistency {
* Holds if the number of back edges differs between the `Instruction` graph
* and the `IRBlock` graph.
*/
- query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
- fromInstr =
- count(Instruction i1, Instruction i2 |
- i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
- ) and
- fromBlock =
- count(IRBlock b1, IRBlock b2 |
- b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
- ) and
- fromInstr != fromBlock
+ query predicate backEdgeCountMismatch(OptionalIRFunction irFunc, string message) {
+ exists(int fromInstr, int fromBlock |
+ fromInstr =
+ count(Instruction i1, Instruction i2 |
+ getInstructionIRFunction(i1) = irFunc and i1.getBackEdgeSuccessor(_) = i2
+ ) and
+ fromBlock =
+ count(IRBlock b1, IRBlock b2 |
+ getBlockIRFunction(b1) = irFunc and b1.getBackEdgeSuccessor(_) = b2
+ ) and
+ fromInstr != fromBlock and
+ message =
+ "The instruction graph for function '" + irFunc.toString() + "' contains " +
+ fromInstr.toString() + " back edges, but the block graph contains " + fromBlock.toString()
+ + " back edges."
+ )
}
/**
@@ -251,7 +395,7 @@ module InstructionConsistency {
* Holds if `useOperand` has a definition that does not dominate the use.
*/
query predicate useNotDominatedByDefinition(
- Operand useOperand, string message, IRFunction func, string funcText
+ Operand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
pointOfEvaluation(useOperand, useBlock, useIndex) and
@@ -272,19 +416,17 @@ module InstructionConsistency {
message =
"Operand '" + useOperand.toString() +
"' is not dominated by its definition in function '$@'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
)
}
query predicate switchInstructionWithoutDefaultEdge(
- SwitchInstruction switchInstr, string message, IRFunction func, string funcText
+ SwitchInstruction switchInstr, string message, OptionalIRFunction irFunc, string irFuncText
) {
not exists(switchInstr.getDefaultSuccessor()) and
message =
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
- func = switchInstr.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getInstructionIRFunction(switchInstr, irFuncText)
}
/**
@@ -305,18 +447,30 @@ module InstructionConsistency {
instr.getOpcode() instanceof Opcode::InitializeNonLocal
}
- query predicate notMarkedAsConflated(Instruction instr) {
+ query predicate notMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
shouldBeConflated(instr) and
- not instr.isResultConflated()
+ not instr.isResultConflated() and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
- query predicate wronglyMarkedAsConflated(Instruction instr) {
+ query predicate wronglyMarkedAsConflated(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
instr.isResultConflated() and
- not shouldBeConflated(instr)
+ not shouldBeConflated(instr) and
+ message =
+ "Instruction '" + instr.toString() +
+ "' should not be marked as having a conflated result in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
}
query predicate invalidOverlap(
- MemoryOperand useOperand, string message, IRFunction func, string funcText
+ MemoryOperand useOperand, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(Overlap overlap |
overlap = useOperand.getDefinitionOverlap() and
@@ -324,8 +478,20 @@ module InstructionConsistency {
message =
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
overlap.toString() + "'." and
- func = useOperand.getEnclosingIRFunction() and
- funcText = Language::getIdentityString(func.getFunction())
+ irFunc = getOperandIRFunction(useOperand, irFuncText)
+ )
+ }
+
+ query predicate nonUniqueEnclosingIRFunction(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(int irFuncCount |
+ irFuncCount = count(instr.getEnclosingIRFunction()) and
+ irFuncCount != 1 and
+ message =
+ "Instruction '" + instr.toString() + "' has " + irFuncCount.toString() +
+ " results for `getEnclosingIRFunction()` in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll
index 9aea3e00d666..5968e58f90bf 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll
@@ -1,29 +1,17 @@
+/**
+ * Provides the class `IRFunction`, which represents the Intermediate Representation for the
+ * definition of a function.
+ */
+
private import internal.IRInternal
+private import internal.IRFunctionImports as Imports
+import Imports::IRFunctionBase
import Instruction
-private newtype TIRFunction =
- MkIRFunction(Language::Function func) { Construction::functionHasIR(func) }
-
/**
- * Represents the IR for a function.
+ * The IR for a function.
*/
-class IRFunction extends TIRFunction {
- Language::Function func;
-
- IRFunction() { this = MkIRFunction(func) }
-
- final string toString() { result = "IR: " + func.toString() }
-
- /**
- * Gets the function whose IR is represented.
- */
- final Language::Function getFunction() { result = func }
-
- /**
- * Gets the location of the function.
- */
- final Language::Location getLocation() { result = func.getLocation() }
-
+class IRFunction extends IRFunctionBase {
/**
* Gets the entry point for this function.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll
index 9f2a0d4ea281..146fc2707383 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes that represent variables accessed by the IR.
+ */
+
private import internal.IRInternal
import IRFunction
private import internal.IRVariableImports as Imports
@@ -7,15 +11,11 @@ private import Imports::TTempVariableTag
private import Imports::TIRVariable
private import Imports::IRType
-IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
- result.getVariable() = var and
- result.getEnclosingFunction() = func
-}
-
/**
- * A variable referenced by the IR for a function. The variable may be a user-declared variable
- * (`IRUserVariable`) or a temporary variable generated by the AST-to-IR translation
- * (`IRTempVariable`).
+ * A variable referenced by the IR for a function.
+ *
+ * The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
+ * by the AST-to-IR translation (`IRTempVariable`).
*/
class IRVariable extends TIRVariable {
Language::Function func;
@@ -27,6 +27,7 @@ class IRVariable extends TIRVariable {
this = TIRDynamicInitializationFlag(func, _, _)
}
+ /** Gets a textual representation of this element. */
string toString() { none() }
/**
@@ -162,20 +163,30 @@ class IRGeneratedVariable extends IRVariable {
override string getUniqueId() { none() }
+ /**
+ * INTERNAL: Do not use.
+ *
+ * Gets a string containing the source code location of the AST that generated this variable.
+ *
+ * This is used by debugging and printing code only.
+ */
final string getLocationString() {
result =
ast.getLocation().getStartLine().toString() + ":" +
ast.getLocation().getStartColumn().toString()
}
+ /**
+ * INTERNAL: Do not use.
+ *
+ * Gets the string that is combined with the location of the variable to generate the string
+ * representation of this variable.
+ *
+ * This is used by debugging and printing code only.
+ */
string getBaseString() { none() }
}
-IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
- result.getAST() = ast and
- result.getTag() = tag
-}
-
/**
* A temporary variable introduced by IR construction. The most common examples are the variable
* generated to hold the return value of a function, or the variable generated to hold the result of
@@ -190,6 +201,10 @@ class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVa
result = "Temp: " + Construction::getTempVariableUniqueId(this)
}
+ /**
+ * Gets the "tag" object that differentiates this temporary variable from other temporary
+ * variables generated for the same AST.
+ */
final TempVariableTag getTag() { result = tag }
override string getBaseString() { result = "#temp" }
@@ -217,19 +232,23 @@ class IRThrowVariable extends IRTempVariable {
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
* function that accepts a variable number of arguments.
*/
-class IREllipsisVariable extends IRTempVariable {
+class IREllipsisVariable extends IRTempVariable, IRParameter {
IREllipsisVariable() { tag = EllipsisTempVar() }
final override string toString() { result = "#ellipsis" }
+
+ final override int getIndex() { result = func.getNumberOfParameters() }
}
/**
* A temporary variable generated to hold the `this` pointer.
*/
-class IRThisVariable extends IRTempVariable {
+class IRThisVariable extends IRTempVariable, IRParameter {
IRThisVariable() { tag = ThisTempVar() }
final override string toString() { result = "#this" }
+
+ final override int getIndex() { result = -1 }
}
/**
@@ -249,6 +268,9 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
final override string getBaseString() { result = "#string" }
+ /**
+ * Gets the AST of the string literal represented by this `IRStringLiteral`.
+ */
final Language::StringLiteral getLiteral() { result = literal }
}
@@ -266,6 +288,9 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
final override string toString() { result = var.toString() + "#init" }
+ /**
+ * Gets variable whose initialization is guarded by this flag.
+ */
final Language::Variable getVariable() { result = var }
final override string getUniqueId() {
@@ -274,3 +299,29 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
}
+
+/**
+ * An IR variable which acts like a function parameter, including positional parameters and the
+ * temporary variables generated for `this` and ellipsis parameters.
+ */
+class IRParameter extends IRAutomaticVariable {
+ IRParameter() {
+ this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
+ or
+ this = TIRTempVariable(_, _, ThisTempVar(), _)
+ or
+ this = TIRTempVariable(_, _, EllipsisTempVar(), _)
+ }
+
+ /**
+ * Gets the zero-based index of this parameter. The `this` parameter has index -1.
+ */
+ int getIndex() { none() }
+}
+
+/**
+ * An IR variable representing a positional parameter.
+ */
+class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
+ final override int getIndex() { result = getVariable().(Language::Parameter).getIndex() }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
index 9c83a3d99f0c..620b23b942e0 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes that represent the individual instructions in the IR for a function.
+ */
+
private import internal.IRInternal
import IRFunction
import IRBlock
@@ -27,9 +31,16 @@ private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File fil
}
/**
- * Represents a single operation in the IR.
+ * A single instruction in the IR.
*/
-class Instruction extends Construction::TInstruction {
+class Instruction extends Construction::TStageInstruction {
+ Instruction() {
+ // The base `TStageInstruction` type is a superset of the actual instructions appearing in this
+ // stage. This call lets the stage filter out the ones that are not reused from raw IR.
+ Construction::hasInstruction(this)
+ }
+
+ /** Gets a textual representation of this element. */
final string toString() { result = getOpcode().toString() + ": " + getAST().toString() }
/**
@@ -194,16 +205,25 @@ class Instruction extends Construction::TInstruction {
* conversion.
*/
final Language::Expr getConvertedResultExpression() {
- result = Construction::getInstructionConvertedResultExpression(this)
+ result = Raw::getInstructionConvertedResultExpression(this)
}
/**
* Gets the unconverted form of the `Expr` whose result is computed by this instruction, if any.
*/
final Language::Expr getUnconvertedResultExpression() {
- result = Construction::getInstructionUnconvertedResultExpression(this)
+ result = Raw::getInstructionUnconvertedResultExpression(this)
}
+ /**
+ * Gets the language-specific type of the result produced by this instruction.
+ *
+ * Most consumers of the IR should use `getResultIRType()` instead. `getResultIRType()` uses a
+ * less complex, language-neutral type system in which all semantically equivalent types share the
+ * same `IRType` instance. For example, in C++, four different `Instruction`s might have three
+ * different values for `getResultLanguageType()`: `unsigned int`, `char32_t`, and `wchar_t`,
+ * whereas all four instructions would have the same value for `getResultIRType()`, `uint4`.
+ */
final Language::LanguageType getResultLanguageType() {
result = Construction::getInstructionResultType(this)
}
@@ -212,6 +232,7 @@ class Instruction extends Construction::TInstruction {
* Gets the type of the result produced by this instruction. If the instruction does not produce
* a result, its result type will be `IRVoidType`.
*/
+ cached
final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
/**
@@ -240,17 +261,19 @@ class Instruction extends Construction::TInstruction {
* given by `getResultType()`.
*
* For example, the statement `y = x;` generates the following IR:
+ * ```
* r1_0(glval: int) = VariableAddress[x]
* r1_1(int) = Load r1_0, mu0_1
* r1_2(glval: int) = VariableAddress[y]
* mu1_3(int) = Store r1_2, r1_1
+ * ```
*
* The result of each `VariableAddress` instruction is a glvalue of type
* `int`, representing the address of the corresponding integer variable. The
* result of the `Load` instruction is a prvalue of type `int`, representing
* the integer value loaded from variable `x`.
*/
- final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
+ final predicate isGLValue() { getResultLanguageType().hasType(_, true) }
/**
* Gets the size of the result produced by this instruction, in bytes. If the
@@ -259,7 +282,7 @@ class Instruction extends Construction::TInstruction {
* If `this.isGLValue()` holds for this instruction, the value of
* `getResultSize()` will always be the size of a pointer.
*/
- final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
+ final int getResultSize() { result = getResultLanguageType().getByteSize() }
/**
* Gets the opcode that specifies the operation performed by this instruction.
@@ -392,13 +415,27 @@ class Instruction extends Construction::TInstruction {
final Instruction getAPredecessor() { result = getPredecessor(_) }
}
+/**
+ * An instruction that refers to a variable.
+ *
+ * This class is used for any instruction whose operation fundamentally depends on a specific
+ * variable. For example, it is used for `VariableAddress`, which returns the address of a specific
+ * variable, and `InitializeParameter`, which returns the value that was passed to the specified
+ * parameter by the caller. `VariableInstruction` is not used for `Load` or `Store` instructions
+ * that happen to load from or store to a particular variable; in those cases, the memory location
+ * being accessed is specified by the `AddressOperand` on the instruction, which may or may not be
+ * defined by the result of a `VariableAddress` instruction.
+ */
class VariableInstruction extends Instruction {
IRVariable var;
- VariableInstruction() { var = Construction::getInstructionVariable(this) }
+ VariableInstruction() { var = Raw::getInstructionVariable(this) }
override string getImmediateString() { result = var.toString() }
+ /**
+ * Gets the variable that this instruction references.
+ */
final IRVariable getIRVariable() { result = var }
/**
@@ -407,63 +444,156 @@ class VariableInstruction extends Instruction {
final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
}
+/**
+ * An instruction that refers to a field of a class, struct, or union.
+ *
+ * This class is used for any instruction whose operation fundamentally depends on a specific
+ * field. For example, it is used for `FieldAddress`, which computes the address of a specific
+ * field on an object. `FieldInstruction` is not used for `Load` or `Store` instructions that happen
+ * to load from or store to a particular field; in those cases, the memory location being accessed
+ * is specified by the `AddressOperand` on the instruction, which may or may not be defined by the
+ * result of a `FieldAddress` instruction.
+ */
class FieldInstruction extends Instruction {
Language::Field field;
- FieldInstruction() { field = Construction::getInstructionField(this) }
+ FieldInstruction() { field = Raw::getInstructionField(this) }
final override string getImmediateString() { result = field.toString() }
+ /**
+ * Gets the field that this instruction references.
+ */
final Language::Field getField() { result = field }
}
+/**
+ * An instruction that refers to a function.
+ *
+ * This class is used for any instruction whose operation fundamentally depends on a specific
+ * function. For example, it is used for `FunctionAddress`, which returns the address of a specific
+ * function. `FunctionInstruction` is not used for `Call` instructions that happen to call a
+ * particular function; in that case, the function being called is specified by the
+ * `CallTargetOperand` on the instruction, which may or may not be defined by the result of a
+ * `FunctionAddress` instruction.
+ */
class FunctionInstruction extends Instruction {
Language::Function funcSymbol;
- FunctionInstruction() { funcSymbol = Construction::getInstructionFunction(this) }
+ FunctionInstruction() { funcSymbol = Raw::getInstructionFunction(this) }
final override string getImmediateString() { result = funcSymbol.toString() }
+ /**
+ * Gets the function that this instruction references.
+ */
final Language::Function getFunctionSymbol() { result = funcSymbol }
}
+/**
+ * An instruction whose result is a compile-time constant value.
+ */
class ConstantValueInstruction extends Instruction {
string value;
- ConstantValueInstruction() { value = Construction::getInstructionConstantValue(this) }
+ ConstantValueInstruction() { value = Raw::getInstructionConstantValue(this) }
final override string getImmediateString() { result = value }
+ /**
+ * Gets the constant value of this instruction's result.
+ */
final string getValue() { result = value }
}
+/**
+ * An instruction that refers to an argument of a `Call` instruction.
+ *
+ * This instruction is used for side effects of a `Call` instruction that read or write memory
+ * pointed to by one of the arguments of the call.
+ */
class IndexedInstruction extends Instruction {
int index;
- IndexedInstruction() { index = Construction::getInstructionIndex(this) }
+ IndexedInstruction() { index = Raw::getInstructionIndex(this) }
final override string getImmediateString() { result = index.toString() }
+ /**
+ * Gets the zero-based index of the argument that this instruction references.
+ */
final int getIndex() { result = index }
}
+/**
+ * An instruction representing the entry point to a function.
+ *
+ * Each `IRFunction` has exactly one `EnterFunction` instruction. Execution of the function begins
+ * at this instruction. This instruction has no predecessors.
+ */
class EnterFunctionInstruction extends Instruction {
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
}
+/**
+ * An instruction that returns the address of a variable.
+ *
+ * This instruction returns the address of a local variable, parameter, static field,
+ * namespace-scope variable, or global variable. For the address of a non-static field of a class,
+ * struct, or union, see `FieldAddressInstruction`.
+ */
class VariableAddressInstruction extends VariableInstruction {
VariableAddressInstruction() { getOpcode() instanceof Opcode::VariableAddress }
}
+/**
+ * An instruction that returns the address of a function.
+ *
+ * This instruction returns the address of a function, including non-member functions, static member
+ * functions, and non-static member functions.
+ *
+ * The result has an `IRFunctionAddress` type.
+ */
+class FunctionAddressInstruction extends FunctionInstruction {
+ FunctionAddressInstruction() { getOpcode() instanceof Opcode::FunctionAddress }
+}
+
+/**
+ * An instruction that initializes a parameter of the enclosing function with the value of the
+ * corresponding argument passed by the caller.
+ *
+ * Each parameter of a function will have exactly one `InitializeParameter` instruction that
+ * initializes that parameter.
+ */
class InitializeParameterInstruction extends VariableInstruction {
InitializeParameterInstruction() { getOpcode() instanceof Opcode::InitializeParameter }
+ /**
+ * Gets the parameter initialized by this instruction.
+ */
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
}
+/**
+ * An instruction that initializes all memory that existed before this function was called.
+ *
+ * This instruction provides a definition for memory that, because it was actually allocated and
+ * initialized elsewhere, would not otherwise have a definition in this function.
+ */
+class InitializeNonLocalInstruction extends Instruction {
+ InitializeNonLocalInstruction() { getOpcode() instanceof Opcode::InitializeNonLocal }
+}
+
+/**
+ * An instruction that initializes the memory pointed to by a parameter of the enclosing function
+ * with the value of that memory on entry to the function.
+ */
class InitializeIndirectionInstruction extends VariableInstruction {
InitializeIndirectionInstruction() { getOpcode() instanceof Opcode::InitializeIndirection }
+ /**
+ * Gets the parameter initialized by this instruction.
+ */
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
}
@@ -474,14 +604,42 @@ class InitializeThisInstruction extends Instruction {
InitializeThisInstruction() { getOpcode() instanceof Opcode::InitializeThis }
}
+/**
+ * An instruction that computes the address of a non-static field of an object.
+ */
class FieldAddressInstruction extends FieldInstruction {
FieldAddressInstruction() { getOpcode() instanceof Opcode::FieldAddress }
+ /**
+ * Gets the operand that provides the address of the object containing the field.
+ */
final UnaryOperand getObjectAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the object containing the field.
+ */
final Instruction getObjectAddress() { result = getObjectAddressOperand().getDef() }
}
+/**
+ * An instruction that computes the address of the first element of a managed array.
+ *
+ * This instruction is used for element access to C# arrays.
+ */
+class ElementsAddressInstruction extends UnaryInstruction {
+ ElementsAddressInstruction() { getOpcode() instanceof Opcode::ElementsAddress }
+
+ /**
+ * Gets the operand that provides the address of the array object.
+ */
+ final UnaryOperand getArrayObjectAddressOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the address of the array object.
+ */
+ final Instruction getArrayObjectAddress() { result = getArrayObjectAddressOperand().getDef() }
+}
+
/**
* An instruction that produces a well-defined but unknown result and has
* unknown side effects, including side effects that are not conservatively
@@ -496,6 +654,12 @@ class ErrorInstruction extends Instruction {
ErrorInstruction() { getOpcode() instanceof Opcode::Error }
}
+/**
+ * An instruction that returns an uninitialized value.
+ *
+ * This instruction is used to provide an initial definition for a stack variable that does not have
+ * an initializer, or whose initializer only partially initializes the variable.
+ */
class UninitializedInstruction extends VariableInstruction {
UninitializedInstruction() { getOpcode() instanceof Opcode::Uninitialized }
@@ -505,35 +669,94 @@ class UninitializedInstruction extends VariableInstruction {
final Language::Variable getLocalVariable() { result = var.(IRUserVariable).getVariable() }
}
+/**
+ * An instruction that has no effect.
+ *
+ * This instruction is typically inserted to ensure that a particular AST is associated with at
+ * least one instruction, even when the AST has no semantic effect.
+ */
class NoOpInstruction extends Instruction {
NoOpInstruction() { getOpcode() instanceof Opcode::NoOp }
}
+/**
+ * An instruction that returns control to the caller of the function.
+ *
+ * This instruction represents the normal (non-exception) return from a function, either from an
+ * explicit `return` statement or from control flow reaching the end of the function's body.
+ *
+ * Each function has exactly one `ReturnInstruction`. Each `return` statement in a function is
+ * represented as an initialization of the temporary variable that holds the return value, with
+ * control then flowing to the common `ReturnInstruction` for that function. Exception: A function
+ * that never returns will not have a `ReturnInstruction`.
+ *
+ * The `ReturnInstruction` for a function will have a control-flow successor edge to a block
+ * containing the `ExitFunction` instruction for that function.
+ *
+ * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from
+ * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a
+ * `void`-returning function.
+ */
class ReturnInstruction extends Instruction {
ReturnInstruction() { getOpcode() instanceof ReturnOpcode }
}
+/**
+ * An instruction that returns control to the caller of the function, without returning a value.
+ */
class ReturnVoidInstruction extends ReturnInstruction {
ReturnVoidInstruction() { getOpcode() instanceof Opcode::ReturnVoid }
}
+/**
+ * An instruction that returns control to the caller of the function, including a return value.
+ */
class ReturnValueInstruction extends ReturnInstruction {
ReturnValueInstruction() { getOpcode() instanceof Opcode::ReturnValue }
+ /**
+ * Gets the operand that provides the value being returned by the function.
+ */
final LoadOperand getReturnValueOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the value being returned by the function, if an
+ * exact definition is available.
+ */
final Instruction getReturnValue() { result = getReturnValueOperand().getDef() }
}
+/**
+ * An instruction that represents the use of the value pointed to by a parameter of the function
+ * after the function returns control to its caller.
+ *
+ * This instruction does not itself return control to the caller. It merely represents the potential
+ * for a caller to use the memory pointed to by the parameter sometime after the call returns. This
+ * is the counterpart to the `InitializeIndirection` instruction, which represents the possibility
+ * that the caller initialized the memory pointed to by the parameter before the call.
+ */
class ReturnIndirectionInstruction extends VariableInstruction {
ReturnIndirectionInstruction() { getOpcode() instanceof Opcode::ReturnIndirection }
+ /**
+ * Gets the operand that provides the value of the pointed-to memory.
+ */
final SideEffectOperand getSideEffectOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the value of the pointed-to memory, if an exact
+ * definition is available.
+ */
final Instruction getSideEffect() { result = getSideEffectOperand().getDef() }
+ /**
+ * Gets the operand that provides the address of the pointed-to memory.
+ */
final AddressOperand getSourceAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the pointed-to memory.
+ */
final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() }
/**
@@ -541,87 +764,191 @@ class ReturnIndirectionInstruction extends VariableInstruction {
* function.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
+
+ /**
+ * Holds if this instruction is the return indirection for `this`.
+ */
+ final predicate isThisIndirection() { var instanceof IRThisVariable }
}
+/**
+ * An instruction that returns a copy of its operand.
+ *
+ * There are several different copy instructions, depending on the source and destination of the
+ * copy operation:
+ * - `CopyInstruction` - Copies a register operand to a register result.
+ * - `LoadInstruction` - Copies a memory operand to a register result.
+ * - `StoreInstruction` - Copies a register operand to a memory result.
+ */
class CopyInstruction extends Instruction {
CopyInstruction() { getOpcode() instanceof CopyOpcode }
+ /**
+ * Gets the operand that provides the input value of the copy.
+ */
Operand getSourceValueOperand() { none() }
+ /**
+ * Gets the instruction whose result provides the input value of the copy, if an exact definition
+ * is available.
+ */
final Instruction getSourceValue() { result = getSourceValueOperand().getDef() }
}
+/**
+ * An instruction that returns a register result containing a copy of its register operand.
+ */
class CopyValueInstruction extends CopyInstruction, UnaryInstruction {
CopyValueInstruction() { getOpcode() instanceof Opcode::CopyValue }
final override UnaryOperand getSourceValueOperand() { result = getAnOperand() }
}
+/**
+ * An instruction that returns a register result containing a copy of its memory operand.
+ */
class LoadInstruction extends CopyInstruction {
LoadInstruction() { getOpcode() instanceof Opcode::Load }
+ /**
+ * Gets the operand that provides the address of the value being loaded.
+ */
final AddressOperand getSourceAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the value being loaded.
+ */
final Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() }
final override LoadOperand getSourceValueOperand() { result = getAnOperand() }
}
+/**
+ * An instruction that returns a memory result containing a copy of its register operand.
+ */
class StoreInstruction extends CopyInstruction {
StoreInstruction() { getOpcode() instanceof Opcode::Store }
+ /**
+ * Gets the operand that provides the address of the location to which the value will be stored.
+ */
final AddressOperand getDestinationAddressOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the address of the location to which the value will
+ * be stored, if an exact definition is available.
+ */
final Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() }
final override StoreValueOperand getSourceValueOperand() { result = getAnOperand() }
}
+/**
+ * An instruction that branches to one of two successor instructions based on the value of a Boolean
+ * operand.
+ */
class ConditionalBranchInstruction extends Instruction {
ConditionalBranchInstruction() { getOpcode() instanceof Opcode::ConditionalBranch }
+ /**
+ * Gets the operand that provides the Boolean condition controlling the branch.
+ */
final ConditionOperand getConditionOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the Boolean condition controlling the branch.
+ */
final Instruction getCondition() { result = getConditionOperand().getDef() }
+ /**
+ * Gets the instruction to which control will flow if the condition is true.
+ */
final Instruction getTrueSuccessor() { result = getSuccessor(EdgeKind::trueEdge()) }
+ /**
+ * Gets the instruction to which control will flow if the condition is false.
+ */
final Instruction getFalseSuccessor() { result = getSuccessor(EdgeKind::falseEdge()) }
}
+/**
+ * An instruction representing the exit point of a function.
+ *
+ * Each `IRFunction` has exactly one `ExitFunction` instruction, unless the function neither returns
+ * nor throws an exception. Control flows to the `ExitFunction` instruction from both normal returns
+ * (`ReturnVoid`, `ReturnValue`) and propagated exceptions (`Unwind`). This instruction has no
+ * successors.
+ */
class ExitFunctionInstruction extends Instruction {
ExitFunctionInstruction() { getOpcode() instanceof Opcode::ExitFunction }
}
+/**
+ * An instruction whose result is a constant value.
+ */
class ConstantInstruction extends ConstantValueInstruction {
ConstantInstruction() { getOpcode() instanceof Opcode::Constant }
}
+/**
+ * An instruction whose result is a constant value of integer or Boolean type.
+ */
class IntegerConstantInstruction extends ConstantInstruction {
- IntegerConstantInstruction() { getResultType() instanceof Language::IntegralType }
+ IntegerConstantInstruction() {
+ exists(IRType resultType |
+ resultType = getResultIRType() and
+ (resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
+ )
+ }
}
+/**
+ * An instruction whose result is a constant value of floating-point type.
+ */
class FloatConstantInstruction extends ConstantInstruction {
- FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
+ FloatConstantInstruction() { getResultIRType() instanceof IRFloatingPointType }
}
+/**
+ * An instruction whose result is the address of a string literal.
+ */
class StringConstantInstruction extends VariableInstruction {
override IRStringLiteral var;
final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) }
+ /**
+ * Gets the string literal whose address is returned by this instruction.
+ */
final Language::StringLiteral getValue() { result = var.getLiteral() }
}
+/**
+ * An instruction whose result is computed from two operands.
+ */
class BinaryInstruction extends Instruction {
BinaryInstruction() { getOpcode() instanceof BinaryOpcode }
+ /**
+ * Gets the left operand of this binary instruction.
+ */
final LeftOperand getLeftOperand() { result = getAnOperand() }
+ /**
+ * Gets the right operand of this binary instruction.
+ */
final RightOperand getRightOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the value of the left operand of this binary
+ * instruction.
+ */
final Instruction getLeft() { result = getLeftOperand().getDef() }
+ /**
+ * Gets the instruction whose result provides the value of the right operand of this binary
+ * instruction.
+ */
final Instruction getRight() { result = getRightOperand().getDef() }
/**
@@ -634,121 +961,301 @@ class BinaryInstruction extends Instruction {
}
}
+/**
+ * An instruction that computes the result of an arithmetic operation.
+ */
class ArithmeticInstruction extends Instruction {
ArithmeticInstruction() { getOpcode() instanceof ArithmeticOpcode }
}
+/**
+ * An instruction that performs an arithmetic operation on two numeric operands.
+ */
class BinaryArithmeticInstruction extends ArithmeticInstruction, BinaryInstruction { }
+/**
+ * An instruction whose result is computed by performing an arithmetic operation on a single
+ * numeric operand.
+ */
class UnaryArithmeticInstruction extends ArithmeticInstruction, UnaryInstruction { }
+/**
+ * An instruction that computes the sum of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * integer overflow is the infinite-precision result modulo 2^n. Floating-point addition is
+ * performed according to IEEE-754.
+ */
class AddInstruction extends BinaryArithmeticInstruction {
AddInstruction() { getOpcode() instanceof Opcode::Add }
}
+/**
+ * An instruction that computes the difference of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * integer overflow is the infinite-precision result modulo 2^n. Floating-point subtraction is performed
+ * according to IEEE-754.
+ */
class SubInstruction extends BinaryArithmeticInstruction {
SubInstruction() { getOpcode() instanceof Opcode::Sub }
}
+/**
+ * An instruction that computes the product of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * integer overflow is the infinite-precision result modulo 2^n. Floating-point multiplication is
+ * performed according to IEEE-754.
+ */
class MulInstruction extends BinaryArithmeticInstruction {
MulInstruction() { getOpcode() instanceof Opcode::Mul }
}
+/**
+ * An instruction that computes the quotient of two numeric operands.
+ *
+ * Both operands must have the same numeric type, which will also be the result type. The result of
+ * division by zero or integer overflow is undefined. Floating-point division is performed according
+ * to IEEE-754.
+ */
class DivInstruction extends BinaryArithmeticInstruction {
DivInstruction() { getOpcode() instanceof Opcode::Div }
}
+/**
+ * An instruction that computes the remainder of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type. The result of
+ * division by zero or integer overflow is undefined.
+ */
class RemInstruction extends BinaryArithmeticInstruction {
RemInstruction() { getOpcode() instanceof Opcode::Rem }
}
+/**
+ * An instruction that negates a single numeric operand.
+ *
+ * The operand must have a numeric type, which will also be the result type. The result of integer
+ * negation uses two's complement, and is computed modulo 2^n. The result of floating-point negation
+ * is performed according to IEEE-754.
+ */
class NegateInstruction extends UnaryArithmeticInstruction {
NegateInstruction() { getOpcode() instanceof Opcode::Negate }
}
+/**
+ * An instruction that computes the result of a bitwise operation.
+ */
class BitwiseInstruction extends Instruction {
BitwiseInstruction() { getOpcode() instanceof BitwiseOpcode }
}
+/**
+ * An instruction that performs a bitwise operation on two integer operands.
+ */
class BinaryBitwiseInstruction extends BitwiseInstruction, BinaryInstruction { }
+/**
+ * An instruction that performs a bitwise operation on a single integer operand.
+ */
class UnaryBitwiseInstruction extends BitwiseInstruction, UnaryInstruction { }
+/**
+ * An instruction that computes the bitwise "and" of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type.
+ */
class BitAndInstruction extends BinaryBitwiseInstruction {
BitAndInstruction() { getOpcode() instanceof Opcode::BitAnd }
}
+/**
+ * An instruction that computes the bitwise "or" of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type.
+ */
class BitOrInstruction extends BinaryBitwiseInstruction {
BitOrInstruction() { getOpcode() instanceof Opcode::BitOr }
}
+/**
+ * An instruction that computes the bitwise "xor" of two integer operands.
+ *
+ * Both operands must have the same integer type, which will also be the result type.
+ */
class BitXorInstruction extends BinaryBitwiseInstruction {
BitXorInstruction() { getOpcode() instanceof Opcode::BitXor }
}
+/**
+ * An instruction that shifts its left operand to the left by the number of bits specified by its
+ * right operand.
+ *
+ * Both operands must have an integer type. The result has the same type as the left operand. The
+ * rightmost bits are zero-filled.
+ */
class ShiftLeftInstruction extends BinaryBitwiseInstruction {
ShiftLeftInstruction() { getOpcode() instanceof Opcode::ShiftLeft }
}
+/**
+ * An instruction that shifts its left operand to the right by the number of bits specified by its
+ * right operand.
+ *
+ * Both operands must have an integer type. The result has the same type as the left operand. If the
+ * left operand has an unsigned integer type, the leftmost bits are zero-filled. If the left operand
+ * has a signed integer type, the leftmost bits are filled by duplicating the most significant bit
+ * of the left operand.
+ */
class ShiftRightInstruction extends BinaryBitwiseInstruction {
ShiftRightInstruction() { getOpcode() instanceof Opcode::ShiftRight }
}
+/**
+ * An instruction that performs a binary arithmetic operation involving at least one pointer
+ * operand.
+ */
class PointerArithmeticInstruction extends BinaryInstruction {
int elementSize;
PointerArithmeticInstruction() {
getOpcode() instanceof PointerArithmeticOpcode and
- elementSize = Construction::getInstructionElementSize(this)
+ elementSize = Raw::getInstructionElementSize(this)
}
final override string getImmediateString() { result = elementSize.toString() }
+ /**
+ * Gets the size of the elements pointed to by the pointer operands, in bytes.
+ *
+ * When adding an integer offset to a pointer (`PointerAddInstruction`) or subtracting an integer
+ * offset from a pointer (`PointerSubInstruction`), the integer offset is multiplied by the
+ * element size to compute the actual number of bytes added to or subtracted from the pointer
+ * address. When computing the integer difference between two pointers (`PointerDiffInstruction`),
+ * the result is computed by computing the difference between the two pointer byte addresses, then
+ * dividing that byte count by the element size.
+ */
final int getElementSize() { result = elementSize }
}
+/**
+ * An instruction that adds or subtracts an integer offset from a pointer.
+ */
class PointerOffsetInstruction extends PointerArithmeticInstruction {
PointerOffsetInstruction() { getOpcode() instanceof PointerOffsetOpcode }
}
+/**
+ * An instruction that adds an integer offset to a pointer.
+ *
+ * The result is the byte address computed by adding the value of the right (integer) operand,
+ * multiplied by the element size, to the value of the left (pointer) operand. The result of pointer
+ * overflow is undefined.
+ */
class PointerAddInstruction extends PointerOffsetInstruction {
PointerAddInstruction() { getOpcode() instanceof Opcode::PointerAdd }
}
+/**
+ * An instruction that subtracts an integer offset from a pointer.
+ *
+ * The result is the byte address computed by subtracting the value of the right (integer) operand,
+ * multiplied by the element size, from the value of the left (pointer) operand. The result of
+ * pointer underflow is undefined.
+ */
class PointerSubInstruction extends PointerOffsetInstruction {
PointerSubInstruction() { getOpcode() instanceof Opcode::PointerSub }
}
+/**
+ * An instruction that computes the difference between two pointers.
+ *
+ * Both operands must have the same pointer type. The result must have an integer type whose size is
+ * the same as that of the pointer operands. The result is computed by subtracting the byte address
+ * in the right operand from the byte address in the left operand, and dividing by the element size.
+ * If the difference in byte addresses is not divisible by the element size, the result is
+ * undefined.
+ */
class PointerDiffInstruction extends PointerArithmeticInstruction {
PointerDiffInstruction() { getOpcode() instanceof Opcode::PointerDiff }
}
+/**
+ * An instruction whose result is computed from a single operand.
+ */
class UnaryInstruction extends Instruction {
UnaryInstruction() { getOpcode() instanceof UnaryOpcode }
+ /**
+ * Gets the sole operand of this instruction.
+ */
final UnaryOperand getUnaryOperand() { result = getAnOperand() }
+ /**
+ * Gets the instruction whose result provides the sole operand of this instruction.
+ */
final Instruction getUnary() { result = getUnaryOperand().getDef() }
}
+/**
+ * An instruction that converts the value of its operand to a value of a different type.
+ */
class ConvertInstruction extends UnaryInstruction {
ConvertInstruction() { getOpcode() instanceof Opcode::Convert }
}
+/**
+ * An instruction that converts the address of a polymorphic object to the address of a different
+ * subobject of the same polymorphic object, returning a null address if the dynamic type of the
+ * object is not compatible with the result type.
+ *
+ * If the operand holds a null address, the result is a null address.
+ *
+ * This instruction is used to represent a C++ `dynamic_cast<>` to a pointer type, or a C# `is` or
+ * `as` expression.
+ */
class CheckedConvertOrNullInstruction extends UnaryInstruction {
CheckedConvertOrNullInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrNull }
}
/**
- * Represents an instruction that converts between two addresses
- * related by inheritance.
+ * An instruction that converts the address of a polymorphic object to the address of a different
+ * subobject of the same polymorphic object, throwing an exception if the dynamic type of the object
+ * is not compatible with the result type.
+ *
+ * If the operand holds a null address, the result is a null address.
+ *
+ * This instruction is used to represent a C++ `dynamic_cast<>` to a reference type, or a C# cast
+ * expression.
+ */
+class CheckedConvertOrThrowInstruction extends UnaryInstruction {
+ CheckedConvertOrThrowInstruction() { getOpcode() instanceof Opcode::CheckedConvertOrThrow }
+}
+
+/**
+ * An instruction that returns the address of the complete object that contains the subobject
+ * pointed to by its operand.
+ *
+ * If the operand holds a null address, the result is a null address.
+ *
+ * This instruction is used to represent `dyanmic_cast` in C++, which returns the pointer to
+ * the most-derived object.
+ */
+class CompleteObjectAddressInstruction extends UnaryInstruction {
+ CompleteObjectAddressInstruction() { getOpcode() instanceof Opcode::CompleteObjectAddress }
+}
+
+/**
+ * An instruction that converts the address of an object to the address of a different subobject of
+ * the same object, without any type checking at runtime.
*/
class InheritanceConversionInstruction extends UnaryInstruction {
Language::Class baseClass;
Language::Class derivedClass;
InheritanceConversionInstruction() {
- Construction::getInstructionInheritance(this, baseClass, derivedClass)
+ Raw::getInstructionInheritance(this, baseClass, derivedClass)
}
final override string getImmediateString() {
@@ -778,59 +1285,91 @@ class InheritanceConversionInstruction extends UnaryInstruction {
}
/**
- * Represents an instruction that converts from the address of a derived class
- * to the address of a base class.
+ * An instruction that converts from the address of a derived class to the address of a base class.
*/
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
ConvertToBaseInstruction() { getOpcode() instanceof ConvertToBaseOpcode }
}
/**
- * Represents an instruction that converts from the address of a derived class
- * to the address of a direct non-virtual base class.
+ * An instruction that converts from the address of a derived class to the address of a direct
+ * non-virtual base class.
+ *
+ * If the operand holds a null address, the result is a null address.
*/
class ConvertToNonVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToNonVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToNonVirtualBase }
}
/**
- * Represents an instruction that converts from the address of a derived class
- * to the address of a virtual base class.
+ * An instruction that converts from the address of a derived class to the address of a virtual base
+ * class.
+ *
+ * If the operand holds a null address, the result is a null address.
*/
class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
}
/**
- * Represents an instruction that converts from the address of a base class
- * to the address of a direct non-virtual derived class.
+ * An instruction that converts from the address of a base class to the address of a direct
+ * non-virtual derived class.
+ *
+ * If the operand holds a null address, the result is a null address.
*/
class ConvertToDerivedInstruction extends InheritanceConversionInstruction {
ConvertToDerivedInstruction() { getOpcode() instanceof Opcode::ConvertToDerived }
}
+/**
+ * An instruction that computes the bitwise complement of its operand.
+ *
+ * The operand must have an integer type, which will also be the result type.
+ */
class BitComplementInstruction extends UnaryBitwiseInstruction {
BitComplementInstruction() { getOpcode() instanceof Opcode::BitComplement }
}
+/**
+ * An instruction that computes the logical complement of its operand.
+ *
+ * The operand must have a Boolean type, which will also be the result type.
+ */
class LogicalNotInstruction extends UnaryInstruction {
LogicalNotInstruction() { getOpcode() instanceof Opcode::LogicalNot }
}
+/**
+ * An instruction that compares two numeric operands.
+ */
class CompareInstruction extends BinaryInstruction {
CompareInstruction() { getOpcode() instanceof CompareOpcode }
}
+/**
+ * An instruction that returns a `true` result if its operands are equal.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if `left == right`, and `false` if `left != right` or the two operands are
+ * unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareEQInstruction extends CompareInstruction {
CompareEQInstruction() { getOpcode() instanceof Opcode::CompareEQ }
}
+/**
+ * An instruction that returns a `true` result if its operands are not equal.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if `left != right` or if the two operands are unordered, and `false` if
+ * `left == right`. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareNEInstruction extends CompareInstruction {
CompareNEInstruction() { getOpcode() instanceof Opcode::CompareNE }
}
/**
- * Represents an instruction that does a relative comparison of two values, such as `<` or `>=`.
+ * An instruction that does a relative comparison of two values, such as `<` or `>=`.
*/
class RelationalInstruction extends CompareInstruction {
RelationalInstruction() { getOpcode() instanceof RelationalOpcode }
@@ -857,6 +1396,13 @@ class RelationalInstruction extends CompareInstruction {
predicate isStrict() { none() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is less than its right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left < right`, and `false` if `left >= right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareLTInstruction extends RelationalInstruction {
CompareLTInstruction() { getOpcode() instanceof Opcode::CompareLT }
@@ -867,6 +1413,13 @@ class CompareLTInstruction extends RelationalInstruction {
override predicate isStrict() { any() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is greater than its right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left > right`, and `false` if `left <= right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareGTInstruction extends RelationalInstruction {
CompareGTInstruction() { getOpcode() instanceof Opcode::CompareGT }
@@ -877,6 +1430,14 @@ class CompareGTInstruction extends RelationalInstruction {
override predicate isStrict() { any() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is less than or equal to its
+ * right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left <= right`, and `false` if `left > right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareLEInstruction extends RelationalInstruction {
CompareLEInstruction() { getOpcode() instanceof Opcode::CompareLE }
@@ -887,6 +1448,14 @@ class CompareLEInstruction extends RelationalInstruction {
override predicate isStrict() { none() }
}
+/**
+ * An instruction that returns a `true` result if its left operand is greater than or equal to its
+ * right operand.
+ *
+ * Both operands must have the same numeric or address type. The result must have a Boolean type.
+ * The result is `true` if the `left >= right`, and `false` if `left < right` or if the two operands
+ * are unordered. Floating-point comparison is performed according to IEEE-754.
+ */
class CompareGEInstruction extends RelationalInstruction {
CompareGEInstruction() { getOpcode() instanceof Opcode::CompareGE }
@@ -897,15 +1466,32 @@ class CompareGEInstruction extends RelationalInstruction {
override predicate isStrict() { none() }
}
+/**
+ * An instruction that branches to one of multiple successor instructions based on the value of an
+ * integer operand.
+ *
+ * This instruction will have zero or more successors whose edge kind is `CaseEdge`, each
+ * representing the branch that will be taken if the controlling expression is within the range
+ * specified for that case edge. The range of a case edge must be disjoint from the range of each
+ * other case edge.
+ *
+ * The instruction may optionally have a successor edge whose edge kind is `DefaultEdge`,
+ * representing the branch that will be taken if the controlling expression is not within the range
+ * of any case edge.
+ */
class SwitchInstruction extends Instruction {
SwitchInstruction() { getOpcode() instanceof Opcode::Switch }
+ /** Gets the operand that provides the integer value controlling the switch. */
final ConditionOperand getExpressionOperand() { result = getAnOperand() }
+ /** Gets the instruction whose result provides the integer value controlling the switch. */
final Instruction getExpression() { result = getExpressionOperand().getDef() }
+ /** Gets the successor instructions along the case edges of the switch. */
final Instruction getACaseSuccessor() { exists(CaseEdge edge | result = getSuccessor(edge)) }
+ /** Gets the successor instruction along the default edge of the switch, if any. */
final Instruction getDefaultSuccessor() { result = getSuccessor(EdgeKind::defaultEdge()) }
}
@@ -936,7 +1522,7 @@ class CallInstruction extends Instruction {
* Gets the `Function` that the call targets, if this is statically known.
*/
final Language::Function getStaticCallTarget() {
- result = getCallTarget().(FunctionInstruction).getFunctionSymbol()
+ result = getCallTarget().(FunctionAddressInstruction).getFunctionSymbol()
}
/**
@@ -981,6 +1567,9 @@ class CallInstruction extends Instruction {
class SideEffectInstruction extends Instruction {
SideEffectInstruction() { getOpcode() instanceof SideEffectOpcode }
+ /**
+ * Gets the instruction whose execution causes this side effect.
+ */
final Instruction getPrimaryInstruction() {
result = Construction::getPrimaryInstructionForSideEffect(this)
}
@@ -996,9 +1585,10 @@ class CallSideEffectInstruction extends SideEffectInstruction {
/**
* An instruction representing the side effect of a function call on any memory
- * that might be read by that call. This instruction is emitted instead of
- * `CallSideEffectInstruction` when it's certain that the call target cannot
- * write to escaped memory.
+ * that might be read by that call.
+ *
+ * This instruction is emitted instead of `CallSideEffectInstruction` when it is certain that the
+ * call target cannot write to escaped memory.
*/
class CallReadSideEffectInstruction extends SideEffectInstruction {
CallReadSideEffectInstruction() { getOpcode() instanceof Opcode::CallReadSideEffect }
@@ -1046,7 +1636,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction {
getOpcode() instanceof Opcode::SizedBufferReadSideEffect
}
- Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+ /**
+ * Gets the operand that holds the number of bytes read from the buffer.
+ */
+ final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the number of bytes read from the buffer.
+ */
+ final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() }
}
/**
@@ -1056,7 +1654,15 @@ class SizedBufferReadSideEffectInstruction extends ReadSideEffectInstruction {
class WriteSideEffectInstruction extends SideEffectInstruction, IndexedInstruction {
WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
- Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
+ /**
+ * Get the operand that holds the address of the memory to be written.
+ */
+ final AddressOperand getDestinationAddressOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the address of the memory to be written.
+ */
+ Instruction getDestinationAddress() { result = getDestinationAddressOperand().getDef() }
}
/**
@@ -1087,11 +1693,20 @@ class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstructi
getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
}
- Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+ /**
+ * Gets the operand that holds the number of bytes written to the buffer.
+ */
+ final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the number of bytes written to the buffer.
+ */
+ final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() }
}
/**
* An instruction representing the potential write of an indirect parameter within a function call.
+ *
* Unlike `IndirectWriteSideEffectInstruction`, the location might not be completely overwritten.
* written.
*/
@@ -1103,6 +1718,7 @@ class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
/**
* An instruction representing the write of an indirect buffer parameter within a function call.
+ *
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
*/
class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
@@ -1111,6 +1727,7 @@ class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
/**
* An instruction representing the write of an indirect buffer parameter within a function call.
+ *
* Unlike `BufferWriteSideEffectInstruction`, the buffer might not be completely overwritten.
*/
class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
@@ -1118,11 +1735,19 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
}
- Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+ /**
+ * Gets the operand that holds the number of bytes written to the buffer.
+ */
+ final BufferSizeOperand getBufferSizeOperand() { result = getAnOperand() }
+
+ /**
+ * Gets the instruction whose result provides the number of bytes written to the buffer.
+ */
+ final Instruction getBufferSize() { result = getBufferSizeOperand().getDef() }
}
/**
- * An instruction representing the initial value of newly allocated memory, e.g. the result of a
+ * An instruction representing the initial value of newly allocated memory, such as the result of a
* call to `malloc`.
*/
class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
@@ -1211,7 +1836,7 @@ class CatchByTypeInstruction extends CatchInstruction {
CatchByTypeInstruction() {
getOpcode() instanceof Opcode::CatchByType and
- exceptionType = Construction::getInstructionExceptionType(this)
+ exceptionType = Raw::getInstructionExceptionType(this)
}
final override string getImmediateString() { result = exceptionType.toString() }
@@ -1337,29 +1962,43 @@ class ChiInstruction extends Instruction {
* Gets the operand that represents the new value written by the memory write.
*/
final Instruction getPartial() { result = getPartialOperand().getDef() }
+
+ /**
+ * Gets the bit range `[startBit, endBit)` updated by the partial operand of this `ChiInstruction`, relative to the start address of the total operand.
+ */
+ final predicate getUpdatedInterval(int startBit, int endBit) {
+ Construction::getIntervalUpdatedByChi(this, startBit, endBit)
+ }
}
/**
- * An instruction representing unreachable code. Inserted in place of the original target
- * instruction of a `ConditionalBranch` or `Switch` instruction where that particular edge is
- * infeasible.
+ * An instruction representing unreachable code.
+ *
+ * This instruction is inserted in place of the original target instruction of a `ConditionalBranch`
+ * or `Switch` instruction where that particular edge is infeasible.
*/
class UnreachedInstruction extends Instruction {
UnreachedInstruction() { getOpcode() instanceof Opcode::Unreached }
}
/**
- * An instruction representing a built-in operation. This is used to represent
- * operations such as access to variable argument lists.
+ * An instruction representing a built-in operation.
+ *
+ * This is used to represent a variety of intrinsic operations provided by the compiler
+ * implementation, such as vector arithmetic.
*/
class BuiltInOperationInstruction extends Instruction {
Language::BuiltInOperation operation;
BuiltInOperationInstruction() {
getOpcode() instanceof BuiltInOperationOpcode and
- operation = Construction::getInstructionBuiltInOperation(this)
+ operation = Raw::getInstructionBuiltInOperation(this)
}
+ /**
+ * Gets the language-specific `BuiltInOperation` object that specifies the operation that is
+ * performed by this instruction.
+ */
final Language::BuiltInOperation getBuiltInOperation() { result = operation }
}
@@ -1372,3 +2011,59 @@ class BuiltInInstruction extends BuiltInOperationInstruction {
final override string getImmediateString() { result = getBuiltInOperation().toString() }
}
+
+/**
+ * An instruction that returns a `va_list` to access the arguments passed to the `...` parameter.
+ *
+ * The operand specifies the address of the `IREllipsisVariable` used to represent the `...`
+ * parameter. The result is a `va_list` that initially refers to the first argument that was passed
+ * to the `...` parameter.
+ */
+class VarArgsStartInstruction extends UnaryInstruction {
+ VarArgsStartInstruction() { getOpcode() instanceof Opcode::VarArgsStart }
+}
+
+/**
+ * An instruction that cleans up a `va_list` after it is no longer in use.
+ *
+ * The operand specifies the address of the `va_list` to clean up. This instruction does not return
+ * a result.
+ */
+class VarArgsEndInstruction extends UnaryInstruction {
+ VarArgsEndInstruction() { getOpcode() instanceof Opcode::VarArgsEnd }
+}
+
+/**
+ * An instruction that returns the address of the argument currently pointed to by a `va_list`.
+ *
+ * The operand is the `va_list` that points to the argument. The result is the address of the
+ * argument.
+ */
+class VarArgInstruction extends UnaryInstruction {
+ VarArgInstruction() { getOpcode() instanceof Opcode::VarArg }
+}
+
+/**
+ * An instruction that modifies a `va_list` to point to the next argument that was passed to the
+ * `...` parameter.
+ *
+ * The operand is the current `va_list`. The result is an updated `va_list` that points to the next
+ * argument of the `...` parameter.
+ */
+class NextVarArgInstruction extends UnaryInstruction {
+ NextVarArgInstruction() { getOpcode() instanceof Opcode::NextVarArg }
+}
+
+/**
+ * An instruction that allocates a new object on the managed heap.
+ *
+ * This instruction is used to represent the allocation of a new object in C# using the `new`
+ * expression. This instruction does not invoke a constructor for the object. Instead, there will be
+ * a subsequent `Call` instruction to invoke the appropriate constructor directory, passing the
+ * result of the `NewObj` as the `this` argument.
+ *
+ * The result is the address of the newly allocated object.
+ */
+class NewObjInstruction extends Instruction {
+ NewObjInstruction() { getOpcode() instanceof Opcode::NewObj }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll
index f82704094c8e..a12e35d471b8 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes that represent the input values of IR instructions.
+ */
+
private import internal.IRInternal
private import Instruction
private import IRBlock
@@ -75,13 +79,21 @@ private PhiOperandBase phiOperand(
}
/**
- * A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
+ * An operand of an `Instruction`. The operand represents a use of the result of one instruction
+ * (the defining instruction) in another instruction (the use instruction)
*/
class Operand extends TOperand {
+ /** Gets a textual representation of this element. */
string toString() { result = "Operand" }
+ /**
+ * Gets the location of the source code for this operand.
+ */
final Language::Location getLocation() { result = getUse().getLocation() }
+ /**
+ * Gets the function that contains this operand.
+ */
final IRFunction getEnclosingIRFunction() { result = getUse().getEnclosingIRFunction() }
/**
@@ -139,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
@@ -268,8 +285,13 @@ 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() }
+ /**
+ * Gets the `OperandTag` that specifies how this operand is used by its `Instruction`.
+ */
final OperandTag getOperandTag() { result = tag }
}
@@ -292,6 +314,9 @@ class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
}
}
+/**
+ * A memory operand other than the operand of a `Phi` instruction.
+ */
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
override MemoryOperandTag tag;
@@ -311,8 +336,19 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOper
not Construction::isInCycle(useInstr) and
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
}
+
+ /**
+ * Holds if the operand totally overlaps with its definition and consumes the
+ * bit range `[startBitOffset, endBitOffset)` relative to the start address of the definition.
+ */
+ predicate getUsedInterval(int startBitOffset, int endBitOffset) {
+ Construction::getUsedInterval(this, startBitOffset, endBitOffset)
+ }
}
+/**
+ * A memory operand whose type may be different from the type of the result of its definition.
+ */
class TypedOperand extends NonPhiMemoryOperand {
override TypedOperandTag tag;
@@ -416,6 +452,9 @@ class PositionalArgumentOperand extends ArgumentOperand {
final int getIndex() { result = tag.getArgIndex() }
}
+/**
+ * An operand representing memory read as a side effect of evaluating another instruction.
+ */
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
}
@@ -445,6 +484,8 @@ 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 d9c0df44e12e..59dadee71545 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll
@@ -1,3 +1,13 @@
+/**
+ * Outputs a representation of the IR as a control flow graph.
+ *
+ * This file contains the actual implementation of `PrintIR.ql`. For test cases and very small
+ * databases, `PrintIR.ql` can be run directly to dump the IR for the entire database. For most
+ * uses, however, it is better to write a query that imports `PrintIR.qll`, extends
+ * `PrintIRConfiguration`, and overrides `shouldPrintFunction()` to select a subset of functions to
+ * dump.
+ */
+
private import internal.IRInternal
private import IR
private import internal.PrintIRImports as Imports
@@ -9,6 +19,7 @@ private newtype TPrintIRConfiguration = MkPrintIRConfiguration()
* The query can extend this class to control which functions are printed.
*/
class PrintIRConfiguration extends TPrintIRConfiguration {
+ /** Gets a textual representation of this configuration. */
string toString() { result = "PrintIRConfiguration" }
/**
@@ -39,6 +50,37 @@ 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
@@ -47,7 +89,7 @@ private newtype TPrintableIRNode =
/**
* A node to be emitted in the IR graph.
*/
-abstract class PrintableIRNode extends TPrintableIRNode {
+abstract private class PrintableIRNode extends TPrintableIRNode {
abstract string toString();
/**
@@ -98,7 +140,7 @@ abstract class PrintableIRNode extends TPrintableIRNode {
/**
* An IR graph node representing a `IRFunction` object.
*/
-class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction {
+private class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction {
IRFunction irFunc;
PrintableIRFunction() { this = TPrintableIRFunction(irFunc) }
@@ -129,7 +171,7 @@ class PrintableIRFunction extends PrintableIRNode, TPrintableIRFunction {
/**
* An IR graph node representing an `IRBlock` object.
*/
-class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
+private class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
IRBlock block;
PrintableIRBlock() { this = TPrintableIRBlock(block) }
@@ -161,7 +203,7 @@ class PrintableIRBlock extends PrintableIRNode, TPrintableIRBlock {
/**
* An IR graph node representing an `Instruction`.
*/
-class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
+private class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
Instruction instr;
PrintableInstruction() { this = TPrintableInstruction(instr) }
@@ -179,7 +221,7 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
|
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()) + " = " +
@@ -199,6 +241,22 @@ class PrintableInstruction extends PrintableIRNode, TPrintableInstruction {
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) {
@@ -224,6 +282,9 @@ private string getPaddingString(int n) {
n > 0 and n <= maxColumnWidth() and result = getPaddingString(n - 1) + " "
}
+/**
+ * Holds if `node` belongs to the output graph, and its property `key` has the given `value`.
+ */
query predicate nodes(PrintableIRNode node, string key, string value) {
value = node.getProperty(key)
}
@@ -237,6 +298,10 @@ private int getSuccessorIndex(IRBlock pred, IRBlock succ) {
)
}
+/**
+ * Holds if the output graph contains an edge from `pred` to `succ`, and that edge's property `key`
+ * has the given `value`.
+ */
query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key, string value) {
exists(EdgeKind kind, IRBlock predBlock, IRBlock succBlock |
predBlock = pred.getBlock() and
@@ -256,6 +321,9 @@ query predicate edges(PrintableIRBlock pred, PrintableIRBlock succ, string key,
)
}
+/**
+ * Holds if `parent` is the parent node of `child` in the output graph.
+ */
query predicate parents(PrintableIRNode child, PrintableIRNode parent) {
parent = child.getParent()
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll
index 1612e0065b77..19fb0490f808 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll
@@ -196,16 +196,17 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) {
bitOffset = Ints::unknown()
}
-private predicate isArgumentForParameter(CallInstruction ci, Operand operand, Instruction init) {
+private predicate isArgumentForParameter(
+ CallInstruction ci, Operand operand, InitializeParameterInstruction init
+) {
exists(Language::Function f |
ci = operand.getUse() and
f = ci.getStaticCallTarget() and
(
- init.(InitializeParameterInstruction).getParameter() =
- f.getParameter(operand.(PositionalArgumentOperand).getIndex())
+ init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex())
or
- init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and
- init.getEnclosingFunction() = f and
+ init.getIRVariable() instanceof IRThisVariable and
+ unique( | | init.getEnclosingFunction()) = f and
operand instanceof ThisArgumentOperand
) and
not Language::isFunctionVirtual(f) and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll
new file mode 100644
index 000000000000..8ec63b7c1cbd
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll
@@ -0,0 +1 @@
+import semmle.code.cpp.ir.implementation.internal.IRFunctionBase as IRFunctionBase
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll
index 4cc52d3bbf99..3a7a08accc0d 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRInternal.qll
@@ -1,3 +1,4 @@
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import SSAConstruction as Construction
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll
index 30414bb5db3a..a6cb78b2b3a7 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll
@@ -1,5 +1,11 @@
import SSAConstructionInternal
-private import SSAConstructionImports
+private import SSAConstructionImports as Imports
+private import Imports::Opcode
+private import Imports::OperandTag
+private import Imports::Overlap
+private import Imports::TInstruction
+private import Imports::RawIR as RawIR
+private import SSAInstructions
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -10,52 +16,45 @@ import Cached
cached
private module Cached {
- private IRBlock getNewBlock(OldBlock oldBlock) {
- result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
+ cached
+ predicate hasPhiInstructionCached(
+ OldInstruction blockStartInstr, Alias::MemoryLocation defLocation
+ ) {
+ exists(OldBlock oldBlock |
+ definitionHasPhiNode(defLocation, oldBlock) and
+ blockStartInstr = oldBlock.getFirstInstruction()
+ )
}
cached
- predicate functionHasIR(Language::Function func) {
- exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
+ predicate hasChiInstructionCached(OldInstruction primaryInstruction) {
+ hasChiNode(_, primaryInstruction)
}
cached
- OldInstruction getOldInstruction(Instruction instr) { instr = WrappedInstruction(result) }
-
- private IRVariable getNewIRVariable(OldIR::IRVariable var) {
- // This is just a type cast. Both classes derive from the same newtype.
- result = var
+ predicate hasUnreachedInstructionCached(IRFunction irFunc) {
+ exists(OldInstruction oldInstruction |
+ irFunc = oldInstruction.getEnclosingIRFunction() and
+ Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
+ )
}
- cached
- newtype TInstruction =
- WrappedInstruction(OldInstruction oldInstruction) {
- not oldInstruction instanceof OldIR::PhiInstruction
- } or
- Phi(OldBlock block, Alias::MemoryLocation defLocation) {
- definitionHasPhiNode(defLocation, block)
- } or
- Chi(OldInstruction oldInstruction) {
- not oldInstruction instanceof OldIR::PhiInstruction and
- hasChiNode(_, oldInstruction)
- } or
- Unreached(Language::Function function) {
- exists(OldInstruction oldInstruction |
- function = oldInstruction.getEnclosingFunction() and
- Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
- )
- }
+ class TStageInstruction =
+ TRawInstruction or TPhiInstruction or TChiInstruction or TUnreachedInstruction;
cached
- predicate hasTempVariable(
- Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
- ) {
- exists(OldIR::IRTempVariable var |
- var.getEnclosingFunction() = func and
- var.getAST() = ast and
- var.getTag() = tag and
- var.getLanguageType() = type
- )
+ predicate hasInstruction(TStageInstruction instr) {
+ instr instanceof TRawInstruction and instr instanceof OldInstruction
+ or
+ instr instanceof TPhiInstruction
+ or
+ instr instanceof TChiInstruction
+ or
+ instr instanceof TUnreachedInstruction
+ }
+
+ private IRBlock getNewBlock(OldBlock oldBlock) {
+ result.getFirstInstruction() = getNewInstruction(oldBlock.getFirstInstruction())
}
cached
@@ -73,7 +72,7 @@ private module Cached {
or
// Chi instructions track virtual variables, and therefore a chi instruction is
// conflated if it's associated with the aliased virtual variable.
- exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
+ exists(OldInstruction oldInstruction | instruction = getChi(oldInstruction) |
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
Alias::AliasedVirtualVariable
)
@@ -81,7 +80,7 @@ private module Cached {
// Phi instructions track locations, and therefore a phi instruction is
// conflated if it's associated with a conflated location.
exists(Alias::MemoryLocation location |
- instruction = Phi(_, location) and
+ instruction = getPhi(_, location) and
not exists(location.getAllocation())
)
}
@@ -128,7 +127,7 @@ private module Cached {
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
)
or
- instruction = Chi(getOldInstruction(result)) and
+ instruction = getChi(getOldInstruction(result)) and
tag instanceof ChiPartialOperandTag and
overlap instanceof MustExactlyOverlap
or
@@ -150,6 +149,34 @@ private module Cached {
)
}
+ /**
+ * Holds if the partial operand of this `ChiInstruction` updates the bit range
+ * `[startBitOffset, endBitOffset)` of the total operand.
+ */
+ cached
+ predicate getIntervalUpdatedByChi(ChiInstruction chi, int startBitOffset, int endBitOffset) {
+ exists(Alias::MemoryLocation location, OldInstruction oldInstruction |
+ oldInstruction = getOldInstruction(chi.getPartial()) and
+ location = Alias::getResultMemoryLocation(oldInstruction) and
+ startBitOffset = Alias::getStartBitOffset(location) and
+ endBitOffset = Alias::getEndBitOffset(location)
+ )
+ }
+
+ /**
+ * Holds if `operand` totally overlaps with its definition and consumes the bit range
+ * `[startBitOffset, endBitOffset)`.
+ */
+ cached
+ predicate getUsedInterval(NonPhiMemoryOperand operand, int startBitOffset, int endBitOffset) {
+ exists(Alias::MemoryLocation location, OldIR::NonPhiMemoryOperand oldOperand |
+ oldOperand = operand.getUse().(OldInstruction).getAnOperand() and
+ location = Alias::getOperandMemoryLocation(oldOperand) and
+ startBitOffset = Alias::getStartBitOffset(location) and
+ endBitOffset = Alias::getEndBitOffset(location)
+ )
+ }
+
/**
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
* through a phi instruction and therefore should be impossible.
@@ -172,13 +199,15 @@ private module Cached {
pragma[noopt]
cached
- Instruction getPhiOperandDefinition(Phi instr, IRBlock newPredecessorBlock, Overlap overlap) {
+ Instruction getPhiOperandDefinition(
+ PhiInstruction instr, IRBlock newPredecessorBlock, Overlap overlap
+ ) {
exists(
Alias::MemoryLocation defLocation, Alias::MemoryLocation useLocation, OldBlock phiBlock,
OldBlock predBlock, OldBlock defBlock, int defOffset, Alias::MemoryLocation actualDefLocation
|
hasPhiOperandDefinition(defLocation, useLocation, phiBlock, predBlock, defBlock, defOffset) and
- instr = Phi(phiBlock, useLocation) and
+ instr = getPhi(phiBlock, useLocation) and
newPredecessorBlock = getNewBlock(predBlock) and
result = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
overlap = Alias::getOverlap(actualDefLocation, useLocation)
@@ -191,7 +220,7 @@ private module Cached {
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
- chiInstr = Chi(oldInstr) and
+ chiInstr = getChi(oldInstr) and
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
@@ -203,21 +232,11 @@ private module Cached {
cached
Instruction getPhiInstructionBlockStart(PhiInstruction instr) {
exists(OldBlock oldBlock |
- instr = Phi(oldBlock, _) and
+ instr = getPhi(oldBlock, _) and
result = getNewInstruction(oldBlock.getFirstInstruction())
)
}
- cached
- Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
- result = getOldInstruction(instruction).getConvertedResultExpression()
- }
-
- cached
- Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
- result = getOldInstruction(instruction).getUnconvertedResultExpression()
- }
-
/*
* This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node,
* that node is its successor in the new successor relation, and the Chi node's successors are
@@ -228,20 +247,20 @@ private module Cached {
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
if hasChiNode(_, getOldInstruction(instruction))
then
- result = Chi(getOldInstruction(instruction)) and
+ result = getChi(getOldInstruction(instruction)) and
kind instanceof GotoEdge
else (
exists(OldInstruction oldInstruction |
oldInstruction = getOldInstruction(instruction) and
(
if Reachability::isInfeasibleInstructionSuccessor(oldInstruction, kind)
- then result = Unreached(instruction.getEnclosingFunction())
+ then result = unreachedInstruction(instruction.getEnclosingIRFunction())
else result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
)
or
exists(OldInstruction oldInstruction |
- instruction = Chi(oldInstruction) and
+ instruction = getChi(oldInstruction) and
result = getNewInstruction(oldInstruction.getSuccessor(kind))
)
)
@@ -260,137 +279,73 @@ private module Cached {
// `oldInstruction`, in which case the back edge should come out of the
// chi node instead.
if hasChiNode(_, oldInstruction)
- then instruction = Chi(oldInstruction)
+ then instruction = getChi(oldInstruction)
else instruction = getNewInstruction(oldInstruction)
)
}
cached
- Language::AST getInstructionAST(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction)
- or
- instruction = Chi(oldInstruction)
- |
- result = oldInstruction.getAST()
+ Language::AST getInstructionAST(Instruction instr) {
+ result = getOldInstruction(instr).getAST()
+ or
+ exists(RawIR::Instruction blockStartInstr |
+ instr = phiInstruction(blockStartInstr, _) and
+ result = blockStartInstr.getAST()
)
or
- exists(OldBlock block |
- instruction = Phi(block, _) and
- result = block.getFirstInstruction().getAST()
+ exists(RawIR::Instruction primaryInstr |
+ instr = chiInstruction(primaryInstr) and
+ result = primaryInstr.getAST()
)
or
- instruction = Unreached(result)
+ exists(IRFunctionBase irFunc |
+ instr = unreachedInstruction(irFunc) and result = irFunc.getFunction()
+ )
}
cached
- Language::LanguageType getInstructionResultType(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction) and
- result = oldInstruction.getResultLanguageType()
- )
+ Language::LanguageType getInstructionResultType(Instruction instr) {
+ result = instr.(RawIR::Instruction).getResultLanguageType()
or
- exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
- instruction = Chi(oldInstruction) and
- hasChiNode(vvar, oldInstruction) and
- result = vvar.getType()
+ exists(Alias::MemoryLocation defLocation |
+ instr = phiInstruction(_, defLocation) and
+ result = defLocation.getType()
)
or
- exists(Alias::MemoryLocation location |
- instruction = Phi(_, location) and
- result = location.getType()
+ exists(Instruction primaryInstr, Alias::VirtualVariable vvar |
+ instr = chiInstruction(primaryInstr) and
+ hasChiNode(vvar, primaryInstr) and
+ result = vvar.getType()
)
or
- instruction = Unreached(_) and
- result = Language::getVoidType()
+ instr = unreachedInstruction(_) and result = Language::getVoidType()
}
cached
- Opcode getInstructionOpcode(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction) and
- result = oldInstruction.getOpcode()
- )
+ Opcode getInstructionOpcode(Instruction instr) {
+ result = getOldInstruction(instr).getOpcode()
or
- instruction instanceof Chi and
- result instanceof Opcode::Chi
+ instr = phiInstruction(_, _) and result instanceof Opcode::Phi
or
- instruction instanceof Phi and
- result instanceof Opcode::Phi
+ instr = chiInstruction(_) and result instanceof Opcode::Chi
or
- instruction instanceof Unreached and
- result instanceof Opcode::Unreached
+ instr = unreachedInstruction(_) and result instanceof Opcode::Unreached
}
cached
- IRFunction getInstructionEnclosingIRFunction(Instruction instruction) {
- exists(OldInstruction oldInstruction |
- instruction = WrappedInstruction(oldInstruction)
- or
- instruction = Chi(oldInstruction)
- |
- result.getFunction() = oldInstruction.getEnclosingFunction()
- )
+ IRFunctionBase getInstructionEnclosingIRFunction(Instruction instr) {
+ result = getOldInstruction(instr).getEnclosingIRFunction()
or
- exists(OldBlock block |
- instruction = Phi(block, _) and
- result.getFunction() = block.getEnclosingFunction()
+ exists(OldInstruction blockStartInstr |
+ instr = phiInstruction(blockStartInstr, _) and
+ result = blockStartInstr.getEnclosingIRFunction()
)
or
- instruction = Unreached(result.getFunction())
- }
-
- cached
- IRVariable getInstructionVariable(Instruction instruction) {
- result =
- getNewIRVariable(getOldInstruction(instruction).(OldIR::VariableInstruction).getIRVariable())
- }
-
- cached
- Language::Field getInstructionField(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
- }
-
- cached
- int getInstructionIndex(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
- }
-
- cached
- Language::Function getInstructionFunction(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
- }
-
- cached
- string getInstructionConstantValue(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::ConstantValueInstruction).getValue()
- }
-
- cached
- Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
- result =
- getOldInstruction(instruction).(OldIR::BuiltInOperationInstruction).getBuiltInOperation()
- }
-
- cached
- Language::LanguageType getInstructionExceptionType(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
- }
-
- cached
- int getInstructionElementSize(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize()
- }
-
- cached
- predicate getInstructionInheritance(
- Instruction instruction, Language::Class baseClass, Language::Class derivedClass
- ) {
- exists(OldIR::InheritanceConversionInstruction oldInstr |
- oldInstr = getOldInstruction(instruction) and
- baseClass = oldInstr.getBaseClass() and
- derivedClass = oldInstr.getDerivedClass()
+ exists(OldInstruction primaryInstr |
+ instr = chiInstruction(primaryInstr) and result = primaryInstr.getEnclosingIRFunction()
)
+ or
+ instr = unreachedInstruction(result)
}
cached
@@ -401,7 +356,7 @@ private module Cached {
)
or
exists(OldIR::Instruction oldInstruction |
- instruction = Chi(oldInstruction) and
+ instruction = getChi(oldInstruction) and
result = getNewInstruction(oldInstruction)
)
}
@@ -409,6 +364,14 @@ private module Cached {
private Instruction getNewInstruction(OldInstruction instr) { getOldInstruction(result) = instr }
+private OldInstruction getOldInstruction(Instruction instr) { instr = result }
+
+private ChiInstruction getChi(OldInstruction primaryInstr) { result = chiInstruction(primaryInstr) }
+
+private PhiInstruction getPhi(OldBlock defBlock, Alias::MemoryLocation defLocation) {
+ result = phiInstruction(defBlock.getFirstInstruction(), defLocation)
+}
+
/**
* Holds if instruction `def` needs to have a `Chi` instruction inserted after it, to account for a partial definition
* of a virtual variable. The `Chi` instruction provides a definition of the entire virtual variable of which the
@@ -588,7 +551,7 @@ module DefUse {
|
// An odd offset corresponds to the `Chi` instruction.
defOffset = oldOffset * 2 + 1 and
- result = Chi(oldInstr) and
+ result = getChi(oldInstr) and
(
defLocation = Alias::getResultMemoryLocation(oldInstr) or
defLocation = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable()
@@ -607,7 +570,7 @@ module DefUse {
or
defOffset = -1 and
hasDefinition(_, defLocation, defBlock, defOffset) and
- result = Phi(defBlock, defLocation) and
+ result = getPhi(defBlock, defLocation) and
actualDefLocation = defLocation
}
@@ -891,7 +854,7 @@ private module CachedForDebugging {
)
or
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
- instr = Phi(phiBlock, location) and
+ instr = getPhi(phiBlock, location) and
result =
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
if location instanceof Alias::VirtualVariable
@@ -901,7 +864,7 @@ private module CachedForDebugging {
else specificity = "s"
)
or
- instr = Unreached(_) and
+ instr = unreachedInstruction(_) and
result = "Unreached"
}
@@ -961,3 +924,19 @@ module SSAConsistency {
)
}
}
+
+/**
+ * Provides the portion of the parameterized IR interface that is used to construct the SSA stages
+ * of the IR. The raw stage of the IR does not expose these predicates.
+ * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures
+ * that all of SSA construction will be evaluated in the same stage.
+ */
+module SSA {
+ class MemoryLocation = Alias::MemoryLocation;
+
+ predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2;
+
+ predicate hasChiInstruction = Cached::hasChiInstructionCached/1;
+
+ predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1;
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll
index 00f12020a29d..f347df86ba1e 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll
@@ -1,3 +1,5 @@
-import semmle.code.cpp.ir.implementation.Opcode
-import semmle.code.cpp.ir.implementation.internal.OperandTag
-import semmle.code.cpp.ir.internal.Overlap
+import semmle.code.cpp.ir.implementation.Opcode as Opcode
+import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
+import semmle.code.cpp.ir.internal.Overlap as Overlap
+import semmle.code.cpp.ir.implementation.internal.TInstruction as TInstruction
+import semmle.code.cpp.ir.implementation.raw.IR as RawIR
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll
index 4cfbdfe831e1..73b08d1286b5 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll
@@ -2,5 +2,7 @@ import semmle.code.cpp.ir.implementation.raw.IR as OldIR
import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability
import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR
+import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as RawStage
+import semmle.code.cpp.ir.implementation.internal.TInstruction::UnaliasedSSAInstructions as SSAInstructions
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
import SimpleSSA as Alias
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll
index aee4959513ed..a7b9160bdc75 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll
@@ -59,6 +59,12 @@ class MemoryLocation extends TMemoryLocation {
final string getUniqueId() { result = var.getUniqueId() }
}
+/**
+ * Represents a set of `MemoryLocation`s that cannot overlap with
+ * `MemoryLocation`s outside of the set. The `VirtualVariable` will be
+ * represented by a `MemoryLocation` that totally overlaps all other
+ * `MemoryLocations` in the set.
+ */
class VirtualVariable extends MemoryLocation { }
/** A virtual variable that groups all escaped memory within a function. */
@@ -79,3 +85,9 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
result = getMemoryLocation(getAddressOperandAllocation(operand.getAddressOperand()))
}
+
+/** Gets the start bit offset of a `MemoryLocation`, if any. */
+int getStartBitOffset(MemoryLocation location) { none() }
+
+/** Gets the end bit offset of a `MemoryLocation`, if any. */
+int getEndBitOffset(MemoryLocation location) { none() }
diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
index f8c4ceaf9049..2ce23f098a2d 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
@@ -1,7 +1,7 @@
private import cpp
private import semmle.code.cpp.Print
private import semmle.code.cpp.ir.implementation.IRType
-private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
+private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction::Raw as Raw
private int getPointerSize() { result = max(any(NullPointerType t).getSize()) }
@@ -143,7 +143,7 @@ private predicate isOpaqueType(Type type) {
predicate hasOpaqueType(Type tag, int byteSize) {
isOpaqueType(tag) and byteSize = getTypeSize(tag)
or
- tag instanceof UnknownType and IRConstruction::needsUnknownOpaqueType(byteSize)
+ tag instanceof UnknownType and Raw::needsUnknownOpaqueType(byteSize)
}
/**
@@ -191,7 +191,7 @@ private newtype TCppType =
TPRValueType(Type type) { exists(getIRTypeForPRValue(type)) } or
TFunctionGLValueType() or
TGLValueAddressType(Type type) or
- TUnknownOpaqueType(int byteSize) { IRConstruction::needsUnknownOpaqueType(byteSize) } or
+ TUnknownOpaqueType(int byteSize) { Raw::needsUnknownOpaqueType(byteSize) } or
TUnknownType()
/**
diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll
index 8ce0549b2b4d..f9a0c574f8c3 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/Overlap.qll
@@ -3,18 +3,33 @@ private newtype TOverlap =
TMustTotallyOverlap() or
TMustExactlyOverlap()
+/**
+ * Represents a possible overlap between two memory ranges.
+ */
abstract class Overlap extends TOverlap {
abstract string toString();
}
+/**
+ * Represents a partial overlap between two memory ranges, which may or may not
+ * actually occur in practice.
+ */
class MayPartiallyOverlap extends Overlap, TMayPartiallyOverlap {
final override string toString() { result = "MayPartiallyOverlap" }
}
+/**
+ * Represents an overlap in which the first memory range is known to include all
+ * bits of the second memory range, but may be larger or have a different type.
+ */
class MustTotallyOverlap extends Overlap, TMustTotallyOverlap {
final override string toString() { result = "MustTotallyOverlap" }
}
+/**
+ * Represents an overlap between two memory ranges that have the same extent and
+ * the same type.
+ */
class MustExactlyOverlap extends Overlap, TMustExactlyOverlap {
final override string toString() { result = "MustExactlyOverlap" }
}
diff --git a/cpp/ql/src/semmle/code/cpp/metrics/MetricFunction.qll b/cpp/ql/src/semmle/code/cpp/metrics/MetricFunction.qll
index 3dbb290d71a7..45036cfddf33 100644
--- a/cpp/ql/src/semmle/code/cpp/metrics/MetricFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/metrics/MetricFunction.qll
@@ -334,7 +334,7 @@ private predicate branchingExpr(Expr expr) {
* Gets the number of branching statements and expressions in a block. This is
* for computing cyclomatic complexity.
*/
-int cyclomaticComplexityBranches(Block b) {
+int cyclomaticComplexityBranches(BlockStmt b) {
result =
count(Stmt stmt |
branchingStmt(stmt) and
@@ -373,7 +373,7 @@ private predicate skipParent(Stmt s) {
exists(Stmt parent | parent = s.getParentStmt() |
s instanceof IfStmt and parent.(IfStmt).getElse() = s
or
- parent instanceof Block
+ parent instanceof BlockStmt
or
exists(File f, int startLine, int startCol |
startsAt(s, f, startLine, startCol) and
diff --git a/cpp/ql/src/semmle/code/cpp/models/Models.qll b/cpp/ql/src/semmle/code/cpp/models/Models.qll
index 82ae1fdc4f0a..75e81fdc318e 100644
--- a/cpp/ql/src/semmle/code/cpp/models/Models.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/Models.qll
@@ -4,6 +4,8 @@ private import implementations.Fread
private import implementations.Gets
private import implementations.IdentityFunction
private import implementations.Inet
+private import implementations.Iterator
+private import implementations.MemberFunction
private import implementations.Memcpy
private import implementations.Memset
private import implementations.Printf
@@ -12,6 +14,11 @@ private import implementations.Strcat
private import implementations.Strcpy
private import implementations.Strdup
private import implementations.Strftime
+private import implementations.StdContainer
+private import implementations.StdPair
+private import implementations.StdMap
+private import implementations.StdSet
private import implementations.StdString
private import implementations.Swap
private import implementations.GetDelim
+private import implementations.SmartPointer
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
index 782800d0fa24..1f82b90bff42 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Allocation.qll
@@ -1,5 +1,5 @@
/**
- * Provides implementation classes modelling various methods of allocation
+ * Provides implementation classes modeling various methods of allocation
* (`malloc`, `new` etc). See `semmle.code.cpp.models.interfaces.Allocation`
* for usage information.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll
index 2ef355bf3982..4f0341b673ee 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Deallocation.qll
@@ -1,5 +1,5 @@
/**
- * Provides implementation classes modelling various methods of deallocation
+ * Provides implementation classes modeling various methods of deallocation
* (`free`, `delete` etc). See `semmle.code.cpp.models.interfaces.Deallocation`
* for usage information.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
index e5e45729e0dc..d22f2f376806 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides implementation classes modeling `gets` and various similar
+ * functions. See `semmle.code.cpp.models.Models` for usage information.
+ */
+
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.ArrayFunction
@@ -11,11 +16,10 @@ import semmle.code.cpp.models.interfaces.FlowSource
class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
SideEffectFunction, RemoteFlowFunction {
GetsFunction() {
- exists(string name | hasGlobalOrStdName(name) |
- name = "gets" or // gets(str)
- name = "fgets" or // fgets(str, num, stream)
- name = "fgetws" // fgetws(wstr, num, stream)
- )
+ // gets(str)
+ // fgets(str, num, stream)
+ // fgetws(wstr, num, stream)
+ hasGlobalOrStdName(["gets", "fgets", "fgetws"])
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
@@ -48,4 +52,17 @@ class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, Alias
output.isParameterDeref(0) and
description = "String read by " + this.getName()
}
+
+ override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
+ not hasGlobalOrStdName("gets") and
+ bufParam = 0 and
+ countParam = 1
+ }
+
+ override predicate hasArrayWithUnknownSize(int bufParam) {
+ hasGlobalOrStdName("gets") and
+ bufParam = 0
+ }
+
+ override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll
index 865a74b55717..f9eab7aba939 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll
@@ -4,16 +4,13 @@ import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.SideEffect
/**
- * The standard function templates `std::move` and `std::identity`
+ * The standard function templates `std::move` and `std::forward`.
*/
class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction {
IdentityFunction() {
this.getNamespace().getParentNamespace() instanceof GlobalNamespace and
this.getNamespace().getName() = "std" and
- (
- this.getName() = "move" or
- this.getName() = "forward"
- )
+ this.getName() = ["move", "forward"]
}
override predicate hasOnlySpecificReadSideEffects() { any() }
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll
new file mode 100644
index 000000000000..71453467d8a7
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Iterator.qll
@@ -0,0 +1,293 @@
+/**
+ * Provides implementation classes modeling C++ iterators, including
+ * `std::iterator`, `std::iterator_traits`, and types meeting the
+ * `LegacyIterator` named requirement. See `semmle.code.cpp.models.Models` for
+ * usage information.
+ */
+
+import cpp
+import semmle.code.cpp.models.interfaces.Taint
+import semmle.code.cpp.models.interfaces.DataFlow
+import semmle.code.cpp.models.interfaces.Iterator
+
+/**
+ * An instantiation of the `std::iterator_traits` template.
+ */
+class IteratorTraits extends Class {
+ IteratorTraits() {
+ this.hasQualifiedName("std", "iterator_traits") and
+ not this instanceof TemplateClass and
+ exists(TypedefType t |
+ this.getAMember() = t and
+ t.getName() = "iterator_category"
+ )
+ }
+
+ Type getIteratorType() { result = this.getTemplateArgument(0) }
+}
+
+/**
+ * A type which has the typedefs expected for an iterator.
+ */
+class IteratorByTypedefs extends Class {
+ IteratorByTypedefs() {
+ this.getAMember().(TypedefType).hasName("difference_type") and
+ this.getAMember().(TypedefType).hasName("value_type") and
+ this.getAMember().(TypedefType).hasName("pointer") and
+ this.getAMember().(TypedefType).hasName("reference") and
+ this.getAMember().(TypedefType).hasName("iterator_category") and
+ not this.hasQualifiedName("std", "iterator_traits")
+ }
+}
+
+/**
+ * The `std::iterator` class.
+ */
+class StdIterator extends Class {
+ StdIterator() { this.hasQualifiedName("std", "iterator") }
+}
+
+/**
+ * A type which can be used as an iterator
+ */
+class Iterator extends Type {
+ Iterator() {
+ this instanceof IteratorByTypedefs or
+ exists(IteratorTraits it | it.getIteratorType() = this) or
+ this instanceof StdIterator
+ }
+}
+
+private FunctionInput getIteratorArgumentInput(Operator op, int index) {
+ exists(Type t |
+ t =
+ op
+ .getACallToThisFunction()
+ .getArgument(index)
+ .getExplicitlyConverted()
+ .getType()
+ .stripTopLevelSpecifiers()
+ |
+ (
+ t instanceof Iterator or
+ t.(ReferenceType).getBaseType() instanceof Iterator
+ ) and
+ if op.getParameter(index).getUnspecifiedType() instanceof ReferenceType
+ then result.isParameterDeref(index)
+ else result.isParameter(index)
+ )
+}
+
+/**
+ * A non-member prefix `operator*` function for an iterator type.
+ */
+class IteratorPointerDereferenceOperator extends Operator, TaintFunction, IteratorReferenceFunction {
+ FunctionInput iteratorInput;
+
+ IteratorPointerDereferenceOperator() {
+ this.hasName("operator*") and
+ iteratorInput = getIteratorArgumentInput(this, 0)
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input = iteratorInput and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * A non-member `operator++` or `operator--` function for an iterator type.
+ */
+class IteratorCrementOperator extends Operator, DataFlowFunction {
+ FunctionInput iteratorInput;
+
+ IteratorCrementOperator() {
+ this.hasName(["operator++", "operator--"]) and
+ iteratorInput = getIteratorArgumentInput(this, 0)
+ }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ input = iteratorInput and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * A non-member `operator+` function for an iterator type.
+ */
+class IteratorAddOperator extends Operator, TaintFunction {
+ FunctionInput iteratorInput;
+
+ IteratorAddOperator() {
+ this.hasName("operator+") and
+ iteratorInput = getIteratorArgumentInput(this, [0, 1])
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input = iteratorInput and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * A non-member `operator-` function that takes a pointer difference type as its second argument.
+ */
+class IteratorSubOperator extends Operator, TaintFunction {
+ FunctionInput iteratorInput;
+
+ IteratorSubOperator() {
+ this.hasName("operator-") and
+ iteratorInput = getIteratorArgumentInput(this, 0) and
+ this.getParameter(1).getUnspecifiedType() instanceof IntegralType // not an iterator difference
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input = iteratorInput and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * A non-member `operator+=` or `operator-=` function for an iterator type.
+ */
+class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
+ IteratorAssignArithmeticOperator() {
+ this.hasName(["operator+=", "operator-="]) and
+ this.getDeclaringType() instanceof Iterator
+ }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ input.isParameter(0) and
+ output.isReturnValue()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isParameterDeref(1) and
+ output.isParameterDeref(0)
+ }
+}
+
+/**
+ * A prefix `operator*` member function for an iterator type.
+ */
+class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction,
+ IteratorReferenceFunction {
+ IteratorPointerDereferenceMemberOperator() {
+ this.hasName("operator*") and
+ this.getDeclaringType() instanceof Iterator
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * An `operator++` or `operator--` member function for an iterator type.
+ */
+class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
+ IteratorCrementMemberOperator() {
+ this.hasName(["operator++", "operator--"]) and
+ this.getDeclaringType() instanceof Iterator
+ }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierAddress() and
+ output.isReturnValue()
+ or
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValueDeref()
+ }
+}
+
+/**
+ * A member `operator->` function for an iterator type.
+ */
+class IteratorFieldMemberOperator extends Operator, TaintFunction {
+ IteratorFieldMemberOperator() {
+ this.hasName("operator->") and
+ this.getDeclaringType() instanceof Iterator
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * An `operator+` or `operator-` member function of an iterator class.
+ */
+class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
+ IteratorBinaryArithmeticMemberOperator() {
+ this.hasName(["operator+", "operator-"]) and
+ this.getDeclaringType() instanceof Iterator
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * An `operator+=` or `operator-=` member function of an iterator class.
+ */
+class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
+ IteratorAssignArithmeticMemberOperator() {
+ this.hasName(["operator+=", "operator-="]) and
+ this.getDeclaringType() instanceof Iterator
+ }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierAddress() and
+ output.isReturnValue()
+ or
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValueDeref()
+ }
+}
+
+/**
+ * An `operator[]` member function of an iterator class.
+ */
+class IteratorArrayMemberOperator extends MemberFunction, TaintFunction, IteratorReferenceFunction {
+ IteratorArrayMemberOperator() {
+ this.hasName("operator[]") and
+ this.getDeclaringType() instanceof Iterator
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * A `begin` or `end` member function, or a related member function, that
+ * returns an iterator.
+ */
+class BeginOrEndFunction extends MemberFunction, TaintFunction {
+ BeginOrEndFunction() {
+ this
+ .hasName(["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend",
+ "before_begin", "cbefore_begin"]) and
+ this.getType().getUnspecifiedType() instanceof Iterator
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll
new file mode 100644
index 000000000000..0e4812cc25cc
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/MemberFunction.qll
@@ -0,0 +1,92 @@
+/**
+ * Provides models for C++ constructors and user-defined operators.
+ */
+
+import cpp
+import semmle.code.cpp.models.interfaces.DataFlow
+import semmle.code.cpp.models.interfaces.Taint
+
+/**
+ * Model for C++ conversion constructors. As of C++11 this does not correspond
+ * perfectly with the language definition of a converting constructor, however,
+ * it does correspond with the constructors we are confident taint should flow
+ * through.
+ */
+class ConversionConstructorModel extends Constructor, TaintFunction {
+ ConversionConstructorModel() {
+ strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
+ not hasSpecifier("explicit")
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // taint flow from the first constructor argument to the returned object
+ input.isParameter(0) and
+ (
+ output.isReturnValue()
+ or
+ output.isQualifierObject()
+ )
+ }
+}
+
+/**
+ * Model for C++ copy constructors.
+ */
+class CopyConstructorModel extends CopyConstructor, DataFlowFunction {
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ // data flow from the first constructor argument to the returned object
+ input.isParameter(0) and
+ (
+ output.isReturnValue()
+ or
+ output.isQualifierObject()
+ )
+ }
+}
+
+/**
+ * Model for C++ move constructors.
+ */
+class MoveConstructorModel extends MoveConstructor, DataFlowFunction {
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ // data flow from the first constructor argument to the returned object
+ input.isParameter(0) and
+ (
+ output.isReturnValue()
+ or
+ output.isQualifierObject()
+ )
+ }
+}
+
+/**
+ * Model for C++ copy assignment operators.
+ */
+class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintFunction {
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // taint flow from argument to self
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ or
+ // taint flow from argument to return value
+ input.isParameterDeref(0) and
+ output.isReturnValueDeref()
+ // TODO: it would be more accurate to model copy assignment as data flow
+ }
+}
+
+/**
+ * Model for C++ move assignment operators.
+ */
+class MoveAssignmentOperatorModel extends MoveAssignmentOperator, TaintFunction {
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // taint flow from argument to self
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ or
+ // taint flow from argument to return value
+ input.isParameterDeref(0) and
+ output.isReturnValueDeref()
+ // TODO: it would be more accurate to model move assignment as data flow
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll
index 0951daea11b7..ef4aa8b7290e 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides implementation classes modeling `memcpy` and various similar
+ * functions. See `semmle.code.cpp.models.Models` for usage information.
+ */
+
import semmle.code.cpp.Function
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll
index 7fc7fbfc9e5b..2c34369aee44 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides implementation classes modeling `memset` and various similar
+ * functions. See `semmle.code.cpp.models.Models` for usage information.
+ */
+
import semmle.code.cpp.Function
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll
index b5047c25e854..61dd3bc50b9d 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll
@@ -1,3 +1,10 @@
+/**
+ * Provides implementation classes modeling various standard formatting
+ * functions (`printf`, `snprintf` etc).
+ * See `semmle.code.cpp.models.interfaces.FormattingFunction` for usage
+ * information.
+ */
+
import semmle.code.cpp.models.interfaces.FormattingFunction
import semmle.code.cpp.models.interfaces.Alias
@@ -59,12 +66,25 @@ class Sprintf extends FormattingFunction {
Sprintf() {
this instanceof TopLevelFunction and
(
- hasGlobalOrStdName("sprintf") or
- hasGlobalName("_sprintf_l") or
- hasGlobalName("__swprintf_l") or
- hasGlobalOrStdName("wsprintf") or
- hasGlobalName("g_strdup_printf") or
- hasGlobalName("g_sprintf") or
+ // sprintf(dst, format, args...)
+ hasGlobalOrStdName("sprintf")
+ or
+ // _sprintf_l(dst, format, locale, args...)
+ hasGlobalName("_sprintf_l")
+ or
+ // __swprintf_l(dst, format, locale, args...)
+ hasGlobalName("__swprintf_l")
+ or
+ // wsprintf(dst, format, args...)
+ hasGlobalOrStdName("wsprintf")
+ or
+ // g_strdup_printf(format, ...)
+ hasGlobalName("g_strdup_printf")
+ or
+ // g_sprintf(dst, format, ...)
+ hasGlobalName("g_sprintf")
+ or
+ // __builtin___sprintf_chk(dst, flag, os, format, ...)
hasGlobalName("__builtin___sprintf_chk")
) and
not exists(getDefinition().getFile().getRelativePath())
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll
new file mode 100644
index 000000000000..7dde921bfa1f
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/SmartPointer.qll
@@ -0,0 +1,61 @@
+import semmle.code.cpp.models.interfaces.Taint
+
+/**
+ * The `std::shared_ptr` and `std::unique_ptr` template classes.
+ */
+class UniqueOrSharedPtr extends Class {
+ UniqueOrSharedPtr() { this.hasQualifiedName("std", ["shared_ptr", "unique_ptr"]) }
+}
+
+/**
+ * The `std::make_shared` and `std::make_unique` template functions.
+ */
+class MakeUniqueOrShared extends TaintFunction {
+ MakeUniqueOrShared() { this.hasQualifiedName("std", ["make_shared", "make_unique"]) }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // Exclude the specializations of `std::make_shared` and `std::make_unique` that allocate arrays
+ // since these just take a size argument, which we don't want to propagate taint through.
+ not this.isArray() and
+ input.isParameter(_) and
+ output.isReturnValue()
+ }
+
+ /**
+ * Holds if the function returns a `shared_ptr` (or `unique_ptr`) where `T` is an
+ * array type (i.e., `U[]` for some type `U`).
+ */
+ predicate isArray() {
+ this.getTemplateArgument(0).(Type).getUnderlyingType() instanceof ArrayType
+ }
+}
+
+/**
+ * A prefix `operator*` member function for a `shared_ptr` or `unique_ptr` type.
+ */
+class UniqueOrSharedDereferenceMemberOperator extends MemberFunction, TaintFunction {
+ UniqueOrSharedDereferenceMemberOperator() {
+ this.hasName("operator*") and
+ this.getDeclaringType() instanceof UniqueOrSharedPtr
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValueDeref()
+ }
+}
+
+/**
+ * The `std::shared_ptr` or `std::unique_ptr` function `get`.
+ */
+class UniqueOrSharedGet extends TaintFunction {
+ UniqueOrSharedGet() {
+ this.hasName("get") and
+ this.getDeclaringType() instanceof UniqueOrSharedPtr
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdContainer.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdContainer.qll
new file mode 100644
index 000000000000..a339dadb860f
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdContainer.qll
@@ -0,0 +1,208 @@
+/**
+ * Provides models for C++ containers `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`.
+ */
+
+import semmle.code.cpp.models.interfaces.Taint
+import semmle.code.cpp.models.implementations.Iterator
+
+/**
+ * Additional model for standard container constructors that reference the
+ * value type of the container (that is, the `T` in `std::vector`). For
+ * example the fill constructor:
+ * ```
+ * std::vector v(100, potentially_tainted_string);
+ * ```
+ */
+class StdSequenceContainerConstructor extends Constructor, TaintFunction {
+ StdSequenceContainerConstructor() {
+ this.getDeclaringType().hasQualifiedName("std", ["vector", "deque", "list", "forward_list"])
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is a reference to the
+ * value type of the container.
+ */
+ int getAValueTypeParameterIndex() {
+ getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
+ getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector`
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is an iterator.
+ */
+ int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // taint flow from any parameter of the value type to the returned object
+ (
+ input.isParameterDeref(getAValueTypeParameterIndex()) or
+ input.isParameter(getAnIteratorParameterIndex())
+ ) and
+ (
+ output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
+ or
+ output.isQualifierObject()
+ )
+ }
+}
+
+/**
+ * The standard container function `data`.
+ */
+class StdSequenceContainerData extends TaintFunction {
+ StdSequenceContainerData() { this.hasQualifiedName("std", ["array", "vector"], "data") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from container itself (qualifier) to return value
+ input.isQualifierObject() and
+ output.isReturnValueDeref()
+ or
+ // reverse flow from returned reference to the qualifier (for writes to
+ // `data`)
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The standard container functions `push_back` and `push_front`.
+ */
+class StdSequenceContainerPush extends TaintFunction {
+ StdSequenceContainerPush() {
+ this.hasQualifiedName("std", "vector", "push_back") or
+ this.hasQualifiedName("std", "deque", ["push_back", "push_front"]) or
+ this.hasQualifiedName("std", "list", ["push_back", "push_front"]) or
+ this.hasQualifiedName("std", "forward_list", "push_front")
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from parameter to qualifier
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The standard container functions `front` and `back`.
+ */
+class StdSequenceContainerFrontBack extends TaintFunction {
+ StdSequenceContainerFrontBack() {
+ this.hasQualifiedName("std", "array", ["front", "back"]) or
+ this.hasQualifiedName("std", "vector", ["front", "back"]) or
+ this.hasQualifiedName("std", "deque", ["front", "back"]) or
+ this.hasQualifiedName("std", "list", ["front", "back"]) or
+ this.hasQualifiedName("std", "forward_list", "front")
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from object to returned reference
+ input.isQualifierObject() and
+ output.isReturnValueDeref()
+ }
+}
+
+/**
+ * The standard container functions `insert` and `insert_after`.
+ */
+class StdSequenceContainerInsert extends TaintFunction {
+ StdSequenceContainerInsert() {
+ this.hasQualifiedName("std", ["vector", "deque", "list"], "insert") or
+ this.hasQualifiedName("std", ["forward_list"], "insert_after")
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is a reference to the
+ * value type of the container.
+ */
+ int getAValueTypeParameterIndex() {
+ getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
+ getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector`
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is an iterator.
+ */
+ int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from parameter to container itself (qualifier) and return value
+ (
+ input.isQualifierObject() or
+ input.isParameterDeref(getAValueTypeParameterIndex()) or
+ input.isParameter(getAnIteratorParameterIndex())
+ ) and
+ (
+ output.isQualifierObject() or
+ output.isReturnValueDeref()
+ )
+ }
+}
+
+/**
+ * The standard container function `assign`.
+ */
+class StdSequenceContainerAssign extends TaintFunction {
+ StdSequenceContainerAssign() {
+ this.hasQualifiedName("std", ["vector", "deque", "list", "forward_list"], "assign")
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is a reference to the
+ * value type of the container.
+ */
+ int getAValueTypeParameterIndex() {
+ getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
+ getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector`
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is an iterator.
+ */
+ int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from parameter to container itself (qualifier)
+ (
+ input.isParameterDeref(getAValueTypeParameterIndex()) or
+ input.isParameter(getAnIteratorParameterIndex())
+ ) and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The standard container `swap` functions.
+ */
+class StdSequenceContainerSwap extends TaintFunction {
+ StdSequenceContainerSwap() {
+ this.hasQualifiedName("std", ["array", "vector", "deque", "list", "forward_list"], "swap")
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // container1.swap(container2)
+ input.isQualifierObject() and
+ output.isParameterDeref(0)
+ or
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The standard container functions `at` and `operator[]`.
+ */
+class StdSequenceContainerAt extends TaintFunction {
+ StdSequenceContainerAt() {
+ this.hasQualifiedName("std", ["vector", "array", "deque"], ["at", "operator[]"])
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to referenced return value
+ input.isQualifierObject() and
+ output.isReturnValueDeref()
+ or
+ // reverse flow from returned reference to the qualifier
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdMap.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdMap.qll
new file mode 100644
index 000000000000..12e6624903e6
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdMap.qll
@@ -0,0 +1,192 @@
+/**
+ * Provides models for C++ containers `std::map` and `std::unordered_map`.
+ */
+
+import semmle.code.cpp.models.interfaces.Taint
+import semmle.code.cpp.models.implementations.Iterator
+
+/**
+ * Additional model for map constructors using iterator inputs.
+ */
+class StdMapConstructor extends Constructor, TaintFunction {
+ StdMapConstructor() {
+ this.hasQualifiedName("std", "map", "map") or
+ this.hasQualifiedName("std", "unordered_map", "unordered_map")
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is an iterator.
+ */
+ int getAnIteratorParameterIndex() {
+ getParameter(result).getUnspecifiedType() instanceof Iterator
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // taint flow from any parameter of an iterator type to the qualifier
+ input.isParameterDeref(getAnIteratorParameterIndex()) and
+ (
+ output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
+ or
+ output.isQualifierObject()
+ )
+ }
+}
+
+/**
+ * The standard map `insert` and `insert_or_assign` functions.
+ */
+class StdMapInsert extends TaintFunction {
+ StdMapInsert() {
+ this.hasQualifiedName("std", ["map", "unordered_map"], ["insert", "insert_or_assign"])
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from last parameter to qualifier and return value
+ // (where the return value is a pair, this should really flow just to the first part of it)
+ input.isParameterDeref(getNumberOfParameters() - 1) and
+ (
+ output.isQualifierObject() or
+ output.isReturnValue()
+ )
+ }
+}
+
+/**
+ * The standard map `emplace` and `emplace_hint` functions.
+ */
+class StdMapEmplace extends TaintFunction {
+ StdMapEmplace() {
+ this.hasQualifiedName("std", ["map", "unordered_map"], ["emplace", "emplace_hint"])
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from the last parameter (which may be the value part used to
+ // construct a pair, or a pair to be copied / moved) to the qualifier and
+ // return value.
+ // (where the return value is a pair, this should really flow just to the first part of it)
+ input.isParameterDeref(getNumberOfParameters() - 1) and
+ (
+ output.isQualifierObject() or
+ output.isReturnValue()
+ )
+ or
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * The standard map `try_emplace` function.
+ */
+class StdMapTryEmplace extends TaintFunction {
+ StdMapTryEmplace() { this.hasQualifiedName("std", ["map", "unordered_map"], "try_emplace") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from any parameter apart from the key to qualifier and return value
+ // (here we assume taint flow from any constructor parameter to the constructed object)
+ // (where the return value is a pair, this should really flow just to the first part of it)
+ exists(int arg | arg = [1 .. getNumberOfParameters() - 1] |
+ (
+ not getUnspecifiedType() instanceof Iterator or
+ arg != 1
+ ) and
+ input.isParameterDeref(arg)
+ ) and
+ (
+ output.isQualifierObject() or
+ output.isReturnValue()
+ )
+ or
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * The standard map `swap` function.
+ */
+class StdMapSwap extends TaintFunction {
+ StdMapSwap() { this.hasQualifiedName("std", ["map", "unordered_map"], "swap") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // container1.swap(container2)
+ input.isQualifierObject() and
+ output.isParameterDeref(0)
+ or
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The standard map `merge` function.
+ */
+class StdMapMerge extends TaintFunction {
+ StdMapMerge() { this.hasQualifiedName("std", ["map", "unordered_map"], "merge") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // container1.merge(container2)
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The standard map functions `at` and `operator[]`.
+ */
+class StdMapAt extends TaintFunction {
+ StdMapAt() { this.hasQualifiedName("std", ["map", "unordered_map"], ["at", "operator[]"]) }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to referenced return value
+ input.isQualifierObject() and
+ output.isReturnValueDeref()
+ or
+ // reverse flow from returned reference to the qualifier
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The standard map `find` function.
+ */
+class StdMapFind extends TaintFunction {
+ StdMapFind() { this.hasQualifiedName("std", ["map", "unordered_map"], "find") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * The standard map `erase` function.
+ */
+class StdMapErase extends TaintFunction {
+ StdMapErase() { this.hasQualifiedName("std", ["map", "unordered_map"], "erase") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to iterator return value
+ getType().getUnderlyingType() instanceof Iterator and
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * The standard map `lower_bound`, `upper_bound` and `equal_range` functions.
+ */
+class StdMapEqualRange extends TaintFunction {
+ StdMapEqualRange() {
+ this
+ .hasQualifiedName("std", ["map", "unordered_map"],
+ ["lower_bound", "upper_bound", "equal_range"])
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to return value
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdPair.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdPair.qll
new file mode 100644
index 000000000000..b36f7ab3325d
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdPair.qll
@@ -0,0 +1,48 @@
+/**
+ * Provides models for the C++ `std::pair` class.
+ */
+
+import semmle.code.cpp.models.interfaces.Taint
+
+/**
+ * Additional model for `std::pair` constructors.
+ */
+class StdPairConstructor extends Constructor, TaintFunction {
+ StdPairConstructor() { this.hasQualifiedName("std", "pair", "pair") }
+
+ /**
+ * Gets the index of a parameter to this function that is a reference to
+ * either value type of the pair.
+ */
+ int getAValueTypeParameterIndex() {
+ getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
+ getDeclaringType().getTemplateArgument(_).(Type).getUnspecifiedType() // i.e. the `T1` or `T2` of this `std::pair`
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // taint flow from second parameter of a value type to the qualifier
+ getAValueTypeParameterIndex() = 1 and
+ input.isParameterDeref(1) and
+ (
+ output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
+ or
+ output.isQualifierObject()
+ )
+ }
+}
+
+/**
+ * The standard pair `swap` function.
+ */
+class StdPairSwap extends TaintFunction {
+ StdPairSwap() { this.hasQualifiedName("std", "pair", "swap") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // container1.swap(container2)
+ input.isQualifierObject() and
+ output.isParameterDeref(0)
+ or
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdSet.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdSet.qll
new file mode 100644
index 000000000000..06e8be4c4a41
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdSet.qll
@@ -0,0 +1,145 @@
+/**
+ * Provides models for C++ containers `std::set` and `std::unordered_set`.
+ */
+
+import semmle.code.cpp.models.interfaces.Taint
+import semmle.code.cpp.models.implementations.Iterator
+
+/**
+ * Additional model for set constructors using iterator inputs.
+ */
+class StdSetConstructor extends Constructor, TaintFunction {
+ StdSetConstructor() {
+ this.hasQualifiedName("std", "set", "set") or
+ this.hasQualifiedName("std", "unordered_set", "unordered_set")
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is an iterator.
+ */
+ int getAnIteratorParameterIndex() {
+ getParameter(result).getUnspecifiedType() instanceof Iterator
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // taint flow from any parameter of an iterator type to the qualifier
+ input.isParameterDeref(getAnIteratorParameterIndex()) and
+ (
+ output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
+ or
+ output.isQualifierObject()
+ )
+ }
+}
+
+/**
+ * The standard set `insert` and `insert_or_assign` functions.
+ */
+class StdSetInsert extends TaintFunction {
+ StdSetInsert() { this.hasQualifiedName("std", ["set", "unordered_set"], "insert") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from last parameter to qualifier and return value
+ // (where the return value is a pair, this should really flow just to the first part of it)
+ input.isParameterDeref(getNumberOfParameters() - 1) and
+ (
+ output.isQualifierObject() or
+ output.isReturnValue()
+ )
+ }
+}
+
+/**
+ * The standard set `emplace` and `emplace_hint` functions.
+ */
+class StdSetEmplace extends TaintFunction {
+ StdSetEmplace() {
+ this.hasQualifiedName("std", ["set", "unordered_set"], ["emplace", "emplace_hint"])
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from any parameter to qualifier and return value
+ // (here we assume taint flow from any constructor parameter to the constructed object)
+ // (where the return value is a pair, this should really flow just to the first part of it)
+ input.isParameter([0 .. getNumberOfParameters() - 1]) and
+ (
+ output.isQualifierObject() or
+ output.isReturnValue()
+ )
+ or
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * The standard set `swap` functions.
+ */
+class StdSetSwap extends TaintFunction {
+ StdSetSwap() { this.hasQualifiedName("std", ["set", "unordered_set"], "swap") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // container1.swap(container2)
+ input.isQualifierObject() and
+ output.isParameterDeref(0)
+ or
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The standard set `merge` function.
+ */
+class StdSetMerge extends TaintFunction {
+ StdSetMerge() { this.hasQualifiedName("std", ["set", "unordered_set"], "merge") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // container1.merge(container2)
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The standard set `find` function.
+ */
+class StdSetFind extends TaintFunction {
+ StdSetFind() { this.hasQualifiedName("std", ["set", "unordered_set"], "find") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * The standard set `erase` function.
+ */
+class StdSetErase extends TaintFunction {
+ StdSetErase() { this.hasQualifiedName("std", ["set", "unordered_set"], "erase") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to iterator return value
+ getType().getUnderlyingType() instanceof Iterator and
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * The standard set `lower_bound`, `upper_bound` and `equal_range` functions.
+ */
+class StdSetEqualRange extends TaintFunction {
+ StdSetEqualRange() {
+ this
+ .hasQualifiedName("std", ["set", "unordered_set"],
+ ["lower_bound", "upper_bound", "equal_range"])
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to return value
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll
index 9c6ebd1a8779..b8a000b963ea 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll
@@ -1,21 +1,62 @@
+/**
+ * Provides implementation classes modeling `std::string` (and other
+ * instantiations of `std::basic_string`) and `std::ostream`. See
+ * `semmle.code.cpp.models.Models` for usage information.
+ */
+
import semmle.code.cpp.models.interfaces.Taint
+import semmle.code.cpp.models.implementations.Iterator
+
+/**
+ * The `std::basic_string` template class.
+ */
+class StdBasicString extends TemplateClass {
+ StdBasicString() { this.hasQualifiedName("std", "basic_string") }
+}
/**
- * The `std::basic_string` constructor(s).
+ * Additional model for `std::string` constructors that reference the character
+ * type of the container, or an iterator. For example construction from
+ * iterators:
+ * ```
+ * std::string b(a.begin(), a.end());
+ * ```
*/
-class StdStringConstructor extends TaintFunction {
- pragma[noinline]
- StdStringConstructor() { this.hasQualifiedName("std", "basic_string", "basic_string") }
+class StdStringConstructor extends Constructor, TaintFunction {
+ StdStringConstructor() { this.getDeclaringType().hasQualifiedName("std", "basic_string") }
+
+ /**
+ * Gets the index of a parameter to this function that is a string (or
+ * character).
+ */
+ int getAStringParameterIndex() {
+ getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *`
+ getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &`
+ getParameter(result).getUnspecifiedType() =
+ getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT`
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is an iterator.
+ */
+ int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- // flow from any constructor argument to return value
- input.isParameter(_) and
- output.isReturnValue()
+ // taint flow from any parameter of the value type to the returned object
+ (
+ input.isParameterDeref(getAStringParameterIndex()) or
+ input.isParameter(getAnIteratorParameterIndex())
+ ) and
+ (
+ output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
+ or
+ output.isQualifierObject()
+ )
}
}
/**
- * The standard function `std::string.c_str`.
+ * The `std::string` function `c_str`.
*/
class StdStringCStr extends TaintFunction {
StdStringCStr() { this.hasQualifiedName("std", "basic_string", "c_str") }
@@ -23,6 +64,547 @@ class StdStringCStr extends TaintFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from string itself (qualifier) to return value
input.isQualifierObject() and
+ output.isReturnValueDeref()
+ }
+}
+
+/**
+ * The `std::string` function `data`.
+ */
+class StdStringData extends TaintFunction {
+ StdStringData() { this.hasQualifiedName("std", "basic_string", "data") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from string itself (qualifier) to return value
+ input.isQualifierObject() and
+ output.isReturnValueDeref()
+ or
+ // reverse flow from returned reference to the qualifier (for writes to
+ // `data`)
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The `std::string` function `push_back`.
+ */
+class StdStringPush extends TaintFunction {
+ StdStringPush() { this.hasQualifiedName("std", "basic_string", "push_back") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from parameter to qualifier
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The `std::string` functions `front` and `back`.
+ */
+class StdStringFrontBack extends TaintFunction {
+ StdStringFrontBack() { this.hasQualifiedName("std", "basic_string", ["front", "back"]) }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from object to returned reference
+ input.isQualifierObject() and
+ output.isReturnValueDeref()
+ }
+}
+
+/**
+ * The `std::string` function `operator+`.
+ */
+class StdStringPlus extends TaintFunction {
+ StdStringPlus() {
+ this.hasQualifiedName("std", "operator+") and
+ this.getUnspecifiedType() = any(StdBasicString s).getAnInstantiation()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from parameters to return value
+ (
+ input.isParameterDeref(0) or
+ input.isParameterDeref(1)
+ ) and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * The `std::string` functions `operator+=`, `append`, `insert` and
+ * `replace`. All of these functions combine the existing string
+ * with a new string (or character) from one of the arguments.
+ */
+class StdStringAppend extends TaintFunction {
+ StdStringAppend() {
+ this.hasQualifiedName("std", "basic_string", ["operator+=", "append", "insert", "replace"])
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is a string (or
+ * character).
+ */
+ int getAStringParameterIndex() {
+ getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *`
+ getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &`
+ getParameter(result).getUnspecifiedType() =
+ getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT`
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is an iterator.
+ */
+ int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from string and parameter to string (qualifier) and return value
+ (
+ input.isQualifierObject() or
+ input.isParameterDeref(getAStringParameterIndex()) or
+ input.isParameter(getAnIteratorParameterIndex())
+ ) and
+ (
+ output.isQualifierObject() or
+ output.isReturnValueDeref()
+ )
+ or
+ // reverse flow from returned reference to the qualifier (for writes to
+ // the result)
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The standard function `std::string.assign`.
+ */
+class StdStringAssign extends TaintFunction {
+ StdStringAssign() { this.hasQualifiedName("std", "basic_string", "assign") }
+
+ /**
+ * Gets the index of a parameter to this function that is a string (or
+ * character).
+ */
+ int getAStringParameterIndex() {
+ getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *`
+ getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &`
+ getParameter(result).getUnspecifiedType() =
+ getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT`
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is an iterator.
+ */
+ int getAnIteratorParameterIndex() { getParameter(result).getType() instanceof Iterator }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from parameter to string itself (qualifier) and return value
+ (
+ input.isParameterDeref(getAStringParameterIndex()) or
+ input.isParameter(getAnIteratorParameterIndex())
+ ) and
+ (
+ output.isQualifierObject() or
+ output.isReturnValueDeref()
+ )
+ or
+ // reverse flow from returned reference to the qualifier (for writes to
+ // the result)
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The standard function `std::string.copy`.
+ */
+class StdStringCopy extends TaintFunction {
+ StdStringCopy() { this.hasQualifiedName("std", "basic_string", "copy") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // copy(dest, num, pos)
+ input.isQualifierObject() and
+ output.isParameterDeref(0)
+ }
+}
+
+/**
+ * The standard function `std::string.substr`.
+ */
+class StdStringSubstr extends TaintFunction {
+ StdStringSubstr() { this.hasQualifiedName("std", "basic_string", "substr") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // substr(pos, num)
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * The standard functions `std::string.swap` and `std::stringstream::swap`.
+ */
+class StdStringSwap extends TaintFunction {
+ StdStringSwap() {
+ this.hasQualifiedName("std", "basic_string", "swap") or
+ this.hasQualifiedName("std", "basic_stringstream", "swap")
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // str1.swap(str2)
+ input.isQualifierObject() and
+ output.isParameterDeref(0)
+ or
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The `std::string` functions `at` and `operator[]`.
+ */
+class StdStringAt extends TaintFunction {
+ StdStringAt() { this.hasQualifiedName("std", "basic_string", ["at", "operator[]"]) }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to referenced return value
+ input.isQualifierObject() and
+ output.isReturnValueDeref()
+ or
+ // reverse flow from returned reference to the qualifier
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The `std::basic_istream` template class.
+ */
+class StdBasicIStream extends TemplateClass {
+ StdBasicIStream() { this.hasQualifiedName("std", "basic_istream") }
+}
+
+/**
+ * The `std::istream` function `operator>>` (defined as a member function).
+ */
+class StdIStreamIn extends DataFlowFunction, TaintFunction {
+ StdIStreamIn() { this.hasQualifiedName("std", "basic_istream", "operator>>") }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ // returns reference to `*this`
+ input.isQualifierAddress() and
+ output.isReturnValue()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to first parameter
+ input.isQualifierObject() and
+ output.isParameterDeref(0)
+ or
+ // reverse flow from returned reference to the qualifier
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The `std::istream` function `operator>>` (defined as a non-member function).
+ */
+class StdIStreamInNonMember extends DataFlowFunction, TaintFunction {
+ StdIStreamInNonMember() {
+ this.hasQualifiedName("std", "operator>>") and
+ this.getUnspecifiedType().(ReferenceType).getBaseType() =
+ any(StdBasicIStream s).getAnInstantiation()
+ }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ // flow from first parameter to return value
+ input.isParameter(0) and
+ output.isReturnValue()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from first parameter to second parameter
+ input.isParameterDeref(0) and
+ output.isParameterDeref(1)
+ or
+ // reverse flow from returned reference to the first parameter
+ input.isReturnValueDeref() and
+ output.isParameterDeref(0)
+ }
+}
+
+/**
+ * The `std::istream` functions `get` (without parameters) and `peek`.
+ */
+class StdIStreamGet extends TaintFunction {
+ StdIStreamGet() {
+ this.hasQualifiedName("std", "basic_istream", ["get", "peek"]) and
+ this.getNumberOfParameters() = 0
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to return value
+ input.isQualifierObject() and
+ output.isReturnValue()
+ }
+}
+
+/**
+ * The `std::istream` functions `get` (with parameters) and `read`.
+ */
+class StdIStreamRead extends DataFlowFunction, TaintFunction {
+ StdIStreamRead() {
+ this.hasQualifiedName("std", "basic_istream", ["get", "read"]) and
+ this.getNumberOfParameters() > 0
+ }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ // returns reference to `*this`
+ input.isQualifierAddress() and
+ output.isReturnValue()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to first parameter
+ input.isQualifierObject() and
+ output.isParameterDeref(0)
+ or
+ // reverse flow from returned reference to the qualifier
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The `std::istream` function `readsome`.
+ */
+class StdIStreamReadSome extends TaintFunction {
+ StdIStreamReadSome() { this.hasQualifiedName("std", "basic_istream", "readsome") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to first parameter
+ input.isQualifierObject() and
+ output.isParameterDeref(0)
+ }
+}
+
+/**
+ * The `std::istream` function `putback`.
+ */
+class StdIStreamPutBack extends DataFlowFunction, TaintFunction {
+ StdIStreamPutBack() { this.hasQualifiedName("std", "basic_istream", "putback") }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ // returns reference to `*this`
+ input.isQualifierAddress() and
+ output.isReturnValue()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from first parameter (value or pointer) to qualifier
+ input.isParameter(0) and
+ output.isQualifierObject()
+ or
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ or
+ // flow from first parameter (value or pointer) to return value
+ input.isParameter(0) and
+ output.isReturnValueDeref()
+ or
+ input.isParameterDeref(0) and
+ output.isReturnValueDeref()
+ or
+ // reverse flow from returned reference to the qualifier
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The `std::istream` function `getline`.
+ */
+class StdIStreamGetLine extends DataFlowFunction, TaintFunction {
+ StdIStreamGetLine() { this.hasQualifiedName("std", "basic_istream", "getline") }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ // returns reference to `*this`
+ input.isQualifierAddress() and
+ output.isReturnValue()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to first parameter
+ input.isQualifierObject() and
+ output.isParameterDeref(0)
+ or
+ // reverse flow from returned reference to the qualifier
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The (non-member) function `std::getline`.
+ */
+class StdGetLine extends DataFlowFunction, TaintFunction {
+ StdGetLine() { this.hasQualifiedName("std", "getline") }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ // flow from first parameter to return value
+ input.isParameter(0) and
+ output.isReturnValue()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from first parameter to second parameter
+ input.isParameterDeref(0) and
+ output.isParameterDeref(1)
+ or
+ // reverse flow from returned reference to first parameter
+ input.isReturnValueDeref() and
+ output.isParameterDeref(0)
+ }
+}
+
+/**
+ * The `std::basic_ostream` template class.
+ */
+class StdBasicOStream extends TemplateClass {
+ StdBasicOStream() { this.hasQualifiedName("std", "basic_ostream") }
+}
+
+/**
+ * The `std::ostream` functions `operator<<` (defined as a member function),
+ * `put` and `write`.
+ */
+class StdOStreamOut extends DataFlowFunction, TaintFunction {
+ StdOStreamOut() { this.hasQualifiedName("std", "basic_ostream", ["operator<<", "put", "write"]) }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ // returns reference to `*this`
+ input.isQualifierAddress() and
+ output.isReturnValue()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from first parameter (value or pointer) to qualifier
+ input.isParameter(0) and
+ output.isQualifierObject()
+ or
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ or
+ // flow from first parameter (value or pointer) to return value
+ input.isParameter(0) and
+ output.isReturnValueDeref()
+ or
+ input.isParameterDeref(0) and
+ output.isReturnValueDeref()
+ or
+ // reverse flow from returned reference to the qualifier
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * The `std::ostream` function `operator<<` (defined as a non-member function).
+ */
+class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction {
+ StdOStreamOutNonMember() {
+ this.hasQualifiedName("std", "operator<<") and
+ this.getUnspecifiedType().(ReferenceType).getBaseType() =
+ any(StdBasicOStream s).getAnInstantiation()
+ }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ // flow from first parameter to return value
+ input.isParameter(0) and
+ output.isReturnValue()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from second parameter to first parameter
+ input.isParameter(1) and
+ output.isParameterDeref(0)
+ or
+ // flow from second parameter to return value
+ input.isParameter(1) and
+ output.isReturnValueDeref()
+ or
+ // reverse flow from returned reference to the first parameter
+ input.isReturnValueDeref() and
+ output.isParameterDeref(0)
+ }
+}
+
+/**
+ * Additional model for `std::stringstream` constructors that take a string
+ * input parameter.
+ */
+class StdStringStreamConstructor extends Constructor, TaintFunction {
+ StdStringStreamConstructor() {
+ this.getDeclaringType().hasQualifiedName("std", "basic_stringstream")
+ }
+
+ /**
+ * Gets the index of a parameter to this function that is a string.
+ */
+ int getAStringParameterIndex() {
+ getParameter(result).getType() instanceof ReferenceType // `const std::basic_string &`
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // taint flow from any parameter of string type to the returned object
+ input.isParameterDeref(getAStringParameterIndex()) and
+ (
+ output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
+ or
+ output.isQualifierObject()
+ )
+ }
+}
+
+/**
+ * The `std::stringstream` function `str`.
+ */
+class StdStringStreamStr extends TaintFunction {
+ StdStringStreamStr() { this.hasQualifiedName("std", "basic_stringstream", "str") }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // flow from qualifier to return value (if any)
+ input.isQualifierObject() and
output.isReturnValue()
+ or
+ // flow from first parameter (if any) to qualifier
+ input.isParameterDeref(0) and
+ output.isQualifierObject()
+ }
+}
+
+/**
+ * A `std::` stream function that does not require a model, except that it
+ * returns a reference to `*this` and thus could be used in a chain.
+ */
+class StdStreamFunction extends DataFlowFunction, TaintFunction {
+ StdStreamFunction() {
+ this.hasQualifiedName("std", "basic_istream", ["ignore", "unget", "seekg"]) or
+ this.hasQualifiedName("std", "basic_ostream", ["seekp", "flush"]) or
+ this.hasQualifiedName("std", "basic_ios", "copyfmt")
+ }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ // returns reference to `*this`
+ input.isQualifierAddress() and
+ output.isReturnValue()
+ }
+
+ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // reverse flow from returned reference to the qualifier
+ input.isReturnValueDeref() and
+ output.isQualifierObject()
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll
index d44b28f041da..9acd5b32d4f0 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides implementation classes modeling `strcat` and various similar functions.
+ * See `semmle.code.cpp.models.Models` for usage information.
+ */
+
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
@@ -19,6 +24,21 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid
)
}
+ /**
+ * Gets the index of the parameter that is the size of the copy (in characters).
+ */
+ int getParamSize() { exists(getParameter(2)) and result = 2 }
+
+ /**
+ * Gets the index of the parameter that is the source of the copy.
+ */
+ int getParamSrc() { result = 1 }
+
+ /**
+ * Gets the index of the parameter that is the destination to be appended to.
+ */
+ int getParamDest() { result = 0 }
+
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
input.isParameter(0) and
output.isReturnValue()
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll
index c7f0898f3582..d0b3f92fa0fd 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides implementation classes modeling `strcpy` and various similar
+ * functions. See `semmle.code.cpp.models.Models` for usage information.
+ */
+
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
@@ -8,70 +13,91 @@ import semmle.code.cpp.models.interfaces.SideEffect
*/
class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, SideEffectFunction {
StrcpyFunction() {
- this.hasName("strcpy") or
- this.hasName("_mbscpy") or
- this.hasName("wcscpy") or
- this.hasName("strncpy") or
- this.hasName("_strncpy_l") or
- this.hasName("_mbsncpy") or
- this.hasName("_mbsncpy_l") or
- this.hasName("wcsncpy") or
- this.hasName("_wcsncpy_l")
+ getName() =
+ ["strcpy", // strcpy(dst, src)
+ "wcscpy", // wcscpy(dst, src)
+ "_mbscpy", // _mbscpy(dst, src)
+ "strncpy", // strncpy(dst, src, max_amount)
+ "_strncpy_l", // _strncpy_l(dst, src, max_amount, locale)
+ "wcsncpy", // wcsncpy(dst, src, max_amount)
+ "_wcsncpy_l", // _wcsncpy_l(dst, src, max_amount, locale)
+ "_mbsncpy", // _mbsncpy(dst, src, max_amount)
+ "_mbsncpy_l"] // _mbsncpy_l(dst, src, max_amount, locale)
+ or
+ getName() =
+ ["strcpy_s", // strcpy_s(dst, max_amount, src)
+ "wcscpy_s", // wcscpy_s(dst, max_amount, src)
+ "_mbscpy_s"] and // _mbscpy_s(dst, max_amount, src)
+ // exclude the 2-parameter template versions
+ // that find the size of a fixed size destination buffer.
+ getNumberOfParameters() = 3
}
- override predicate hasArrayInput(int bufParam) { bufParam = 1 }
+ /**
+ * Holds if this is one of the `strcpy_s` variants.
+ */
+ private predicate isSVariant() {
+ exists(string name | name = getName() | name.suffix(name.length() - 2) = "_s")
+ }
+
+ /**
+ * Gets the index of the parameter that is the maximum size of the copy (in characters).
+ */
+ int getParamSize() {
+ if isSVariant()
+ then result = 1
+ else
+ if exists(getName().indexOf("ncpy"))
+ then result = 2
+ else none()
+ }
- override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
+ /**
+ * Gets the index of the parameter that is the source of the copy.
+ */
+ int getParamSrc() { if isSVariant() then result = 2 else result = 1 }
- override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 1 }
+ /**
+ * Gets the index of the parameter that is the destination of the copy.
+ */
+ int getParamDest() { result = 0 }
+
+ override predicate hasArrayInput(int bufParam) { bufParam = getParamSrc() }
+
+ override predicate hasArrayOutput(int bufParam) { bufParam = getParamDest() }
+
+ override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = getParamSrc() }
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
- (
- this.hasName("strncpy") or
- this.hasName("_strncpy_l") or
- this.hasName("_mbsncpy") or
- this.hasName("_mbsncpy_l") or
- this.hasName("wcsncpy") or
- this.hasName("_wcsncpy_l")
- ) and
- bufParam = 0 and
- countParam = 2
+ bufParam = getParamDest() and
+ countParam = getParamSize()
}
override predicate hasArrayWithUnknownSize(int bufParam) {
- (
- this.hasName("strcpy") or
- this.hasName("_mbscpy") or
- this.hasName("wcscpy")
- ) and
- bufParam = 0
+ not exists(getParamSize()) and
+ bufParam = getParamDest()
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
- input.isParameterDeref(1) and
- output.isParameterDeref(0)
+ not exists(getParamSize()) and
+ input.isParameterDeref(getParamSrc()) and
+ output.isParameterDeref(getParamDest())
or
- input.isParameterDeref(1) and
+ not exists(getParamSize()) and
+ input.isParameterDeref(getParamSrc()) and
output.isReturnValueDeref()
or
- input.isParameter(0) and
+ input.isParameter(getParamDest()) and
output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
+ // these may do only a partial copy of the input buffer to the output
+ // buffer
+ exists(getParamSize()) and
+ input.isParameter(getParamSrc()) and
(
- // these may do only a partial copy of the input buffer to the output
- // buffer
- this.hasName("strncpy") or
- this.hasName("_strncpy_l") or
- this.hasName("_mbsncpy") or
- this.hasName("_mbsncpy_l") or
- this.hasName("wcsncpy") or
- this.hasName("_wcsncpy_l")
- ) and
- input.isParameter(2) and
- (
- output.isParameterDeref(0) or
+ output.isParameterDeref(getParamDest()) or
output.isReturnValueDeref()
)
}
@@ -81,17 +107,18 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
- i = 0 and
+ i = getParamDest() and
buffer = true and
mustWrite = false
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
- i = 1 and
+ i = getParamSrc() and
buffer = true
}
override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
- hasArrayWithVariableSize(i, result)
+ i = getParamDest() and
+ result = getParamSize()
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll
index f7d122db259b..3497ab9a0650 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strdup.qll
@@ -1,3 +1,8 @@
+/**
+ * Provides implementation classes modeling `strdup` and various similar
+ * functions. See `semmle.code.cpp.models.Models` for usage information.
+ */
+
import semmle.code.cpp.models.interfaces.Allocation
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll
index 81a40cd349af..1ebf40b1f014 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Allocation.qll
@@ -1,5 +1,5 @@
/**
- * Provides an abstract class for modelling functions and expressions that
+ * Provides an abstract class for modeling functions and expressions that
* allocate memory, such as the standard `malloc` function. To use this QL
* library, create one or more QL classes extending a class here with a
* characteristic predicate that selects the functions or expressions you are
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll
index 872cfcd29975..c1b65d627069 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/DataFlow.qll
@@ -19,5 +19,10 @@ import semmle.code.cpp.models.Models
* to destinations; that is covered by `TaintModel.qll`.
*/
abstract class DataFlowFunction extends Function {
+ /**
+ * Holds if data can be copied from the argument, qualifier, or buffer
+ * represented by `input` to the return value or buffer represented by
+ * `output`
+ */
abstract predicate hasDataFlow(FunctionInput input, FunctionOutput output);
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll
index 9223592ef678..23eca5164188 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Deallocation.qll
@@ -1,5 +1,5 @@
/**
- * Provides an abstract class for modelling functions and expressions that
+ * Provides an abstract class for modeling functions and expressions that
* deallocate memory, such as the standard `free` function. To use this QL
* library, create one or more QL classes extending a class here with a
* characteristic predicate that selects the functions or expressions you are
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll
index 7227e6e95133..f97646ca8330 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll
@@ -1,6 +1,6 @@
/**
* Provides a class for modeling `printf`-style formatting functions. To use
- * this QL library, create a QL class extending `DataFlowFunction` with a
+ * this QL library, create a QL class extending `FormattingFunction` with a
* characteristic predicate that selects the function or set of functions you
* are modeling. Within that class, override the predicates provided by
* `FormattingFunction` to match the flow within that function.
@@ -44,7 +44,7 @@ abstract class FormattingFunction extends ArrayFunction, TaintFunction {
/** Gets the position at which the format parameter occurs. */
abstract int getFormatParameterIndex();
- override string getCanonicalQLClass() { result = "FormattingFunction" }
+ override string getAPrimaryQlClass() { result = "FormattingFunction" }
/**
* Holds if this `FormattingFunction` is in a context that supports
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll
index d0fbb50ebfa8..9804cd6aff75 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll
@@ -10,7 +10,8 @@ private newtype TFunctionInput =
TInParameter(ParameterIndex i) or
TInParameterDeref(ParameterIndex i) or
TInQualifierObject() or
- TInQualifierAddress()
+ TInQualifierAddress() or
+ TInReturnValueDeref()
/**
* An input to a function. This can be:
@@ -106,8 +107,47 @@ class FunctionInput extends TFunctionInput {
* (with type `C const *`) on entry to the function.
*/
predicate isQualifierAddress() { none() }
+
+ /**
+ * Holds if this is the input value pointed to by the return value of a
+ * function, if the function returns a pointer, or the input value referred
+ * to by the return value of a function, if the function returns a reference.
+ *
+ * Example:
+ * ```
+ * char* getPointer();
+ * float& getReference();
+ * int getInt();
+ * ```
+ * - `isReturnValueDeref()` holds for the `FunctionInput` that represents the
+ * value of `*getPointer()` (with type `char`).
+ * - `isReturnValueDeref()` holds for the `FunctionInput` that represents the
+ * value of `getReference()` (with type `float`).
+ * - There is no `FunctionInput` of `getInt()` for which
+ * `isReturnValueDeref()` holds because the return type of `getInt()` is
+ * neither a pointer nor a reference.
+ *
+ * Note that data flows in through function return values are relatively
+ * rare, but they do occur when a function returns a reference to itself,
+ * part of itself, or one of its other inputs.
+ */
+ predicate isReturnValueDeref() { none() }
}
+/**
+ * The input value of a parameter.
+ *
+ * Example:
+ * ```
+ * void func(int n, char* p, float& r);
+ * ```
+ * - There is an `InParameter` representing the value of `n` (with type `int`) on entry to the
+ * function.
+ * - There is an `InParameter` representing the value of `p` (with type `char*`) on entry to the
+ * function.
+ * - There is an `InParameter` representing the "value" of the reference `r` (with type `float&`) on
+ * entry to the function, _not_ the value of the referred-to `float`.
+ */
class InParameter extends FunctionInput, TInParameter {
ParameterIndex index;
@@ -121,6 +161,21 @@ class InParameter extends FunctionInput, TInParameter {
override predicate isParameter(ParameterIndex i) { i = index }
}
+/**
+ * The input value pointed to by a pointer parameter to a function, or the input value referred to
+ * by a reference parameter to a function.
+ *
+ * Example:
+ * ```
+ * void func(int n, char* p, float& r);
+ * ```
+ * - There is an `InParameterDeref` with `getIndex() = 1` that represents the value of `*p` (with
+ * type `char`) on entry to the function.
+ * - There is an `InParameterDeref` with `getIndex() = 2` that represents the value of `r` (with
+ * type `float`) on entry to the function.
+ * - There is no `InParameterDeref` representing the value of `n`, because `n` is neither a pointer
+ * nor a reference.
+ */
class InParameterDeref extends FunctionInput, TInParameterDeref {
ParameterIndex index;
@@ -134,18 +189,70 @@ class InParameterDeref extends FunctionInput, TInParameterDeref {
override predicate isParameterDeref(ParameterIndex i) { i = index }
}
+/**
+ * The input value pointed to by the `this` pointer of an instance member function.
+ *
+ * Example:
+ * ```
+ * struct C {
+ * void mfunc(int n, char* p, float& r) const;
+ * };
+ * ```
+ * - `InQualifierObject` represents the value of `*this` (with type `C const`) on entry to the
+ * function.
+ */
class InQualifierObject extends FunctionInput, TInQualifierObject {
override string toString() { result = "InQualifierObject" }
override predicate isQualifierObject() { any() }
}
+/**
+ * The input value of the `this` pointer of an instance member function.
+ *
+ * Example:
+ * ```
+ * struct C {
+ * void mfunc(int n, char* p, float& r) const;
+ * };
+ * ```
+ * - `InQualifierAddress` represents the value of `this` (with type `C const *`) on entry to the
+ * function.
+ */
class InQualifierAddress extends FunctionInput, TInQualifierAddress {
override string toString() { result = "InQualifierAddress" }
override predicate isQualifierAddress() { any() }
}
+/**
+ * The input value pointed to by the return value of a function, if the
+ * function returns a pointer, or the input value referred to by the return
+ * value of a function, if the function returns a reference.
+ *
+ * Example:
+ * ```
+ * char* getPointer();
+ * float& getReference();
+ * int getInt();
+ * ```
+ * - `InReturnValueDeref` represents the value of `*getPointer()` (with type
+ * `char`).
+ * - `InReturnValueDeref` represents the value of `getReference()` (with type
+ * `float`).
+ * - `InReturnValueDeref` does not represent the return value of `getInt()`
+ * because the return type of `getInt()` is neither a pointer nor a reference.
+ *
+ * Note that data flows in through function return values are relatively
+ * rare, but they do occur when a function returns a reference to itself,
+ * part of itself, or one of its other inputs.
+ */
+class InReturnValueDeref extends FunctionInput, TInReturnValueDeref {
+ override string toString() { result = "InReturnValueDeref" }
+
+ override predicate isReturnValueDeref() { any() }
+}
+
private newtype TFunctionOutput =
TOutParameterDeref(ParameterIndex i) or
TOutQualifierObject() or
@@ -265,6 +372,21 @@ class FunctionOutput extends TFunctionOutput {
deprecated final predicate isOutReturnPointer() { isReturnValueDeref() }
}
+/**
+ * The output value pointed to by a pointer parameter to a function, or the output value referred to
+ * by a reference parameter to a function.
+ *
+ * Example:
+ * ```
+ * void func(int n, char* p, float& r);
+ * ```
+ * - There is an `OutParameterDeref` with `getIndex()=1` that represents the value of `*p` (with
+ * type `char`) on return from the function.
+ * - There is an `OutParameterDeref` with `getIndex()=2` that represents the value of `r` (with
+ * type `float`) on return from the function.
+ * - There is no `OutParameterDeref` representing the value of `n`, because `n` is neither a
+ * pointer nor a reference.
+ */
class OutParameterDeref extends FunctionOutput, TOutParameterDeref {
ParameterIndex index;
@@ -277,18 +399,62 @@ class OutParameterDeref extends FunctionOutput, TOutParameterDeref {
override predicate isParameterDeref(ParameterIndex i) { i = index }
}
+/**
+ * The output value pointed to by the `this` pointer of an instance member function.
+ *
+ * Example:
+ * ```
+ * struct C {
+ * void mfunc(int n, char* p, float& r);
+ * };
+ * ```
+ * - The `OutQualifierObject` represents the value of `*this` (with type `C`) on return from the
+ * function.
+ */
class OutQualifierObject extends FunctionOutput, TOutQualifierObject {
override string toString() { result = "OutQualifierObject" }
override predicate isQualifierObject() { any() }
}
+/**
+ * The value returned by a function.
+ *
+ * Example:
+ * ```
+ * int getInt();
+ * char* getPointer();
+ * float& getReference();
+ * ```
+ * - `OutReturnValue` represents the value returned by
+ * `getInt()` (with type `int`).
+ * - `OutReturnValue` represents the value returned by
+ * `getPointer()` (with type `char*`).
+ * - `OutReturnValue` represents the "value" of the reference returned by `getReference()` (with
+ * type `float&`), _not_ the value of the referred-to `float`.
+ */
class OutReturnValue extends FunctionOutput, TOutReturnValue {
override string toString() { result = "OutReturnValue" }
override predicate isReturnValue() { any() }
}
+/**
+ * The output value pointed to by the return value of a function, if the function returns a pointer,
+ * or the output value referred to by the return value of a function, if the function returns a
+ * reference.
+ *
+ * Example:
+ * ```
+ * char* getPointer();
+ * float& getReference();
+ * int getInt();
+ * ```
+ * - `OutReturnValueDeref` represents the value of `*getPointer()` (with type `char`).
+ * - `OutReturnValueDeref` represents the value of `getReference()` (with type `float`).
+ * - `OutReturnValueDeref` does not represent the return value of `getInt()` because the return type
+ * of `getInt()` is neither a pointer nor a reference.
+ */
class OutReturnValueDeref extends FunctionOutput, TOutReturnValueDeref {
override string toString() { result = "OutReturnValueDeref" }
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll
new file mode 100644
index 000000000000..de6977765253
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Iterator.qll
@@ -0,0 +1,17 @@
+/**
+ * Provides an abstract class for accurate modeling of flow through output
+ * iterators. To use this QL library, create a QL class extending
+ * `IteratorReferenceFunction` with a characteristic predicate that selects the
+ * function or set of functions you are modeling. Within that class, override
+ * the predicates provided by `AliasFunction` to match the flow within that
+ * function.
+ */
+
+import cpp
+import semmle.code.cpp.models.Models
+
+/**
+ * A function which takes an iterator argument and returns a reference that
+ * can be used to write to the iterator's underlying collection.
+ */
+abstract class IteratorReferenceFunction extends Function { }
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll
index c619f2efaa5c..fe617533f59d 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/Taint.qll
@@ -24,5 +24,9 @@ import semmle.code.cpp.models.Models
* data flow.
*/
abstract class TaintFunction extends Function {
+ /**
+ * Holds if data passed into the argument, qualifier, or buffer represented by
+ * `input` influences the return value or buffer represented by `output`
+ */
abstract predicate hasTaintFlow(FunctionInput input, FunctionOutput output);
}
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll
deleted file mode 100644
index 9e56794233fe..000000000000
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/Bound.qll
+++ /dev/null
@@ -1,82 +0,0 @@
-import cpp
-private import semmle.code.cpp.ir.IR
-private import semmle.code.cpp.ir.ValueNumbering
-
-private newtype TBound =
- TBoundZero() or
- TBoundValueNumber(ValueNumber vn) {
- exists(Instruction i |
- vn.getAnInstruction() = i and
- (
- i.getResultType() instanceof IntegralType or
- i.getResultType() instanceof PointerType
- ) and
- not vn.getAnInstruction() instanceof ConstantInstruction
- |
- i instanceof PhiInstruction
- or
- i instanceof InitializeParameterInstruction
- or
- i instanceof CallInstruction
- or
- i instanceof VariableAddressInstruction
- or
- i instanceof FieldAddressInstruction
- or
- i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction
- or
- i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction
- or
- i.getAUse() instanceof ArgumentOperand
- )
- }
-
-/**
- * A bound that may be inferred for an expression plus/minus an integer delta.
- */
-abstract class Bound extends TBound {
- abstract string toString();
-
- /** Gets an expression that equals this bound plus `delta`. */
- abstract Instruction getInstruction(int delta);
-
- /** Gets an expression that equals this bound. */
- Instruction getInstruction() { result = getInstruction(0) }
-
- abstract Location getLocation();
-}
-
-/**
- * The bound that corresponds to the integer 0. This is used to represent all
- * integer bounds as bounds are always accompanied by an added integer delta.
- */
-class ZeroBound extends Bound, TBoundZero {
- override string toString() { result = "0" }
-
- override Instruction getInstruction(int delta) {
- result.(ConstantValueInstruction).getValue().toInt() = delta
- }
-
- override Location getLocation() { result instanceof UnknownDefaultLocation }
-}
-
-/**
- * A bound corresponding to the value of an `Instruction`.
- */
-class ValueNumberBound extends Bound, TBoundValueNumber {
- ValueNumber vn;
-
- ValueNumberBound() { this = TBoundValueNumber(vn) }
-
- /** Gets an `Instruction` that equals this bound. */
- override Instruction getInstruction(int delta) {
- this = TBoundValueNumber(valueNumber(result)) and delta = 0
- }
-
- override string toString() { result = vn.getExampleInstruction().toString() }
-
- override Location getLocation() { result = vn.getLocation() }
-
- /** Gets the value number that equals this bound. */
- ValueNumber getValueNumber() { result = vn }
-}
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll
index ea12434ac5bd..e402b672cbc2 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides classes and predicates for recognizing floating point expressions which cannot be NaN.
+ */
+
import cpp
private import semmle.code.cpp.rangeanalysis.RangeSSA
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll
index 3c9177ad9db5..47289c7552b9 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/PointlessComparison.qll
@@ -11,8 +11,17 @@ private float lowerBoundFC(Expr expr) { result = lowerBound(expr.getFullyConvert
/** Gets the upper bound of the fully converted expression. */
private float upperBoundFC(Expr expr) { result = upperBound(expr.getFullyConverted()) }
+/**
+ * Describes which side of a pointless comparison is known to be smaller.
+ */
newtype SmallSide =
+ /**
+ * Represents that the left side of a pointless comparison is known to be smaller.
+ */
LeftIsSmaller() or
+ /**
+ * Represents that the right side of a pointless comparison is known to be smaller.
+ */
RightIsSmaller()
/**
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll
deleted file mode 100644
index a79df3c37d75..000000000000
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll
+++ /dev/null
@@ -1,624 +0,0 @@
-/**
- * Provides classes and predicates for range analysis.
- *
- * An inferred bound can either be a specific integer or a `ValueNumber`
- * representing the abstract value of a set of `Instruction`s.
- *
- * If an inferred bound relies directly on a condition, then this condition is
- * reported as the reason for the bound.
- */
-
-/*
- * This library tackles range analysis as a flow problem. Consider e.g.:
- * ```
- * len = arr.length;
- * if (x < len) { ... y = x-1; ... y ... }
- * ```
- * In this case we would like to infer `y <= arr.length - 2`, and this is
- * accomplished by tracking the bound through a sequence of steps:
- * ```
- * arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y
- * ```
- *
- * In its simplest form the step relation `I1 --> I2` relates two `Instruction`s
- * such that `I1 <= B` implies `I2 <= B` for any `B` (with a second separate
- * step relation handling lower bounds). Examples of such steps include
- * assignments `I2 = I1` and conditions `x <= I1` where `I2` is a use of `x`
- * guarded by the condition.
- *
- * In order to handle subtractions and additions with constants, and strict
- * comparisons, the step relation is augmented with an integer delta. With this
- * generalization `I1 --(delta)--> I2` relates two `Instruction`s and an integer
- * such that `I1 <= B` implies `I2 <= B + delta` for any `B`. This corresponds
- * to the predicate `boundFlowStep`.
- *
- * The complete range analysis is then implemented as the transitive closure of
- * the step relation summing the deltas along the way. If `I1` transitively
- * steps to `I2`, `delta` is the sum of deltas along the path, and `B` is an
- * interesting bound equal to the value of `I1` then `I2 <= B + delta`. This
- * corresponds to the predicate `boundedInstruction`.
- *
- * Bounds come in two forms: either they are relative to zero (and thus provide
- * a constant bound), or they are relative to some program value. This value is
- * represented by the `ValueNumber` class, each instance of which represents a
- * set of `Instructions` that must have the same value.
- *
- * Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`.
- * There are essentially two cases:
- * - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`.
- * - If `x1 <= B + d1` and `x2 <= x0 + d2` with `d2 <= 0` then `x0 <= B + d1`.
- * The first case is for whenever a bound can be proven without taking looping
- * into account. The second case is relevant when `x2` comes from a back-edge
- * where we can prove that the variable has been non-increasing through the
- * loop-iteration as this means that any upper bound that holds prior to the
- * loop also holds for the variable during the loop.
- * This generalizes to a phi node with `n` inputs, so if
- * `x0 = phi(x1, ..., xn)` and `xi <= B + delta` for one of the inputs, then we
- * also have `x0 <= B + delta` if we can prove either:
- * - `xj <= B + d` with `d <= delta` or
- * - `xj <= x0 + d` with `d <= 0`
- * for each input `xj`.
- *
- * As all inferred bounds can be related directly to a path in the source code
- * the only source of non-termination is if successive redundant (and thereby
- * increasingly worse) bounds are calculated along a loop in the source code.
- * We prevent this by weakening the bound to a small finite set of bounds when
- * a path follows a second back-edge (we postpone weakening till the second
- * back-edge as a precise bound might require traversing a loop once).
- */
-
-import cpp
-private import semmle.code.cpp.ir.IR
-private import semmle.code.cpp.controlflow.IRGuards
-private import semmle.code.cpp.ir.ValueNumbering
-private import RangeUtils
-private import SignAnalysis
-import Bound
-
-cached
-private module RangeAnalysisCache {
- cached
- module RangeAnalysisPublic {
- /**
- * Holds if `b + delta` is a valid bound for `i` and this is the best such delta.
- * - `upper = true` : `i <= b + delta`
- * - `upper = false` : `i >= b + delta`
- *
- * The reason for the bound is given by `reason` and may be either a condition
- * or `NoReason` if the bound was proven directly without the use of a bounding
- * condition.
- */
- cached
- predicate boundedInstruction(Instruction i, Bound b, int delta, boolean upper, Reason reason) {
- boundedInstruction(i, b, delta, upper, _, _, reason) and
- bestInstructionBound(i, b, delta, upper)
- }
-
- /**
- * Holds if `b + delta` is a valid bound for `op` and this is the best such delta.
- * - `upper = true` : `op <= b + delta`
- * - `upper = false` : `op >= b + delta`
- *
- * The reason for the bound is given by `reason` and may be either a condition
- * or `NoReason` if the bound was proven directly without the use of a bounding
- * condition.
- */
- cached
- predicate boundedOperand(Operand op, Bound b, int delta, boolean upper, Reason reason) {
- boundedOperandCand(op, b, delta, upper, reason) and
- bestOperandBound(op, b, delta, upper)
- }
- }
-
- /**
- * Holds if `guard = boundFlowCond(_, _, _, _, _) or guard = eqFlowCond(_, _, _, _, _)`.
- */
- cached
- predicate possibleReason(IRGuardCondition guard) {
- guard = boundFlowCond(_, _, _, _, _)
- or
- guard = eqFlowCond(_, _, _, _, _)
- }
-}
-
-private import RangeAnalysisCache
-import RangeAnalysisPublic
-
-/**
- * Holds if `b + delta` is a valid bound for `e` and this is the best such delta.
- * - `upper = true` : `e <= b + delta`
- * - `upper = false` : `e >= b + delta`
- */
-private predicate bestInstructionBound(Instruction i, Bound b, int delta, boolean upper) {
- delta = min(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = true
- or
- delta = max(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = false
-}
-
-/**
- * Holds if `b + delta` is a valid bound for `op`.
- * - `upper = true` : `op <= b + delta`
- * - `upper = false` : `op >= b + delta`
- *
- * The reason for the bound is given by `reason` and may be either a condition
- * or `NoReason` if the bound was proven directly without the use of a bounding
- * condition.
- */
-private predicate boundedOperandCand(Operand op, Bound b, int delta, boolean upper, Reason reason) {
- boundedNonPhiOperand(op, b, delta, upper, _, _, reason)
- or
- boundedPhiOperand(op, b, delta, upper, _, _, reason)
-}
-
-/**
- * Holds if `b + delta` is a valid bound for `op` and this is the best such delta.
- * - `upper = true` : `op <= b + delta`
- * - `upper = false` : `op >= b + delta`
- */
-private predicate bestOperandBound(Operand op, Bound b, int delta, boolean upper) {
- delta = min(int d | boundedOperandCand(op, b, d, upper, _)) and upper = true
- or
- delta = max(int d | boundedOperandCand(op, b, d, upper, _)) and upper = false
-}
-
-/**
- * Gets a condition that tests whether `vn` equals `bound + delta`.
- *
- * If the condition evaluates to `testIsTrue`:
- * - `isEq = true` : `vn == bound + delta`
- * - `isEq = false` : `vn != bound + delta`
- */
-private IRGuardCondition eqFlowCond(
- ValueNumber vn, Operand bound, int delta, boolean isEq, boolean testIsTrue
-) {
- result.comparesEq(vn.getAUse(), bound, delta, isEq, testIsTrue)
-}
-
-/**
- * Holds if `op1 + delta` is a valid bound for `op2`.
- * - `upper = true` : `op2 <= op1 + delta`
- * - `upper = false` : `op2 >= op1 + delta`
- */
-private predicate boundFlowStepSsa(
- NonPhiOperand op2, Operand op1, int delta, boolean upper, Reason reason
-) {
- exists(IRGuardCondition guard, boolean testIsTrue |
- guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and
- guard.controls(op2.getUse().getBlock(), testIsTrue) and
- reason = TCondReason(guard)
- )
- or
- exists(IRGuardCondition guard, boolean testIsTrue, SafeCastInstruction cast |
- valueNumberOfOperand(op2) = valueNumber(cast.getUnary()) and
- guard = boundFlowCond(valueNumber(cast), op1, delta, upper, testIsTrue) and
- guard.controls(op2.getUse().getBlock(), testIsTrue) and
- reason = TCondReason(guard)
- )
-}
-
-/**
- * Gets a condition that tests whether `vn` is bounded by `bound + delta`.
- *
- * If the condition evaluates to `testIsTrue`:
- * - `upper = true` : `vn <= bound + delta`
- * - `upper = false` : `vn >= bound + delta`
- */
-private IRGuardCondition boundFlowCond(
- ValueNumber vn, NonPhiOperand bound, int delta, boolean upper, boolean testIsTrue
-) {
- exists(int d |
- result.comparesLt(vn.getAUse(), bound, d, upper, testIsTrue) and
- // `comparesLt` provides bounds of the form `x < y + k` or `x >= y + k`, but we need
- // `x <= y + k` so we strengthen here. `testIsTrue` has the same semantics in `comparesLt` as
- // it does here, so we don't need to account for it.
- if upper = true then delta = d - 1 else delta = d
- )
- or
- result = eqFlowCond(vn, bound, delta, true, testIsTrue) and
- (upper = true or upper = false)
-}
-
-private newtype TReason =
- TNoReason() or
- TCondReason(IRGuardCondition guard) { possibleReason(guard) }
-
-/**
- * A reason for an inferred bound. This can either be `CondReason` if the bound
- * is due to a specific condition, or `NoReason` if the bound is inferred
- * without going through a bounding condition.
- */
-abstract class Reason extends TReason {
- abstract string toString();
-}
-
-class NoReason extends Reason, TNoReason {
- override string toString() { result = "NoReason" }
-}
-
-class CondReason extends Reason, TCondReason {
- IRGuardCondition getCond() { this = TCondReason(result) }
-
- override string toString() { result = getCond().toString() }
-}
-
-/**
- * Holds if `typ` is a small integral type with the given lower and upper bounds.
- */
-private predicate typeBound(IntegralType typ, int lowerbound, int upperbound) {
- typ.isSigned() and typ.getSize() = 1 and lowerbound = -128 and upperbound = 127
- or
- typ.isUnsigned() and typ.getSize() = 1 and lowerbound = 0 and upperbound = 255
- or
- typ.isSigned() and typ.getSize() = 2 and lowerbound = -32768 and upperbound = 32767
- or
- typ.isUnsigned() and typ.getSize() = 2 and lowerbound = 0 and upperbound = 65535
-}
-
-/**
- * A cast to a small integral type that may overflow or underflow.
- */
-private class NarrowingCastInstruction extends ConvertInstruction {
- NarrowingCastInstruction() {
- not this instanceof SafeCastInstruction and
- typeBound(getResultType(), _, _)
- }
-
- /** Gets the lower bound of the resulting type. */
- int getLowerBound() { typeBound(getResultType(), result, _) }
-
- /** Gets the upper bound of the resulting type. */
- int getUpperBound() { typeBound(getResultType(), _, result) }
-}
-
-/**
- * Holds if `op + delta` is a valid bound for `i`.
- * - `upper = true` : `i <= op + delta`
- * - `upper = false` : `i >= op + delta`
- */
-private predicate boundFlowStep(Instruction i, NonPhiOperand op, int delta, boolean upper) {
- valueFlowStep(i, op, delta) and
- (upper = true or upper = false)
- or
- i.(SafeCastInstruction).getAnOperand() = op and
- delta = 0 and
- (upper = true or upper = false)
- or
- exists(Operand x |
- i.(AddInstruction).getAnOperand() = op and
- i.(AddInstruction).getAnOperand() = x and
- op != x
- |
- not exists(getValue(getConstantValue(op.getUse()))) and
- not exists(getValue(getConstantValue(x.getUse()))) and
- if strictlyPositive(x)
- then upper = false and delta = 1
- else
- if positive(x)
- then upper = false and delta = 0
- else
- if strictlyNegative(x)
- then upper = true and delta = -1
- else
- if negative(x)
- then upper = true and delta = 0
- else none()
- )
- or
- exists(Operand x |
- exists(SubInstruction sub |
- i = sub and
- sub.getLeftOperand() = op and
- sub.getRightOperand() = x
- )
- |
- // `x` with constant value is covered by valueFlowStep
- not exists(getValue(getConstantValue(x.getUse()))) and
- if strictlyPositive(x)
- then upper = true and delta = -1
- else
- if positive(x)
- then upper = true and delta = 0
- else
- if strictlyNegative(x)
- then upper = false and delta = 1
- else
- if negative(x)
- then upper = false and delta = 0
- else none()
- )
- or
- i.(RemInstruction).getRightOperand() = op and positive(op) and delta = -1 and upper = true
- or
- i.(RemInstruction).getLeftOperand() = op and positive(op) and delta = 0 and upper = true
- or
- i.(BitAndInstruction).getAnOperand() = op and positive(op) and delta = 0 and upper = true
- or
- i.(BitOrInstruction).getAnOperand() = op and
- positiveInstruction(i) and
- delta = 0 and
- upper = false
- // TODO: min, max, rand
-}
-
-private predicate boundFlowStepMul(Instruction i1, Operand op, int factor) {
- exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 |
- i1.(MulInstruction).hasOperands(op, c.getAUse()) and factor = k
- or
- exists(ShiftLeftInstruction i |
- i = i1 and i.getLeftOperand() = op and i.getRightOperand() = c.getAUse() and factor = 2.pow(k)
- )
- )
-}
-
-private predicate boundFlowStepDiv(Instruction i1, Operand op, int factor) {
- exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 |
- exists(DivInstruction i |
- i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = k
- )
- or
- exists(ShiftRightInstruction i |
- i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = 2.pow(k)
- )
- )
-}
-
-/**
- * Holds if `b` is a valid bound for `op`
- */
-pragma[noinline]
-private predicate boundedNonPhiOperand(
- NonPhiOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
- Reason reason
-) {
- exists(NonPhiOperand op2, int d1, int d2 |
- boundFlowStepSsa(op, op2, d1, upper, reason) and
- boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, _) and
- delta = d1 + d2
- )
- or
- boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason)
- or
- exists(int d, Reason r1, Reason r2 |
- boundedNonPhiOperand(op, b, d, upper, fromBackEdge, origdelta, r2)
- |
- unequalOperand(op, b, d, r1) and
- (
- upper = true and delta = d - 1
- or
- upper = false and delta = d + 1
- ) and
- (
- reason = r1
- or
- reason = r2 and not r2 instanceof NoReason
- )
- )
-}
-
-/**
- * Holds if `op1 + delta` is a valid bound for `op2`.
- * - `upper = true` : `op2 <= op1 + delta`
- * - `upper = false` : `op2 >= op1 + delta`
- */
-private predicate boundFlowStepPhi(
- PhiInputOperand op2, Operand op1, int delta, boolean upper, Reason reason
-) {
- op2.getDef().(CopyInstruction).getSourceValueOperand() = op1 and
- (upper = true or upper = false) and
- reason = TNoReason() and
- delta = 0
- or
- exists(IRGuardCondition guard, boolean testIsTrue |
- guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and
- guard.controlsEdge(op2.getPredecessorBlock(), op2.getUse().getBlock(), testIsTrue) and
- reason = TCondReason(guard)
- )
-}
-
-private predicate boundedPhiOperand(
- PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
- Reason reason
-) {
- exists(NonPhiOperand op2, int d1, int d2, Reason r1, Reason r2 |
- boundFlowStepPhi(op, op2, d1, upper, r1) and
- boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, r2) and
- delta = d1 + d2 and
- (if r1 instanceof NoReason then reason = r2 else reason = r1)
- )
- or
- boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason)
- or
- exists(int d, Reason r1, Reason r2 |
- boundedInstruction(op.getDef(), b, d, upper, fromBackEdge, origdelta, r2)
- |
- unequalOperand(op, b, d, r1) and
- (
- upper = true and delta = d - 1
- or
- upper = false and delta = d + 1
- ) and
- (
- reason = r1
- or
- reason = r2 and not r2 instanceof NoReason
- )
- )
-}
-
-/** Holds if `op2 != op1 + delta` at `pos`. */
-private predicate unequalFlowStep(Operand op2, Operand op1, int delta, Reason reason) {
- exists(IRGuardCondition guard, boolean testIsTrue |
- guard = eqFlowCond(valueNumberOfOperand(op2), op1, delta, false, testIsTrue) and
- guard.controls(op2.getUse().getBlock(), testIsTrue) and
- reason = TCondReason(guard)
- )
-}
-
-/**
- * Holds if `op != b + delta` at `pos`.
- */
-private predicate unequalOperand(Operand op, Bound b, int delta, Reason reason) {
- exists(Operand op2, int d1, int d2 |
- unequalFlowStep(op, op2, d1, reason) and
- boundedNonPhiOperand(op2, b, d2, true, _, _, _) and
- boundedNonPhiOperand(op2, b, d2, false, _, _, _) and
- delta = d1 + d2
- )
-}
-
-private predicate boundedPhiCandValidForEdge(
- PhiInstruction phi, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
- Reason reason, PhiInputOperand op
-) {
- boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and
- (
- exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = true and d <= delta)
- or
- exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = false and d >= delta)
- or
- selfBoundedPhiInp(phi, op, upper)
- )
-}
-
-/** Weakens a delta to lie in the range `[-1..1]`. */
-bindingset[delta, upper]
-private int weakenDelta(boolean upper, int delta) {
- delta in [-1 .. 1] and result = delta
- or
- upper = true and result = -1 and delta < -1
- or
- upper = false and result = 1 and delta > 1
-}
-
-private predicate boundedPhiInp(
- PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge,
- int origdelta, Reason reason
-) {
- phi.getAnOperand() = op and
- exists(int d, boolean fromBackEdge0 |
- boundedPhiOperand(op, b, d, upper, fromBackEdge0, origdelta, reason)
- or
- b.(ValueNumberBound).getInstruction() = op.getDef() and
- d = 0 and
- (upper = true or upper = false) and
- fromBackEdge0 = false and
- origdelta = 0 and
- reason = TNoReason()
- |
- if backEdge(phi, op)
- then
- fromBackEdge = true and
- (
- fromBackEdge0 = true and delta = weakenDelta(upper, d - origdelta) + origdelta
- or
- fromBackEdge0 = false and delta = d
- )
- else (
- delta = d and fromBackEdge = fromBackEdge0
- )
- )
-}
-
-pragma[noinline]
-private predicate boundedPhiInp1(
- PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper
-) {
- boundedPhiInp(phi, op, b, delta, upper, _, _, _)
-}
-
-private predicate selfBoundedPhiInp(PhiInstruction phi, PhiInputOperand op, boolean upper) {
- exists(int d, ValueNumberBound phibound |
- phibound.getInstruction() = phi and
- boundedPhiInp(phi, op, phibound, d, upper, _, _, _) and
- (
- upper = true and d <= 0
- or
- upper = false and d >= 0
- )
- )
-}
-
-pragma[noinline]
-private predicate boundedPhiCand(
- PhiInstruction phi, boolean upper, Bound b, int delta, boolean fromBackEdge, int origdelta,
- Reason reason
-) {
- exists(PhiInputOperand op |
- boundedPhiInp(phi, op, b, delta, upper, fromBackEdge, origdelta, reason)
- )
-}
-
-/**
- * Holds if the value being cast has an upper (for `upper = true`) or lower
- * (for `upper = false`) bound within the bounds of the resulting type.
- * For `upper = true` this means that the cast will not overflow and for
- * `upper = false` this means that the cast will not underflow.
- */
-private predicate safeNarrowingCast(NarrowingCastInstruction cast, boolean upper) {
- exists(int bound |
- boundedNonPhiOperand(cast.getAnOperand(), any(ZeroBound zb), bound, upper, _, _, _)
- |
- upper = true and bound <= cast.getUpperBound()
- or
- upper = false and bound >= cast.getLowerBound()
- )
-}
-
-pragma[noinline]
-private predicate boundedCastExpr(
- NarrowingCastInstruction cast, Bound b, int delta, boolean upper, boolean fromBackEdge,
- int origdelta, Reason reason
-) {
- boundedNonPhiOperand(cast.getAnOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
-}
-
-/**
- * Holds if `b + delta` is a valid bound for `i`.
- * - `upper = true` : `i <= b + delta`
- * - `upper = false` : `i >= b + delta`
- */
-private predicate boundedInstruction(
- Instruction i, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
- Reason reason
-) {
- i instanceof PhiInstruction and
- forex(PhiInputOperand op | op = i.getAnOperand() |
- boundedPhiCandValidForEdge(i, b, delta, upper, fromBackEdge, origdelta, reason, op)
- )
- or
- i = b.getInstruction(delta) and
- (upper = true or upper = false) and
- fromBackEdge = false and
- origdelta = delta and
- reason = TNoReason()
- or
- exists(Operand mid, int d1, int d2 |
- boundFlowStep(i, mid, d1, upper) and
- boundedNonPhiOperand(mid, b, d2, upper, fromBackEdge, origdelta, reason) and
- delta = d1 + d2 and
- not exists(getValue(getConstantValue(i)))
- )
- or
- exists(Operand mid, int factor, int d |
- boundFlowStepMul(i, mid, factor) and
- boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and
- b instanceof ZeroBound and
- delta = d * factor and
- not exists(getValue(getConstantValue(i)))
- )
- or
- exists(Operand mid, int factor, int d |
- boundFlowStepDiv(i, mid, factor) and
- boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and
- d >= 0 and
- b instanceof ZeroBound and
- delta = d / factor and
- not exists(getValue(getConstantValue(i)))
- )
- or
- exists(NarrowingCastInstruction cast |
- cast = i and
- safeNarrowingCast(cast, upper.booleanNot()) and
- boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason)
- )
-}
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll
index 9e99fcb82041..410a39716dc3 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysisUtils.qll
@@ -5,7 +5,13 @@ import cpp
* relation) or 'non-strict' (a `<=` or `>=` relation).
*/
newtype RelationStrictness =
+ /**
+ * Represents that a relation is 'strict' (that is, a `<` or `>` relation).
+ */
Strict() or
+ /**
+ * Represents that a relation is 'non-strict' (that is, a `<=` or `>=` relation)
+ */
Nonstrict()
/**
@@ -13,7 +19,13 @@ newtype RelationStrictness =
* relation) or 'lesser' (a `<` or `<=` relation).
*/
newtype RelationDirection =
+ /**
+ * Represents that a relation is 'greater' (that is, a `>` or `>=` relation).
+ */
Greater() or
+ /**
+ * Represents that a relation is 'lesser' (that is, a `<` or `<=` relation).
+ */
Lesser()
private RelationStrictness negateStrictness(RelationStrictness strict) {
@@ -28,12 +40,18 @@ private RelationDirection negateDirection(RelationDirection dir) {
dir = Lesser() and result = Greater()
}
+/**
+ * Holds if `dir` is `Greater` (that is, a `>` or `>=` relation)
+ */
boolean directionIsGreater(RelationDirection dir) {
dir = Greater() and result = true
or
dir = Lesser() and result = false
}
+/**
+ * Holds if `dir` is `Lesser` (that is, a `<` or `<=` relation)
+ */
boolean directionIsLesser(RelationDirection dir) {
dir = Greater() and result = false
or
@@ -153,6 +171,65 @@ predicate eqOpWithSwapAndNegate(EqualityOperation cmp, Expr a, Expr b, boolean i
eqOpWithSwap(cmp, a, b, branch.booleanNot()) and isEQ = false
}
+/**
+ * Holds if `cmp` is an unconverted conversion of `a` to a Boolean that
+ * evalutes to `isEQ` iff `a` is 0.
+ *
+ * Note that `a` can be `cmp` itself or a conversion thereof.
+ */
+private predicate eqZero(Expr cmp, Expr a, boolean isEQ) {
+ // The `!a` expression tests `a` equal to zero when `a` is a number converted
+ // to a Boolean.
+ isEQ = true and
+ exists(Expr notOperand | notOperand = cmp.(NotExpr).getOperand().getFullyConverted() |
+ // In C++ code there will be a BoolConversion in `!myInt`
+ a = notOperand.(BoolConversion).getExpr()
+ or
+ // In C code there is no conversion since there was no bool type before C99
+ a = notOperand and
+ not a instanceof BoolConversion // avoid overlap with the case above
+ )
+ or
+ // The `(bool)a` expression tests `a` NOT equal to zero when `a` is a number
+ // converted to a Boolean. To avoid overlap with the case above, this case
+ // excludes conversions that are right below a `!`.
+ isEQ = false and
+ linearAccess(cmp, _, _, _) and
+ // This test for `isCondition` implies that `cmp` is unconverted and that the
+ // parent of `cfg` is not a `NotExpr` -- the CFG doesn't do branching from
+ // inside `NotExpr`.
+ cmp.isCondition() and
+ // The GNU two-operand conditional expression is not supported for the
+ // purpose of guards, but the value of the conditional expression itself is
+ // modeled in the range analysis.
+ not exists(ConditionalExpr cond | cmp = cond.getCondition() and cond.isTwoOperand()) and
+ (
+ // In C++ code there will be a BoolConversion in `if (myInt)`
+ a = cmp.getFullyConverted().(BoolConversion).getExpr()
+ or
+ // In C code there is no conversion since there was no bool type before C99
+ a = cmp.getFullyConverted() and
+ not a instanceof BoolConversion // avoid overlap with the case above
+ )
+}
+
+/**
+ * Holds if `branch` of `cmp` is taken when `a` compares `isEQ` to zero.
+ *
+ * Note that `a` can be `cmp` itself or a conversion thereof.
+ */
+predicate eqZeroWithNegate(Expr cmp, Expr a, boolean isEQ, boolean branch) {
+ // The comparison for _equality_ to zero is on the `true` branch when `cmp`
+ // compares equal to zero and on the `false` branch when `cmp` compares not
+ // equal to zero.
+ eqZero(cmp, a, branch) and isEQ = true
+ or
+ // The comparison for _inequality_ to zero is on the `false` branch when
+ // `cmp` compares equal to zero and on the `true` branch when `cmp` compares
+ // not equal to zero.
+ eqZero(cmp, a, branch.booleanNot()) and isEQ = false
+}
+
/**
* Holds if `expr` is equivalent to `p*v + q`, where `p` is a non-zero
* number. This takes into account the associativity, commutativity and
@@ -173,6 +250,8 @@ private predicate linearAccessImpl(Expr expr, VariableAccess v, float p, float q
// Base case
expr = v and p = 1.0 and q = 0.0
or
+ expr.(ReferenceDereferenceExpr).getExpr() = v and p = 1.0 and q = 0.0
+ or
// a+(p*v+b) == p*v + (a+b)
exists(AddExpr addExpr, float a, float b |
addExpr.getLeftOperand().isConstant() and
@@ -331,13 +410,20 @@ private predicate typeBounds(ArithmeticType t, float lb, float ub) {
t instanceof FloatingPointType and lb = -(1.0 / 0.0) and ub = 1.0 / 0.0
}
+private Type stripReference(Type t) {
+ if t instanceof ReferenceType then result = t.(ReferenceType).getBaseType() else result = t
+}
+
+/** Gets the type used by range analysis for the given `StackVariable`. */
+Type getVariableRangeType(StackVariable v) { result = stripReference(v.getUnspecifiedType()) }
+
/**
* Gets the lower bound for the unspecified type `t`.
*
* For example, if `t` is a signed 32-bit type then the result is
* `-2^31`.
*/
-float typeLowerBound(ArithmeticType t) { typeBounds(t, result, _) }
+float typeLowerBound(Type t) { typeBounds(stripReference(t), result, _) }
/**
* Gets the upper bound for the unspecified type `t`.
@@ -345,7 +431,7 @@ float typeLowerBound(ArithmeticType t) { typeBounds(t, result, _) }
* For example, if `t` is a signed 32-bit type then the result is
* `2^31 - 1`.
*/
-float typeUpperBound(ArithmeticType t) { typeBounds(t, _, result) }
+float typeUpperBound(Type t) { typeBounds(stripReference(t), _, result) }
/**
* Gets the minimum value that this expression could represent, based on
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll
index 99e6539658d7..93dcf989590b 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll
@@ -25,6 +25,10 @@ import semmle.code.cpp.controlflow.Dominance
import semmle.code.cpp.controlflow.SSAUtils
private import RangeAnalysisUtils
+/**
+ * The SSA logic comes in two versions: the standard SSA and range-analysis RangeSSA.
+ * This class provides the range-analysis SSA logic.
+ */
library class RangeSSA extends SSAHelper {
RangeSSA() { this = 1 }
@@ -36,21 +40,20 @@ library class RangeSSA extends SSAHelper {
}
}
-private predicate guard_defn(
- VariableAccess v, ComparisonOperation guard, BasicBlock b, boolean branch
-) {
+private predicate guard_defn(VariableAccess v, Expr guard, BasicBlock b, boolean branch) {
guardCondition(guard, v, branch) and
guardSuccessor(guard, branch, b)
}
-private predicate guardCondition(ComparisonOperation guard, VariableAccess v, boolean branch) {
+private predicate guardCondition(Expr guard, VariableAccess v, boolean branch) {
exists(Expr lhs | linearAccess(lhs, v, _, _) |
relOpWithSwapAndNegate(guard, lhs, _, _, _, branch) or
- eqOpWithSwapAndNegate(guard, lhs, _, _, branch)
+ eqOpWithSwapAndNegate(guard, lhs, _, _, branch) or
+ eqZeroWithNegate(guard, lhs, _, branch)
)
}
-private predicate guardSuccessor(ComparisonOperation guard, boolean branch, BasicBlock succ) {
+private predicate guardSuccessor(Expr guard, boolean branch, BasicBlock succ) {
branch = true and succ = guard.getATrueSuccessor()
or
branch = false and succ = guard.getAFalseSuccessor()
@@ -84,6 +87,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
/** Gets the control flow node for this definition. */
ControlFlowNode getDefinition() { result = this }
+ /** Gets the basic block containing this definition. */
BasicBlock getBasicBlock() { result.contains(getDefinition()) }
/** Whether this definition is a phi node for variable `v`. */
@@ -93,15 +97,17 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
* If this definition is a phi node corresponding to a guard,
* then return the variable and the guard.
*/
- predicate isGuardPhi(VariableAccess v, ComparisonOperation guard, boolean branch) {
+ predicate isGuardPhi(VariableAccess v, Expr guard, boolean branch) {
guard_defn(v, guard, this, branch)
}
+ /** Gets the primary location of this definition. */
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
/** Whether this definition is from a parameter */
predicate definedByParameter(Parameter p) { this = p.getFunction().getEntryPoint() }
+ /** Gets a definition of `v` that is a phi input for this basic block. */
RangeSsaDefinition getAPhiInput(StackVariable v) {
this.isPhiNode(v) and
exists(BasicBlock pred |
@@ -153,6 +159,9 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
)
}
+ /**
+ * Holds if this definition of the variable `v` reached the end of the basic block `b`.
+ */
predicate reachesEndOfBB(StackVariable v, BasicBlock b) {
exists(RangeSSA x | x.ssaDefinitionReachesEndOfBB(v, this, b))
}
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll
deleted file mode 100644
index ee790404559e..000000000000
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeUtils.qll
+++ /dev/null
@@ -1,134 +0,0 @@
-import cpp
-private import semmle.code.cpp.ir.IR
-// TODO: move this dependency
-import semmle.code.cpp.ir.internal.IntegerConstant
-
-// TODO: move this out of test code
-language[monotonicAggregates]
-IntValue getConstantValue(Instruction instr) {
- result = instr.(IntegerConstantInstruction).getValue().toInt()
- or
- exists(BinaryInstruction binInstr, IntValue left, IntValue right |
- binInstr = instr and
- left = getConstantValue(binInstr.getLeft()) and
- right = getConstantValue(binInstr.getRight()) and
- (
- binInstr instanceof AddInstruction and result = add(left, right)
- or
- binInstr instanceof SubInstruction and result = sub(left, right)
- or
- binInstr instanceof MulInstruction and result = mul(left, right)
- or
- binInstr instanceof DivInstruction and result = div(left, right)
- )
- )
- or
- result = getConstantValue(instr.(CopyInstruction).getSourceValue())
- or
- exists(PhiInstruction phi |
- phi = instr and
- result =
- max(PhiInputOperand operand |
- operand = phi.getAnOperand()
- |
- getConstantValue(operand.getDef())
- ) and
- result =
- min(PhiInputOperand operand |
- operand = phi.getAnOperand()
- |
- getConstantValue(operand.getDef())
- )
- )
-}
-
-predicate valueFlowStep(Instruction i, Operand op, int delta) {
- i.(CopyInstruction).getSourceValueOperand() = op and delta = 0
- or
- exists(Operand x |
- i.(AddInstruction).getAnOperand() = op and
- i.(AddInstruction).getAnOperand() = x and
- op != x
- |
- delta = getValue(getConstantValue(x.getDef()))
- )
- or
- exists(Operand x |
- i.(SubInstruction).getLeftOperand() = op and
- i.(SubInstruction).getRightOperand() = x
- |
- delta = -getValue(getConstantValue(x.getDef()))
- )
- or
- exists(Operand x |
- i.(PointerAddInstruction).getAnOperand() = op and
- i.(PointerAddInstruction).getAnOperand() = x and
- op != x
- |
- delta = i.(PointerAddInstruction).getElementSize() * getValue(getConstantValue(x.getDef()))
- )
- or
- exists(Operand x |
- i.(PointerSubInstruction).getLeftOperand() = op and
- i.(PointerSubInstruction).getRightOperand() = x
- |
- delta = i.(PointerSubInstruction).getElementSize() * -getValue(getConstantValue(x.getDef()))
- )
-}
-
-predicate backEdge(PhiInstruction phi, PhiInputOperand op) {
- phi.getAnOperand() = op and
- phi.getBlock() = op.getPredecessorBlock().getBackEdgeSuccessor(_)
-}
-
-/**
- * Holds if a cast from `fromtyp` to `totyp` can be ignored for the purpose of
- * range analysis.
- */
-pragma[inline]
-private predicate safeCast(IntegralType fromtyp, IntegralType totyp) {
- fromtyp.getSize() < totyp.getSize() and
- (
- fromtyp.isUnsigned()
- or
- totyp.isSigned()
- )
- or
- fromtyp.getSize() <= totyp.getSize() and
- (
- fromtyp.isSigned() and
- totyp.isSigned()
- or
- fromtyp.isUnsigned() and
- totyp.isUnsigned()
- )
-}
-
-/**
- * A `ConvertInstruction` which casts from one pointer type to another.
- */
-class PtrToPtrCastInstruction extends ConvertInstruction {
- PtrToPtrCastInstruction() {
- getResultType() instanceof PointerType and
- getUnary().getResultType() instanceof PointerType
- }
-}
-
-/**
- * A `ConvertInstruction` which casts from one integer type to another in a way
- * that cannot overflow or underflow.
- */
-class SafeIntCastInstruction extends ConvertInstruction {
- SafeIntCastInstruction() { safeCast(getUnary().getResultType(), getResultType()) }
-}
-
-/**
- * A `ConvertInstruction` which does not invalidate bounds determined by
- * range analysis.
- */
-class SafeCastInstruction extends ConvertInstruction {
- SafeCastInstruction() {
- this instanceof PtrToPtrCastInstruction or
- this instanceof SafeIntCastInstruction
- }
-}
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll
deleted file mode 100644
index 37e2ac463861..000000000000
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SignAnalysis.qll
+++ /dev/null
@@ -1,579 +0,0 @@
-/**
- * Provides sign analysis to determine whether expression are always positive
- * or negative.
- *
- * The analysis is implemented as an abstract interpretation over the
- * three-valued domain `{negative, zero, positive}`.
- */
-
-import cpp
-private import semmle.code.cpp.ir.IR
-private import semmle.code.cpp.controlflow.IRGuards
-private import semmle.code.cpp.ir.ValueNumbering
-private import SignAnalysisCached
-
-private newtype TSign =
- TNeg() or
- TZero() or
- TPos()
-
-private class Sign extends TSign {
- string toString() {
- result = "-" and this = TNeg()
- or
- result = "0" and this = TZero()
- or
- result = "+" and this = TPos()
- }
-
- Sign inc() {
- this = TNeg() and result = TNeg()
- or
- this = TNeg() and result = TZero()
- or
- this = TZero() and result = TPos()
- or
- this = TPos() and result = TPos()
- }
-
- Sign dec() { result.inc() = this }
-
- Sign neg() {
- this = TNeg() and result = TPos()
- or
- this = TZero() and result = TZero()
- or
- this = TPos() and result = TNeg()
- }
-
- Sign bitnot() {
- this = TNeg() and result = TPos()
- or
- this = TNeg() and result = TZero()
- or
- this = TZero() and result = TNeg()
- or
- this = TPos() and result = TNeg()
- }
-
- Sign add(Sign s) {
- this = TZero() and result = s
- or
- s = TZero() and result = this
- or
- this = s and this = result
- or
- this = TPos() and s = TNeg()
- or
- this = TNeg() and s = TPos()
- }
-
- Sign mul(Sign s) {
- result = TZero() and this = TZero()
- or
- result = TZero() and s = TZero()
- or
- result = TNeg() and this = TPos() and s = TNeg()
- or
- result = TNeg() and this = TNeg() and s = TPos()
- or
- result = TPos() and this = TPos() and s = TPos()
- or
- result = TPos() and this = TNeg() and s = TNeg()
- }
-
- Sign div(Sign s) {
- result = TZero() and s = TNeg()
- or
- result = TZero() and s = TPos()
- or
- result = TNeg() and this = TPos() and s = TNeg()
- or
- result = TNeg() and this = TNeg() and s = TPos()
- or
- result = TPos() and this = TPos() and s = TPos()
- or
- result = TPos() and this = TNeg() and s = TNeg()
- }
-
- Sign rem(Sign s) {
- result = TZero() and s = TNeg()
- or
- result = TZero() and s = TPos()
- or
- result = this and s = TNeg()
- or
- result = this and s = TPos()
- }
-
- Sign bitand(Sign s) {
- result = TZero() and this = TZero()
- or
- result = TZero() and s = TZero()
- or
- result = TZero() and this = TPos()
- or
- result = TZero() and s = TPos()
- or
- result = TNeg() and this = TNeg() and s = TNeg()
- or
- result = TPos() and this = TNeg() and s = TPos()
- or
- result = TPos() and this = TPos() and s = TNeg()
- or
- result = TPos() and this = TPos() and s = TPos()
- }
-
- Sign bitor(Sign s) {
- result = TZero() and this = TZero() and s = TZero()
- or
- result = TNeg() and this = TNeg()
- or
- result = TNeg() and s = TNeg()
- or
- result = TPos() and this = TPos() and s = TZero()
- or
- result = TPos() and this = TZero() and s = TPos()
- or
- result = TPos() and this = TPos() and s = TPos()
- }
-
- Sign bitxor(Sign s) {
- result = TZero() and this = s
- or
- result = this and s = TZero()
- or
- result = s and this = TZero()
- or
- result = TPos() and this = TPos() and s = TPos()
- or
- result = TNeg() and this = TNeg() and s = TPos()
- or
- result = TNeg() and this = TPos() and s = TNeg()
- or
- result = TPos() and this = TNeg() and s = TNeg()
- }
-
- Sign lshift(Sign s) {
- result = TZero() and this = TZero()
- or
- result = this and s = TZero()
- or
- this != TZero() and s != TZero()
- }
-
- Sign rshift(Sign s) {
- result = TZero() and this = TZero()
- or
- result = this and s = TZero()
- or
- result = TNeg() and this = TNeg()
- or
- result != TNeg() and this = TPos() and s != TZero()
- }
-
- Sign urshift(Sign s) {
- result = TZero() and this = TZero()
- or
- result = this and s = TZero()
- or
- result != TZero() and this = TNeg() and s != TZero()
- or
- result != TNeg() and this = TPos() and s != TZero()
- }
-}
-
-private Sign certainInstructionSign(Instruction inst) {
- exists(int i | inst.(IntegerConstantInstruction).getValue().toInt() = i |
- i < 0 and result = TNeg()
- or
- i = 0 and result = TZero()
- or
- i > 0 and result = TPos()
- )
- or
- exists(float f | f = inst.(FloatConstantInstruction).getValue().toFloat() |
- f < 0 and result = TNeg()
- or
- f = 0 and result = TZero()
- or
- f > 0 and result = TPos()
- )
-}
-
-private newtype CastKind =
- TWiden() or
- TSame() or
- TNarrow()
-
-private CastKind getCastKind(ConvertInstruction ci) {
- exists(int fromSize, int toSize |
- toSize = ci.getResultSize() and
- fromSize = ci.getUnary().getResultSize()
- |
- fromSize < toSize and
- result = TWiden()
- or
- fromSize = toSize and
- result = TSame()
- or
- fromSize > toSize and
- result = TNarrow()
- )
-}
-
-private predicate bindBool(boolean bool) {
- bool = true or
- bool = false
-}
-
-private Sign castSign(Sign s, boolean fromSigned, boolean toSigned, CastKind ck) {
- result = TZero() and
- (
- bindBool(fromSigned) and
- bindBool(toSigned) and
- s = TZero()
- or
- bindBool(fromSigned) and
- bindBool(toSigned) and
- ck = TNarrow()
- )
- or
- result = TPos() and
- (
- bindBool(fromSigned) and
- bindBool(toSigned) and
- s = TPos()
- or
- bindBool(fromSigned) and
- bindBool(toSigned) and
- s = TNeg() and
- ck = TNarrow()
- or
- fromSigned = true and
- toSigned = false and
- s = TNeg()
- )
- or
- result = TNeg() and
- (
- fromSigned = true and
- toSigned = true and
- s = TNeg()
- or
- fromSigned = false and
- toSigned = true and
- s = TPos() and
- ck != TWiden()
- )
-}
-
-/** Holds if the sign of `e` is too complicated to determine. */
-private predicate unknownSign(Instruction i) {
- // REVIEW: This should probably be a list of the instructions that we _do_ understand, rather than
- // the ones we don't understand. Currently, if we try to compute the sign of an instruction that
- // we don't understand, and it isn't on this list, we incorrectly compute the sign as "none"
- // instead of "+,0,-".
- // Even better, we could track the state of each instruction as a power set of {non-negative,
- // non-positive, non-zero}, which would mean that the representation of the sign of an unknown
- // value would be the empty set.
- (
- i instanceof UninitializedInstruction
- or
- i instanceof InitializeParameterInstruction
- or
- i instanceof BuiltInOperationInstruction
- or
- i instanceof CallInstruction
- or
- i instanceof ChiInstruction
- )
-}
-
-/**
- * Holds if `lowerbound` is a lower bound for `bounded`. This is restricted
- * to only include bounds for which we might determine a sign.
- */
-private predicate lowerBound(
- IRGuardCondition comp, Operand lowerbound, Operand bounded, boolean isStrict
-) {
- exists(int adjustment, Operand compared |
- valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and
- (
- isStrict = true and
- adjustment = 0
- or
- isStrict = false and
- adjustment = 1
- ) and
- comp.ensuresLt(lowerbound, compared, adjustment, bounded.getUse().getBlock(), true)
- )
-}
-
-/**
- * Holds if `upperbound` is an upper bound for `bounded` at `pos`. This is restricted
- * to only include bounds for which we might determine a sign.
- */
-private predicate upperBound(
- IRGuardCondition comp, Operand upperbound, Operand bounded, boolean isStrict
-) {
- exists(int adjustment, Operand compared |
- valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and
- (
- isStrict = true and
- adjustment = 0
- or
- isStrict = false and
- adjustment = 1
- ) and
- comp.ensuresLt(compared, upperbound, adjustment, bounded.getUse().getBlock(), true)
- )
-}
-
-/**
- * Holds if `eqbound` is an equality/inequality for `bounded` at `pos`. This is
- * restricted to only include bounds for which we might determine a sign. The
- * boolean `isEq` gives the polarity:
- * - `isEq = true` : `bounded = eqbound`
- * - `isEq = false` : `bounded != eqbound`
- */
-private predicate eqBound(IRGuardCondition guard, Operand eqbound, Operand bounded, boolean isEq) {
- exists(Operand compared |
- valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and
- guard.ensuresEq(compared, eqbound, 0, bounded.getUse().getBlock(), isEq)
- )
-}
-
-/**
- * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
- * order for `v` to be positive.
- */
-private predicate posBound(IRGuardCondition comp, Operand bound, Operand op) {
- upperBound(comp, bound, op, _) or
- eqBound(comp, bound, op, true)
-}
-
-/**
- * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
- * order for `v` to be negative.
- */
-private predicate negBound(IRGuardCondition comp, Operand bound, Operand op) {
- lowerBound(comp, bound, op, _) or
- eqBound(comp, bound, op, true)
-}
-
-/**
- * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
- * can be zero.
- */
-private predicate zeroBound(IRGuardCondition comp, Operand bound, Operand op) {
- lowerBound(comp, bound, op, _) or
- upperBound(comp, bound, op, _) or
- eqBound(comp, bound, op, _)
-}
-
-/** Holds if `bound` allows `v` to be positive at `pos`. */
-private predicate posBoundOk(IRGuardCondition comp, Operand bound, Operand op) {
- posBound(comp, bound, op) and TPos() = operandSign(bound)
-}
-
-/** Holds if `bound` allows `v` to be negative at `pos`. */
-private predicate negBoundOk(IRGuardCondition comp, Operand bound, Operand op) {
- negBound(comp, bound, op) and TNeg() = operandSign(bound)
-}
-
-/** Holds if `bound` allows `v` to be zero at `pos`. */
-private predicate zeroBoundOk(IRGuardCondition comp, Operand bound, Operand op) {
- lowerBound(comp, bound, op, _) and TNeg() = operandSign(bound)
- or
- lowerBound(comp, bound, op, false) and TZero() = operandSign(bound)
- or
- upperBound(comp, bound, op, _) and TPos() = operandSign(bound)
- or
- upperBound(comp, bound, op, false) and TZero() = operandSign(bound)
- or
- eqBound(comp, bound, op, true) and TZero() = operandSign(bound)
- or
- eqBound(comp, bound, op, false) and TZero() != operandSign(bound)
-}
-
-private Sign binaryOpLhsSign(BinaryInstruction i) { result = operandSign(i.getLeftOperand()) }
-
-private Sign binaryOpRhsSign(BinaryInstruction i) { result = operandSign(i.getRightOperand()) }
-
-pragma[noinline]
-private predicate binaryOpSigns(BinaryInstruction i, Sign lhs, Sign rhs) {
- lhs = binaryOpLhsSign(i) and
- rhs = binaryOpRhsSign(i)
-}
-
-private Sign unguardedOperandSign(Operand operand) {
- result = instructionSign(operand.getDef()) and
- not hasGuard(operand, result)
-}
-
-private Sign guardedOperandSign(Operand operand) {
- result = instructionSign(operand.getDef()) and
- hasGuard(operand, result)
-}
-
-private Sign guardedOperandSignOk(Operand operand) {
- result = TPos() and
- forex(IRGuardCondition guard, Operand bound | posBound(guard, bound, operand) |
- posBoundOk(guard, bound, operand)
- )
- or
- result = TNeg() and
- forex(IRGuardCondition guard, Operand bound | negBound(guard, bound, operand) |
- negBoundOk(guard, bound, operand)
- )
- or
- result = TZero() and
- forex(IRGuardCondition guard, Operand bound | zeroBound(guard, bound, operand) |
- zeroBoundOk(guard, bound, operand)
- )
-}
-
-/**
- * Holds if there is a bound that might restrict whether `v` has the sign `s`
- * at `pos`.
- */
-private predicate hasGuard(Operand op, Sign s) {
- s = TPos() and posBound(_, _, op)
- or
- s = TNeg() and negBound(_, _, op)
- or
- s = TZero() and zeroBound(_, _, op)
-}
-
-cached
-module SignAnalysisCached {
- /**
- * Gets a sign that `operand` may have at `pos`, taking guards into account.
- */
- cached
- Sign operandSign(Operand operand) {
- result = unguardedOperandSign(operand)
- or
- result = guardedOperandSign(operand) and
- result = guardedOperandSignOk(operand)
- or
- // `result` is unconstrained if the definition is inexact. Then any sign is possible.
- operand.isDefinitionInexact()
- }
-
- cached
- Sign instructionSign(Instruction i) {
- result = certainInstructionSign(i)
- or
- not exists(certainInstructionSign(i)) and
- not (
- result = TNeg() and
- i.getResultType().(IntegralType).isUnsigned()
- ) and
- (
- unknownSign(i)
- or
- exists(ConvertInstruction ci, Instruction prior, boolean fromSigned, boolean toSigned |
- i = ci and
- prior = ci.getUnary() and
- (if ci.getResultType().(IntegralType).isSigned() then toSigned = true else toSigned = false) and
- (
- if prior.getResultType().(IntegralType).isSigned()
- then fromSigned = true
- else fromSigned = false
- ) and
- result = castSign(operandSign(ci.getAnOperand()), fromSigned, toSigned, getCastKind(ci))
- )
- or
- result = operandSign(i.(CopyInstruction).getSourceValueOperand())
- or
- result = operandSign(i.(BitComplementInstruction).getAnOperand()).bitnot()
- or
- result = operandSign(i.(NegateInstruction).getAnOperand()).neg()
- or
- exists(Sign s1, Sign s2 | binaryOpSigns(i, s1, s2) |
- i instanceof AddInstruction and result = s1.add(s2)
- or
- i instanceof SubInstruction and result = s1.add(s2.neg())
- or
- i instanceof MulInstruction and result = s1.mul(s2)
- or
- i instanceof DivInstruction and result = s1.div(s2)
- or
- i instanceof RemInstruction and result = s1.rem(s2)
- or
- i instanceof BitAndInstruction and result = s1.bitand(s2)
- or
- i instanceof BitOrInstruction and result = s1.bitor(s2)
- or
- i instanceof BitXorInstruction and result = s1.bitxor(s2)
- or
- i instanceof ShiftLeftInstruction and result = s1.lshift(s2)
- or
- i instanceof ShiftRightInstruction and
- i.getResultType().(IntegralType).isSigned() and
- result = s1.rshift(s2)
- or
- i instanceof ShiftRightInstruction and
- not i.getResultType().(IntegralType).isSigned() and
- result = s1.urshift(s2)
- )
- or
- // use hasGuard here?
- result = operandSign(i.(PhiInstruction).getAnOperand())
- )
- }
-}
-
-/** Holds if `i` can be positive and cannot be negative. */
-predicate positiveInstruction(Instruction i) {
- instructionSign(i) = TPos() and
- not instructionSign(i) = TNeg()
-}
-
-/** Holds if `i` at `pos` can be positive at and cannot be negative. */
-predicate positive(Operand op) {
- operandSign(op) = TPos() and
- not operandSign(op) = TNeg()
-}
-
-/** Holds if `i` can be negative and cannot be positive. */
-predicate negativeInstruction(Instruction i) {
- instructionSign(i) = TNeg() and
- not instructionSign(i) = TPos()
-}
-
-/** Holds if `i` at `pos` can be negative and cannot be positive. */
-predicate negative(Operand op) {
- operandSign(op) = TNeg() and
- not operandSign(op) = TPos()
-}
-
-/** Holds if `i` is strictly positive. */
-predicate strictlyPositiveInstruction(Instruction i) {
- instructionSign(i) = TPos() and
- not instructionSign(i) = TNeg() and
- not instructionSign(i) = TZero()
-}
-
-/** Holds if `i` is strictly positive at `pos`. */
-predicate strictlyPositive(Operand op) {
- operandSign(op) = TPos() and
- not operandSign(op) = TNeg() and
- not operandSign(op) = TZero()
-}
-
-/** Holds if `i` is strictly negative. */
-predicate strictlyNegativeInstruction(Instruction i) {
- instructionSign(i) = TNeg() and
- not instructionSign(i) = TPos() and
- not instructionSign(i) = TZero()
-}
-
-/** Holds if `i` is strictly negative at `pos`. */
-predicate strictlyNegative(Operand op) {
- operandSign(op) = TNeg() and
- not operandSign(op) = TPos() and
- not operandSign(op) = TZero()
-}
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
index 867d96b804c6..fcaec3d3e13b 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
@@ -44,6 +44,8 @@
import cpp
private import RangeAnalysisUtils
+private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
+private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDefinition
import RangeSSA
import SimpleRangeAnalysisCached
private import NanAnalysis
@@ -126,7 +128,7 @@ private class UnsignedBitwiseAndExpr extends BitwiseAndExpr {
UnsignedBitwiseAndExpr() {
(
getLeftOperand().getFullyConverted().getType().getUnderlyingType().(IntegralType).isUnsigned() or
- getLeftOperand().getFullyConverted().getValue().toInt() >= 0
+ getValue(getLeftOperand().getFullyConverted()).toInt() >= 0
) and
(
getRightOperand()
@@ -135,7 +137,7 @@ private class UnsignedBitwiseAndExpr extends BitwiseAndExpr {
.getUnderlyingType()
.(IntegralType)
.isUnsigned() or
- getRightOperand().getFullyConverted().getValue().toInt() >= 0
+ getValue(getRightOperand().getFullyConverted()).toInt() >= 0
)
}
}
@@ -156,6 +158,92 @@ float safeFloor(float v) {
result = v
}
+/** A `MulExpr` where exactly one operand is constant. */
+private class MulByConstantExpr extends MulExpr {
+ float constant;
+ Expr operand;
+
+ MulByConstantExpr() {
+ exists(Expr constantExpr |
+ this.hasOperands(constantExpr, operand) and
+ constant = getValue(constantExpr.getFullyConverted()).toFloat() and
+ not exists(getValue(operand.getFullyConverted()).toFloat())
+ )
+ }
+
+ /** Gets the value of the constant operand. */
+ float getConstant() { result = constant }
+
+ /** Gets the non-constant operand. */
+ Expr getOperand() { result = operand }
+}
+
+private class UnsignedMulExpr extends MulExpr {
+ UnsignedMulExpr() {
+ this.getType().(IntegralType).isUnsigned() and
+ // Avoid overlap. It should be slightly cheaper to analyze
+ // `MulByConstantExpr`.
+ not this instanceof MulByConstantExpr
+ }
+}
+
+/**
+ * Holds if `expr` is effectively a multiplication of `operand` with the
+ * positive constant `positive`.
+ */
+private predicate effectivelyMultipliesByPositive(Expr expr, Expr operand, float positive) {
+ operand = expr.(MulByConstantExpr).getOperand() and
+ positive = expr.(MulByConstantExpr).getConstant() and
+ positive >= 0.0 // includes positive zero
+ or
+ operand = expr.(UnaryPlusExpr).getOperand() and
+ positive = 1.0
+ or
+ operand = expr.(CommaExpr).getRightOperand() and
+ positive = 1.0
+ or
+ operand = expr.(StmtExpr).getResultExpr() and
+ positive = 1.0
+}
+
+/**
+ * Holds if `expr` is effectively a multiplication of `operand` with the
+ * negative constant `negative`.
+ */
+private predicate effectivelyMultipliesByNegative(Expr expr, Expr operand, float negative) {
+ operand = expr.(MulByConstantExpr).getOperand() and
+ negative = expr.(MulByConstantExpr).getConstant() and
+ negative < 0.0 // includes negative zero
+ or
+ operand = expr.(UnaryMinusExpr).getOperand() and
+ negative = -1.0
+}
+
+private class AssignMulByConstantExpr extends AssignMulExpr {
+ float constant;
+
+ AssignMulByConstantExpr() { constant = getValue(this.getRValue().getFullyConverted()).toFloat() }
+
+ float getConstant() { result = constant }
+}
+
+private class AssignMulByPositiveConstantExpr extends AssignMulByConstantExpr {
+ AssignMulByPositiveConstantExpr() { constant >= 0.0 }
+}
+
+private class AssignMulByNegativeConstantExpr extends AssignMulByConstantExpr {
+ AssignMulByNegativeConstantExpr() { constant < 0.0 }
+}
+
+private class UnsignedAssignMulExpr extends AssignMulExpr {
+ UnsignedAssignMulExpr() {
+ this.getType().(IntegralType).isUnsigned() and
+ // Avoid overlap. It should be slightly cheaper to analyze
+ // `AssignMulByConstantExpr`.
+ not this instanceof AssignMulByConstantExpr
+ }
+}
+
/** Set of expressions which we know how to analyze. */
private predicate analyzableExpr(Expr e) {
// The type of the expression must be arithmetic. We reuse the logic in
@@ -164,9 +252,9 @@ private predicate analyzableExpr(Expr e) {
(
exists(getValue(e).toFloat())
or
- e instanceof UnaryPlusExpr
+ effectivelyMultipliesByPositive(e, _, _)
or
- e instanceof UnaryMinusExpr
+ effectivelyMultipliesByNegative(e, _, _)
or
e instanceof MinExpr
or
@@ -178,19 +266,21 @@ private predicate analyzableExpr(Expr e) {
or
e instanceof SubExpr
or
+ e instanceof UnsignedMulExpr
+ or
e instanceof AssignExpr
or
e instanceof AssignAddExpr
or
e instanceof AssignSubExpr
or
- e instanceof CrementOperation
+ e instanceof UnsignedAssignMulExpr
or
- e instanceof RemExpr
+ e instanceof AssignMulByConstantExpr
or
- e instanceof CommaExpr
+ e instanceof CrementOperation
or
- e instanceof StmtExpr
+ e instanceof RemExpr
or
// A conversion is analyzable, provided that its child has an arithmetic
// type. (Sometimes the child is a reference type, and so does not get
@@ -206,7 +296,10 @@ private predicate analyzableExpr(Expr e) {
e instanceof UnsignedBitwiseAndExpr
or
// `>>` by a constant
- exists(e.(RShiftExpr).getRightOperand().getValue())
+ exists(getValue(e.(RShiftExpr).getRightOperand()))
+ or
+ // A modeled expression for range analysis
+ e instanceof SimpleRangeAnalysisExpr
)
}
@@ -227,30 +320,27 @@ private predicate defDependsOnDef(
// Definitions with a defining value.
exists(Expr expr | assignmentDef(def, v, expr) | exprDependsOnDef(expr, srcDef, srcVar))
or
- exists(AssignAddExpr assignAdd, RangeSsaDefinition nextDef |
- def = assignAdd and
- assignAdd.getLValue() = nextDef.getAUse(v)
- |
- defDependsOnDef(nextDef, v, srcDef, srcVar) or
- exprDependsOnDef(assignAdd.getRValue(), srcDef, srcVar)
- )
- or
- exists(AssignSubExpr assignSub, RangeSsaDefinition nextDef |
- def = assignSub and
- assignSub.getLValue() = nextDef.getAUse(v)
- |
- defDependsOnDef(nextDef, v, srcDef, srcVar) or
- exprDependsOnDef(assignSub.getRValue(), srcDef, srcVar)
+ // Assignment operations with a defining value
+ exists(AssignOperation assignOp |
+ analyzableExpr(assignOp) and
+ def = assignOp and
+ def.getAVariable() = v and
+ exprDependsOnDef(assignOp, srcDef, srcVar)
)
or
exists(CrementOperation crem |
def = crem and
- crem.getOperand() = v.getAnAccess() and
+ def.getAVariable() = v and
exprDependsOnDef(crem.getOperand(), srcDef, srcVar)
)
or
// Phi nodes.
phiDependsOnDef(def, v, srcDef, srcVar)
+ or
+ // Extensions
+ exists(Expr expr | def.(SimpleRangeAnalysisDefinition).dependsOnExpr(v, expr) |
+ exprDependsOnDef(expr, srcDef, srcVar)
+ )
}
/**
@@ -258,12 +348,14 @@ private predicate defDependsOnDef(
* the structure of `getLowerBoundsImpl` and `getUpperBoundsImpl`.
*/
private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVariable srcVar) {
- exists(UnaryMinusExpr negateExpr | e = negateExpr |
- exprDependsOnDef(negateExpr.getOperand(), srcDef, srcVar)
+ exists(Expr operand |
+ effectivelyMultipliesByNegative(e, operand, _) and
+ exprDependsOnDef(operand, srcDef, srcVar)
)
or
- exists(UnaryPlusExpr plusExpr | e = plusExpr |
- exprDependsOnDef(plusExpr.getOperand(), srcDef, srcVar)
+ exists(Expr operand |
+ effectivelyMultipliesByPositive(e, operand, _) and
+ exprDependsOnDef(operand, srcDef, srcVar)
)
or
exists(MinExpr minExpr | e = minExpr | exprDependsOnDef(minExpr.getAnOperand(), srcDef, srcVar))
@@ -278,6 +370,10 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
or
exists(SubExpr subExpr | e = subExpr | exprDependsOnDef(subExpr.getAnOperand(), srcDef, srcVar))
or
+ exists(UnsignedMulExpr mulExpr | e = mulExpr |
+ exprDependsOnDef(mulExpr.getAnOperand(), srcDef, srcVar)
+ )
+ or
exists(AssignExpr addExpr | e = addExpr | exprDependsOnDef(addExpr.getRValue(), srcDef, srcVar))
or
exists(AssignAddExpr addExpr | e = addExpr |
@@ -288,20 +384,20 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
exprDependsOnDef(subExpr.getAnOperand(), srcDef, srcVar)
)
or
- exists(CrementOperation crementExpr | e = crementExpr |
- exprDependsOnDef(crementExpr.getOperand(), srcDef, srcVar)
+ exists(UnsignedAssignMulExpr mulExpr | e = mulExpr |
+ exprDependsOnDef(mulExpr.getAnOperand(), srcDef, srcVar)
)
or
- exists(RemExpr remExpr | e = remExpr | exprDependsOnDef(remExpr.getAnOperand(), srcDef, srcVar))
- or
- exists(CommaExpr commaExpr | e = commaExpr |
- exprDependsOnDef(commaExpr.getRightOperand(), srcDef, srcVar)
+ exists(AssignMulByConstantExpr mulExpr | e = mulExpr |
+ exprDependsOnDef(mulExpr.getLValue(), srcDef, srcVar)
)
or
- exists(StmtExpr stmtExpr | e = stmtExpr |
- exprDependsOnDef(stmtExpr.getResultExpr(), srcDef, srcVar)
+ exists(CrementOperation crementExpr | e = crementExpr |
+ exprDependsOnDef(crementExpr.getOperand(), srcDef, srcVar)
)
or
+ exists(RemExpr remExpr | e = remExpr | exprDependsOnDef(remExpr.getAnOperand(), srcDef, srcVar))
+ or
exists(Conversion convExpr | e = convExpr | exprDependsOnDef(convExpr.getExpr(), srcDef, srcVar))
or
// unsigned `&`
@@ -313,11 +409,21 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
// `>>` by a constant
exists(RShiftExpr rs |
rs = e and
- exists(rs.getRightOperand().getValue()) and
+ exists(getValue(rs.getRightOperand())) and
exprDependsOnDef(rs.getLeftOperand(), srcDef, srcVar)
)
or
e = srcDef.getAUse(srcVar)
+ or
+ // A modeled expression for range analysis
+ exists(SimpleRangeAnalysisExpr rae | rae = e |
+ rae.dependsOnDef(srcDef, srcVar)
+ or
+ exists(Expr child |
+ rae.dependsOnChild(child) and
+ exprDependsOnDef(child, srcDef, srcVar)
+ )
+ )
}
/**
@@ -327,11 +433,11 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
private predicate phiDependsOnDef(
RangeSsaDefinition phi, StackVariable v, RangeSsaDefinition srcDef, StackVariable srcVar
) {
- exists(VariableAccess access, ComparisonOperation guard |
+ exists(VariableAccess access, Expr guard |
access = v.getAnAccess() and
phi.isGuardPhi(access, guard, _)
|
- exprDependsOnDef(guard.getAnOperand(), srcDef, srcVar) or
+ exprDependsOnDef(guard.(ComparisonOperation).getAnOperand(), srcDef, srcVar) or
exprDependsOnDef(access, srcDef, srcVar)
)
or
@@ -354,6 +460,39 @@ private predicate isRecursiveDef(RangeSsaDefinition def, StackVariable v) {
defDependsOnDefTransitively(def, v, def, v)
}
+/**
+ * Holds if the bounds of `e` depend on a recursive definition, meaning that
+ * `e` is likely to have many candidate bounds during the main recursion.
+ */
+private predicate isRecursiveExpr(Expr e) {
+ exists(RangeSsaDefinition def, StackVariable v | exprDependsOnDef(e, def, v) |
+ isRecursiveDef(def, v)
+ )
+}
+
+/**
+ * Holds if `binop` is a binary operation that's likely to be assigned a
+ * quadratic (or more) number of candidate bounds during the analysis. This can
+ * happen when two conditions are satisfied:
+ * 1. It is likely there are many more candidate bounds for `binop` than for
+ * its operands. For example, the number of candidate bounds for `x + y`,
+ * denoted here nbounds(`x + y`), will be O(nbounds(`x`) * nbounds(`y`)).
+ * In contrast, nbounds(`b ? x : y`) is only O(nbounds(`x`) + nbounds(`y`)).
+ * 2. Both operands of `binop` are recursively determined and are therefore
+ * likely to have a large number of candidate bounds.
+ */
+private predicate isRecursiveBinary(BinaryOperation binop) {
+ (
+ binop instanceof UnsignedMulExpr
+ or
+ binop instanceof AddExpr
+ or
+ binop instanceof SubExpr
+ ) and
+ isRecursiveExpr(binop.getLeftOperand()) and
+ isRecursiveExpr(binop.getRightOperand())
+}
+
/**
* We distinguish 3 kinds of RangeSsaDefinition:
*
@@ -369,7 +508,7 @@ private predicate isRecursiveDef(RangeSsaDefinition def, StackVariable v) {
* This predicate finds all the definitions in the first set.
*/
private predicate assignmentDef(RangeSsaDefinition def, StackVariable v, Expr expr) {
- v.getUnspecifiedType() instanceof ArithmeticType and
+ getVariableRangeType(v) instanceof ArithmeticType and
(
def = v.getInitializer().getExpr() and def = expr
or
@@ -381,9 +520,20 @@ private predicate assignmentDef(RangeSsaDefinition def, StackVariable v, Expr ex
)
}
-/** See comment above sourceDef. */
+/** See comment above assignmentDef. */
private predicate analyzableDef(RangeSsaDefinition def, StackVariable v) {
- assignmentDef(def, v, _) or defDependsOnDef(def, v, _, _)
+ assignmentDef(def, v, _)
+ or
+ analyzableExpr(def.(AssignOperation)) and
+ v = def.getAVariable()
+ or
+ analyzableExpr(def.(CrementOperation)) and
+ v = def.getAVariable()
+ or
+ phiDependsOnDef(def, v, _, _)
+ or
+ // A modeled def for range analysis
+ def.(SimpleRangeAnalysisDefinition).hasRangeInformationFor(v)
}
/**
@@ -435,13 +585,6 @@ private float addRoundingDownSmall(float x, float small) {
if (x + small) - x > small then result = (x + small).nextDown() else result = (x + small)
}
-/**
- * Gets the truncated lower bounds of the fully converted expression.
- */
-private float getFullyConvertedLowerBounds(Expr expr) {
- result = getTruncatedLowerBounds(expr.getFullyConverted())
-}
-
/**
* Gets the lower bounds of the expression.
*
@@ -469,9 +612,18 @@ private float getTruncatedLowerBounds(Expr expr) {
else (
// Some of the bounds computed by getLowerBoundsImpl might
// overflow, so we replace invalid bounds with exprMinVal.
- exists(float newLB | newLB = getLowerBoundsImpl(expr) |
+ exists(float newLB | newLB = normalizeFloatUp(getLowerBoundsImpl(expr)) |
if exprMinVal(expr) <= newLB and newLB <= exprMaxVal(expr)
- then result = newLB
+ then
+ // Apply widening where we might get a combinatorial explosion.
+ if isRecursiveBinary(expr)
+ then
+ result =
+ max(float widenLB |
+ widenLB = wideningLowerBounds(expr.getUnspecifiedType()) and
+ not widenLB > newLB
+ )
+ else result = newLB
else result = exprMinVal(expr)
)
or
@@ -488,13 +640,6 @@ private float getTruncatedLowerBounds(Expr expr) {
result = exprMinVal(expr)
}
-/**
- * Gets the truncated upper bounds of the fully converted expression.
- */
-private float getFullyConvertedUpperBounds(Expr expr) {
- result = getTruncatedUpperBounds(expr.getFullyConverted())
-}
-
/**
* Gets the upper bounds of the expression.
*
@@ -523,9 +668,18 @@ private float getTruncatedUpperBounds(Expr expr) {
// Some of the bounds computed by `getUpperBoundsImpl`
// might overflow, so we replace invalid bounds with
// `exprMaxVal`.
- exists(float newUB | newUB = getUpperBoundsImpl(expr) |
+ exists(float newUB | newUB = normalizeFloatUp(getUpperBoundsImpl(expr)) |
if exprMinVal(expr) <= newUB and newUB <= exprMaxVal(expr)
- then result = newUB
+ then
+ // Apply widening where we might get a combinatorial explosion.
+ if isRecursiveBinary(expr)
+ then
+ result =
+ min(float widenUB |
+ widenUB = wideningUpperBounds(expr.getUnspecifiedType()) and
+ not widenUB < newUB
+ )
+ else result = newUB
else result = exprMaxVal(expr)
)
or
@@ -562,339 +716,393 @@ deprecated predicate positive_overflow(Expr expr) { exprMightOverflowPositively(
/** Only to be called by `getTruncatedLowerBounds`. */
private float getLowerBoundsImpl(Expr expr) {
- exists(UnaryPlusExpr plusExpr |
- expr = plusExpr and
- result = getFullyConvertedLowerBounds(plusExpr.getOperand())
- )
- or
- exists(UnaryMinusExpr negateExpr, float xHigh |
- expr = negateExpr and
- xHigh = getFullyConvertedUpperBounds(negateExpr.getOperand()) and
- result = -xHigh
- )
- or
- exists(MinExpr minExpr |
- expr = minExpr and
- // Return the union of the lower bounds from both children.
- result = getFullyConvertedLowerBounds(minExpr.getAnOperand())
- )
- or
- exists(MaxExpr maxExpr |
- expr = maxExpr and
- // Compute the cross product of the bounds from both children. We are
- // using this mathematical property:
- //
- // max (minimum{X}, minimum{Y})
- // = minimum { max(x,y) | x in X, y in Y }
- exists(float x, float y |
- x = getFullyConvertedLowerBounds(maxExpr.getLeftOperand()) and
- y = getFullyConvertedLowerBounds(maxExpr.getRightOperand()) and
- if x >= y then result = x else result = y
+ (
+ exists(Expr operand, float operandLow, float positive |
+ effectivelyMultipliesByPositive(expr, operand, positive) and
+ operandLow = getFullyConvertedLowerBounds(operand) and
+ result = positive * operandLow
)
- )
- or
- // ConditionalExpr (true branch)
- exists(ConditionalExpr condExpr |
- expr = condExpr and
- // Use `boolConversionUpperBound` to determine whether the condition
- // might evaluate to `true`.
- boolConversionUpperBound(condExpr.getCondition().getFullyConverted()) = 1 and
- result = getFullyConvertedLowerBounds(condExpr.getThen())
- )
- or
- // ConditionalExpr (false branch)
- exists(ConditionalExpr condExpr |
- expr = condExpr and
- // Use `boolConversionLowerBound` to determine whether the condition
- // might evaluate to `false`.
- boolConversionLowerBound(condExpr.getCondition().getFullyConverted()) = 0 and
- result = getFullyConvertedLowerBounds(condExpr.getElse())
- )
- or
- exists(AddExpr addExpr, float xLow, float yLow |
- expr = addExpr and
- xLow = getFullyConvertedLowerBounds(addExpr.getLeftOperand()) and
- yLow = getFullyConvertedLowerBounds(addExpr.getRightOperand()) and
- result = addRoundingDown(xLow, yLow)
- )
- or
- exists(SubExpr subExpr, float xLow, float yHigh |
- expr = subExpr and
- xLow = getFullyConvertedLowerBounds(subExpr.getLeftOperand()) and
- yHigh = getFullyConvertedUpperBounds(subExpr.getRightOperand()) and
- result = addRoundingDown(xLow, -yHigh)
- )
- or
- exists(AssignExpr assign |
- expr = assign and
- result = getFullyConvertedLowerBounds(assign.getRValue())
- )
- or
- exists(AssignAddExpr addExpr, float xLow, float yLow |
- expr = addExpr and
- xLow = getFullyConvertedLowerBounds(addExpr.getLValue()) and
- yLow = getFullyConvertedLowerBounds(addExpr.getRValue()) and
- result = addRoundingDown(xLow, yLow)
- )
- or
- exists(AssignSubExpr subExpr, float xLow, float yHigh |
- expr = subExpr and
- xLow = getFullyConvertedLowerBounds(subExpr.getLValue()) and
- yHigh = getFullyConvertedUpperBounds(subExpr.getRValue()) and
- result = addRoundingDown(xLow, -yHigh)
- )
- or
- exists(PrefixIncrExpr incrExpr, float xLow |
- expr = incrExpr and
- xLow = getFullyConvertedLowerBounds(incrExpr.getOperand()) and
- result = xLow + 1
- )
- or
- exists(PrefixDecrExpr decrExpr, float xLow |
- expr = decrExpr and
- xLow = getFullyConvertedLowerBounds(decrExpr.getOperand()) and
- result = addRoundingDownSmall(xLow, -1)
- )
- or
- // `PostfixIncrExpr` and `PostfixDecrExpr` return the value of their
- // operand. The incrementing/decrementing behavior is handled in
- // `getDefLowerBoundsImpl`.
- exists(PostfixIncrExpr incrExpr |
- expr = incrExpr and
- result = getFullyConvertedLowerBounds(incrExpr.getOperand())
- )
- or
- exists(PostfixDecrExpr decrExpr |
- expr = decrExpr and
- result = getFullyConvertedLowerBounds(decrExpr.getOperand())
- )
- or
- exists(RemExpr remExpr | expr = remExpr |
- // If both inputs are positive then the lower bound is zero.
- result = 0
or
- // If either input could be negative then the output could be
- // negative. If so, the lower bound of `x%y` is `-abs(y)`, which is
- // equal to `min(-y,y)`.
- exists(float childLB |
- childLB = getFullyConvertedLowerBounds(remExpr.getAnOperand()) and
- not childLB >= 0
- |
- result = getFullyConvertedLowerBounds(remExpr.getRightOperand())
+ exists(Expr operand, float operandHigh, float negative |
+ effectivelyMultipliesByNegative(expr, operand, negative) and
+ operandHigh = getFullyConvertedUpperBounds(operand) and
+ result = negative * operandHigh
+ )
+ or
+ exists(MinExpr minExpr |
+ expr = minExpr and
+ // Return the union of the lower bounds from both children.
+ result = getFullyConvertedLowerBounds(minExpr.getAnOperand())
+ )
+ or
+ exists(MaxExpr maxExpr |
+ expr = maxExpr and
+ // Compute the cross product of the bounds from both children. We are
+ // using this mathematical property:
+ //
+ // max (minimum{X}, minimum{Y})
+ // = minimum { max(x,y) | x in X, y in Y }
+ exists(float x, float y |
+ x = getFullyConvertedLowerBounds(maxExpr.getLeftOperand()) and
+ y = getFullyConvertedLowerBounds(maxExpr.getRightOperand()) and
+ if x >= y then result = x else result = y
+ )
+ )
+ or
+ // ConditionalExpr (true branch)
+ exists(ConditionalExpr condExpr |
+ expr = condExpr and
+ // Use `boolConversionUpperBound` to determine whether the condition
+ // might evaluate to `true`.
+ boolConversionUpperBound(condExpr.getCondition().getFullyConverted()) = 1 and
+ result = getFullyConvertedLowerBounds(condExpr.getThen())
+ )
+ or
+ // ConditionalExpr (false branch)
+ exists(ConditionalExpr condExpr |
+ expr = condExpr and
+ // Use `boolConversionLowerBound` to determine whether the condition
+ // might evaluate to `false`.
+ boolConversionLowerBound(condExpr.getCondition().getFullyConverted()) = 0 and
+ result = getFullyConvertedLowerBounds(condExpr.getElse())
+ )
+ or
+ exists(AddExpr addExpr, float xLow, float yLow |
+ expr = addExpr and
+ xLow = getFullyConvertedLowerBounds(addExpr.getLeftOperand()) and
+ yLow = getFullyConvertedLowerBounds(addExpr.getRightOperand()) and
+ result = addRoundingDown(xLow, yLow)
+ )
+ or
+ exists(SubExpr subExpr, float xLow, float yHigh |
+ expr = subExpr and
+ xLow = getFullyConvertedLowerBounds(subExpr.getLeftOperand()) and
+ yHigh = getFullyConvertedUpperBounds(subExpr.getRightOperand()) and
+ result = addRoundingDown(xLow, -yHigh)
+ )
+ or
+ exists(UnsignedMulExpr mulExpr, float xLow, float yLow |
+ expr = mulExpr and
+ xLow = getFullyConvertedLowerBounds(mulExpr.getLeftOperand()) and
+ yLow = getFullyConvertedLowerBounds(mulExpr.getRightOperand()) and
+ result = xLow * yLow
+ )
+ or
+ exists(AssignExpr assign |
+ expr = assign and
+ result = getFullyConvertedLowerBounds(assign.getRValue())
+ )
+ or
+ exists(AssignAddExpr addExpr, float xLow, float yLow |
+ expr = addExpr and
+ xLow = getFullyConvertedLowerBounds(addExpr.getLValue()) and
+ yLow = getFullyConvertedLowerBounds(addExpr.getRValue()) and
+ result = addRoundingDown(xLow, yLow)
+ )
+ or
+ exists(AssignSubExpr subExpr, float xLow, float yHigh |
+ expr = subExpr and
+ xLow = getFullyConvertedLowerBounds(subExpr.getLValue()) and
+ yHigh = getFullyConvertedUpperBounds(subExpr.getRValue()) and
+ result = addRoundingDown(xLow, -yHigh)
+ )
+ or
+ exists(UnsignedAssignMulExpr mulExpr, float xLow, float yLow |
+ expr = mulExpr and
+ xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and
+ yLow = getFullyConvertedLowerBounds(mulExpr.getRValue()) and
+ result = xLow * yLow
+ )
+ or
+ exists(AssignMulByPositiveConstantExpr mulExpr, float xLow |
+ expr = mulExpr and
+ xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and
+ result = xLow * mulExpr.getConstant()
+ )
+ or
+ exists(AssignMulByNegativeConstantExpr mulExpr, float xHigh |
+ expr = mulExpr and
+ xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and
+ result = xHigh * mulExpr.getConstant()
+ )
+ or
+ exists(PrefixIncrExpr incrExpr, float xLow |
+ expr = incrExpr and
+ xLow = getFullyConvertedLowerBounds(incrExpr.getOperand()) and
+ result = xLow + 1
+ )
+ or
+ exists(PrefixDecrExpr decrExpr, float xLow |
+ expr = decrExpr and
+ xLow = getFullyConvertedLowerBounds(decrExpr.getOperand()) and
+ result = addRoundingDownSmall(xLow, -1)
+ )
+ or
+ // `PostfixIncrExpr` and `PostfixDecrExpr` return the value of their
+ // operand. The incrementing/decrementing behavior is handled in
+ // `getDefLowerBoundsImpl`.
+ exists(PostfixIncrExpr incrExpr |
+ expr = incrExpr and
+ result = getFullyConvertedLowerBounds(incrExpr.getOperand())
+ )
+ or
+ exists(PostfixDecrExpr decrExpr |
+ expr = decrExpr and
+ result = getFullyConvertedLowerBounds(decrExpr.getOperand())
+ )
+ or
+ exists(RemExpr remExpr | expr = remExpr |
+ // If both inputs are positive then the lower bound is zero.
+ result = 0
or
- exists(float rhsUB | rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand()) |
- result = -rhsUB
+ // If either input could be negative then the output could be
+ // negative. If so, the lower bound of `x%y` is `-abs(y)`, which is
+ // equal to `min(-y,y)`.
+ exists(float childLB |
+ childLB = getFullyConvertedLowerBounds(remExpr.getAnOperand()) and
+ not childLB >= 0
+ |
+ result = getFullyConvertedLowerBounds(remExpr.getRightOperand())
+ or
+ exists(float rhsUB | rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand()) |
+ result = -rhsUB
+ )
)
)
- )
- or
- exists(CommaExpr commaExpr |
- expr = commaExpr and
- result = getFullyConvertedLowerBounds(commaExpr.getRightOperand())
- )
- or
- exists(StmtExpr stmtExpr |
- expr = stmtExpr and
- result = getFullyConvertedLowerBounds(stmtExpr.getResultExpr())
- )
- or
- // If the conversion is to an arithmetic type then we just return the
- // lower bound of the child. We do not need to handle truncation and
- // overflow here, because that is done in `getTruncatedLowerBounds`.
- // Conversions to `bool` need to be handled specially because they test
- // whether the value of the expression is equal to 0.
- exists(Conversion convExpr | expr = convExpr |
- if convExpr.getUnspecifiedType() instanceof BoolType
- then result = boolConversionLowerBound(convExpr.getExpr())
- else result = getTruncatedLowerBounds(convExpr.getExpr())
- )
- or
- // Use SSA to get the lower bounds for a variable use.
- exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) |
- result = getDefLowerBounds(def, v)
- )
- or
- // unsigned `&` (tighter bounds may exist)
- exists(UnsignedBitwiseAndExpr andExpr |
- andExpr = expr and
- result = 0.0
- )
+ or
+ // If the conversion is to an arithmetic type then we just return the
+ // lower bound of the child. We do not need to handle truncation and
+ // overflow here, because that is done in `getTruncatedLowerBounds`.
+ // Conversions to `bool` need to be handled specially because they test
+ // whether the value of the expression is equal to 0.
+ exists(Conversion convExpr | expr = convExpr |
+ if convExpr.getUnspecifiedType() instanceof BoolType
+ then result = boolConversionLowerBound(convExpr.getExpr())
+ else result = getTruncatedLowerBounds(convExpr.getExpr())
+ )
+ or
+ // Use SSA to get the lower bounds for a variable use.
+ exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) |
+ result = getDefLowerBounds(def, v)
+ )
+ or
+ // unsigned `&` (tighter bounds may exist)
+ exists(UnsignedBitwiseAndExpr andExpr |
+ andExpr = expr and
+ result = 0.0
+ )
+ or
+ // `>>` by a constant
+ exists(RShiftExpr rsExpr, float left, int right |
+ rsExpr = expr and
+ left = getFullyConvertedLowerBounds(rsExpr.getLeftOperand()) and
+ right = getValue(rsExpr.getRightOperand().getFullyConverted()).toInt() and
+ result = safeFloor(left / 2.pow(right))
+ )
+ // Not explicitly modeled by a SimpleRangeAnalysisExpr
+ ) and
+ not expr instanceof SimpleRangeAnalysisExpr
or
- // `>>` by a constant
- exists(RShiftExpr rsExpr, float left, int right |
- rsExpr = expr and
- left = getFullyConvertedLowerBounds(rsExpr.getLeftOperand()) and
- right = rsExpr.getRightOperand().getValue().toInt() and
- result = safeFloor(left / 2.pow(right))
+ // A modeled expression for range analysis
+ exists(SimpleRangeAnalysisExpr rangeAnalysisExpr |
+ rangeAnalysisExpr = expr and
+ result = rangeAnalysisExpr.getLowerBounds()
)
}
/** Only to be called by `getTruncatedUpperBounds`. */
private float getUpperBoundsImpl(Expr expr) {
- exists(UnaryPlusExpr plusExpr |
- expr = plusExpr and
- result = getFullyConvertedUpperBounds(plusExpr.getOperand())
- )
- or
- exists(UnaryMinusExpr negateExpr, float xLow |
- expr = negateExpr and
- xLow = getFullyConvertedLowerBounds(negateExpr.getOperand()) and
- result = -xLow
- )
- or
- exists(MaxExpr maxExpr |
- expr = maxExpr and
- // Return the union of the upper bounds from both children.
- result = getFullyConvertedUpperBounds(maxExpr.getAnOperand())
- )
- or
- exists(MinExpr minExpr |
- expr = minExpr and
- // Compute the cross product of the bounds from both children. We are
- // using this mathematical property:
- //
- // min (maximum{X}, maximum{Y})
- // = maximum { min(x,y) | x in X, y in Y }
- exists(float x, float y |
- x = getFullyConvertedUpperBounds(minExpr.getLeftOperand()) and
- y = getFullyConvertedUpperBounds(minExpr.getRightOperand()) and
- if x <= y then result = x else result = y
+ (
+ exists(Expr operand, float operandHigh, float positive |
+ effectivelyMultipliesByPositive(expr, operand, positive) and
+ operandHigh = getFullyConvertedUpperBounds(operand) and
+ result = positive * operandHigh
)
- )
- or
- // ConditionalExpr (true branch)
- exists(ConditionalExpr condExpr |
- expr = condExpr and
- // Use `boolConversionUpperBound` to determine whether the condition
- // might evaluate to `true`.
- boolConversionUpperBound(condExpr.getCondition().getFullyConverted()) = 1 and
- result = getFullyConvertedUpperBounds(condExpr.getThen())
- )
- or
- // ConditionalExpr (false branch)
- exists(ConditionalExpr condExpr |
- expr = condExpr and
- // Use `boolConversionLowerBound` to determine whether the condition
- // might evaluate to `false`.
- boolConversionLowerBound(condExpr.getCondition().getFullyConverted()) = 0 and
- result = getFullyConvertedUpperBounds(condExpr.getElse())
- )
- or
- exists(AddExpr addExpr, float xHigh, float yHigh |
- expr = addExpr and
- xHigh = getFullyConvertedUpperBounds(addExpr.getLeftOperand()) and
- yHigh = getFullyConvertedUpperBounds(addExpr.getRightOperand()) and
- result = addRoundingUp(xHigh, yHigh)
- )
- or
- exists(SubExpr subExpr, float xHigh, float yLow |
- expr = subExpr and
- xHigh = getFullyConvertedUpperBounds(subExpr.getLeftOperand()) and
- yLow = getFullyConvertedLowerBounds(subExpr.getRightOperand()) and
- result = addRoundingUp(xHigh, -yLow)
- )
- or
- exists(AssignExpr assign |
- expr = assign and
- result = getFullyConvertedUpperBounds(assign.getRValue())
- )
- or
- exists(AssignAddExpr addExpr, float xHigh, float yHigh |
- expr = addExpr and
- xHigh = getFullyConvertedUpperBounds(addExpr.getLValue()) and
- yHigh = getFullyConvertedUpperBounds(addExpr.getRValue()) and
- result = addRoundingUp(xHigh, yHigh)
- )
- or
- exists(AssignSubExpr subExpr, float xHigh, float yLow |
- expr = subExpr and
- xHigh = getFullyConvertedUpperBounds(subExpr.getLValue()) and
- yLow = getFullyConvertedLowerBounds(subExpr.getRValue()) and
- result = addRoundingUp(xHigh, -yLow)
- )
- or
- exists(PrefixIncrExpr incrExpr, float xHigh |
- expr = incrExpr and
- xHigh = getFullyConvertedUpperBounds(incrExpr.getOperand()) and
- result = addRoundingUpSmall(xHigh, 1)
- )
- or
- exists(PrefixDecrExpr decrExpr, float xHigh |
- expr = decrExpr and
- xHigh = getFullyConvertedUpperBounds(decrExpr.getOperand()) and
- result = xHigh - 1
- )
- or
- // `PostfixIncrExpr` and `PostfixDecrExpr` return the value of their operand.
- // The incrementing/decrementing behavior is handled in
- // `getDefUpperBoundsImpl`.
- exists(PostfixIncrExpr incrExpr |
- expr = incrExpr and
- result = getFullyConvertedUpperBounds(incrExpr.getOperand())
- )
- or
- exists(PostfixDecrExpr decrExpr |
- expr = decrExpr and
- result = getFullyConvertedUpperBounds(decrExpr.getOperand())
- )
- or
- exists(RemExpr remExpr, float rhsUB |
- expr = remExpr and
- rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand())
- |
- result = rhsUB
- or
- // If the right hand side could be negative then we need to take its
- // absolute value. Since `abs(x) = max(-x,x)` this is equivalent to
- // adding `-rhsLB` to the set of upper bounds.
- exists(float rhsLB |
- rhsLB = getFullyConvertedLowerBounds(remExpr.getAnOperand()) and
- not rhsLB >= 0
+ or
+ exists(Expr operand, float operandLow, float negative |
+ effectivelyMultipliesByNegative(expr, operand, negative) and
+ operandLow = getFullyConvertedLowerBounds(operand) and
+ result = negative * operandLow
+ )
+ or
+ exists(MaxExpr maxExpr |
+ expr = maxExpr and
+ // Return the union of the upper bounds from both children.
+ result = getFullyConvertedUpperBounds(maxExpr.getAnOperand())
+ )
+ or
+ exists(MinExpr minExpr |
+ expr = minExpr and
+ // Compute the cross product of the bounds from both children. We are
+ // using this mathematical property:
+ //
+ // min (maximum{X}, maximum{Y})
+ // = maximum { min(x,y) | x in X, y in Y }
+ exists(float x, float y |
+ x = getFullyConvertedUpperBounds(minExpr.getLeftOperand()) and
+ y = getFullyConvertedUpperBounds(minExpr.getRightOperand()) and
+ if x <= y then result = x else result = y
+ )
+ )
+ or
+ // ConditionalExpr (true branch)
+ exists(ConditionalExpr condExpr |
+ expr = condExpr and
+ // Use `boolConversionUpperBound` to determine whether the condition
+ // might evaluate to `true`.
+ boolConversionUpperBound(condExpr.getCondition().getFullyConverted()) = 1 and
+ result = getFullyConvertedUpperBounds(condExpr.getThen())
+ )
+ or
+ // ConditionalExpr (false branch)
+ exists(ConditionalExpr condExpr |
+ expr = condExpr and
+ // Use `boolConversionLowerBound` to determine whether the condition
+ // might evaluate to `false`.
+ boolConversionLowerBound(condExpr.getCondition().getFullyConverted()) = 0 and
+ result = getFullyConvertedUpperBounds(condExpr.getElse())
+ )
+ or
+ exists(AddExpr addExpr, float xHigh, float yHigh |
+ expr = addExpr and
+ xHigh = getFullyConvertedUpperBounds(addExpr.getLeftOperand()) and
+ yHigh = getFullyConvertedUpperBounds(addExpr.getRightOperand()) and
+ result = addRoundingUp(xHigh, yHigh)
+ )
+ or
+ exists(SubExpr subExpr, float xHigh, float yLow |
+ expr = subExpr and
+ xHigh = getFullyConvertedUpperBounds(subExpr.getLeftOperand()) and
+ yLow = getFullyConvertedLowerBounds(subExpr.getRightOperand()) and
+ result = addRoundingUp(xHigh, -yLow)
+ )
+ or
+ exists(UnsignedMulExpr mulExpr, float xHigh, float yHigh |
+ expr = mulExpr and
+ xHigh = getFullyConvertedUpperBounds(mulExpr.getLeftOperand()) and
+ yHigh = getFullyConvertedUpperBounds(mulExpr.getRightOperand()) and
+ result = xHigh * yHigh
+ )
+ or
+ exists(AssignExpr assign |
+ expr = assign and
+ result = getFullyConvertedUpperBounds(assign.getRValue())
+ )
+ or
+ exists(AssignAddExpr addExpr, float xHigh, float yHigh |
+ expr = addExpr and
+ xHigh = getFullyConvertedUpperBounds(addExpr.getLValue()) and
+ yHigh = getFullyConvertedUpperBounds(addExpr.getRValue()) and
+ result = addRoundingUp(xHigh, yHigh)
+ )
+ or
+ exists(AssignSubExpr subExpr, float xHigh, float yLow |
+ expr = subExpr and
+ xHigh = getFullyConvertedUpperBounds(subExpr.getLValue()) and
+ yLow = getFullyConvertedLowerBounds(subExpr.getRValue()) and
+ result = addRoundingUp(xHigh, -yLow)
+ )
+ or
+ exists(UnsignedAssignMulExpr mulExpr, float xHigh, float yHigh |
+ expr = mulExpr and
+ xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and
+ yHigh = getFullyConvertedUpperBounds(mulExpr.getRValue()) and
+ result = xHigh * yHigh
+ )
+ or
+ exists(AssignMulByPositiveConstantExpr mulExpr, float xHigh |
+ expr = mulExpr and
+ xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and
+ result = xHigh * mulExpr.getConstant()
+ )
+ or
+ exists(AssignMulByNegativeConstantExpr mulExpr, float xLow |
+ expr = mulExpr and
+ xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and
+ result = xLow * mulExpr.getConstant()
+ )
+ or
+ exists(PrefixIncrExpr incrExpr, float xHigh |
+ expr = incrExpr and
+ xHigh = getFullyConvertedUpperBounds(incrExpr.getOperand()) and
+ result = addRoundingUpSmall(xHigh, 1)
+ )
+ or
+ exists(PrefixDecrExpr decrExpr, float xHigh |
+ expr = decrExpr and
+ xHigh = getFullyConvertedUpperBounds(decrExpr.getOperand()) and
+ result = xHigh - 1
+ )
+ or
+ // `PostfixIncrExpr` and `PostfixDecrExpr` return the value of their operand.
+ // The incrementing/decrementing behavior is handled in
+ // `getDefUpperBoundsImpl`.
+ exists(PostfixIncrExpr incrExpr |
+ expr = incrExpr and
+ result = getFullyConvertedUpperBounds(incrExpr.getOperand())
+ )
+ or
+ exists(PostfixDecrExpr decrExpr |
+ expr = decrExpr and
+ result = getFullyConvertedUpperBounds(decrExpr.getOperand())
+ )
+ or
+ exists(RemExpr remExpr, float rhsUB |
+ expr = remExpr and
+ rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand())
|
- result = -rhsLB
+ result = rhsUB
+ or
+ // If the right hand side could be negative then we need to take its
+ // absolute value. Since `abs(x) = max(-x,x)` this is equivalent to
+ // adding `-rhsLB` to the set of upper bounds.
+ exists(float rhsLB |
+ rhsLB = getFullyConvertedLowerBounds(remExpr.getAnOperand()) and
+ not rhsLB >= 0
+ |
+ result = -rhsLB
+ )
)
- )
- or
- exists(CommaExpr commaExpr |
- expr = commaExpr and
- result = getFullyConvertedUpperBounds(commaExpr.getRightOperand())
- )
- or
- exists(StmtExpr stmtExpr |
- expr = stmtExpr and
- result = getFullyConvertedUpperBounds(stmtExpr.getResultExpr())
- )
- or
- // If the conversion is to an arithmetic type then we just return the
- // upper bound of the child. We do not need to handle truncation and
- // overflow here, because that is done in `getTruncatedUpperBounds`.
- // Conversions to `bool` need to be handled specially because they test
- // whether the value of the expression is equal to 0.
- exists(Conversion convExpr | expr = convExpr |
- if convExpr.getUnspecifiedType() instanceof BoolType
- then result = boolConversionUpperBound(convExpr.getExpr())
- else result = getTruncatedUpperBounds(convExpr.getExpr())
- )
- or
- // Use SSA to get the upper bounds for a variable use.
- exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) |
- result = getDefUpperBounds(def, v)
- )
- or
- // unsigned `&` (tighter bounds may exist)
- exists(UnsignedBitwiseAndExpr andExpr, float left, float right |
- andExpr = expr and
- left = getFullyConvertedUpperBounds(andExpr.getLeftOperand()) and
- right = getFullyConvertedUpperBounds(andExpr.getRightOperand()) and
- result = left.minimum(right)
- )
+ or
+ // If the conversion is to an arithmetic type then we just return the
+ // upper bound of the child. We do not need to handle truncation and
+ // overflow here, because that is done in `getTruncatedUpperBounds`.
+ // Conversions to `bool` need to be handled specially because they test
+ // whether the value of the expression is equal to 0.
+ exists(Conversion convExpr | expr = convExpr |
+ if convExpr.getUnspecifiedType() instanceof BoolType
+ then result = boolConversionUpperBound(convExpr.getExpr())
+ else result = getTruncatedUpperBounds(convExpr.getExpr())
+ )
+ or
+ // Use SSA to get the upper bounds for a variable use.
+ exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) |
+ result = getDefUpperBounds(def, v)
+ )
+ or
+ // unsigned `&` (tighter bounds may exist)
+ exists(UnsignedBitwiseAndExpr andExpr, float left, float right |
+ andExpr = expr and
+ left = getFullyConvertedUpperBounds(andExpr.getLeftOperand()) and
+ right = getFullyConvertedUpperBounds(andExpr.getRightOperand()) and
+ result = left.minimum(right)
+ )
+ or
+ // `>>` by a constant
+ exists(RShiftExpr rsExpr, float left, int right |
+ rsExpr = expr and
+ left = getFullyConvertedUpperBounds(rsExpr.getLeftOperand()) and
+ right = getValue(rsExpr.getRightOperand().getFullyConverted()).toInt() and
+ result = safeFloor(left / 2.pow(right))
+ )
+ // Not explicitly modeled by a SimpleRangeAnalysisExpr
+ ) and
+ not expr instanceof SimpleRangeAnalysisExpr
or
- // `>>` by a constant
- exists(RShiftExpr rsExpr, float left, int right |
- rsExpr = expr and
- left = getFullyConvertedUpperBounds(rsExpr.getLeftOperand()) and
- right = rsExpr.getRightOperand().getValue().toInt() and
- result = safeFloor(left / 2.pow(right))
+ // A modeled expression for range analysis
+ exists(SimpleRangeAnalysisExpr rangeAnalysisExpr |
+ rangeAnalysisExpr = expr and
+ result = rangeAnalysisExpr.getUpperBounds()
)
}
@@ -988,9 +1196,7 @@ private float boolConversionUpperBound(Expr expr) {
* use the guard to deduce that the lower bound is 2 inside the block.
*/
private float getPhiLowerBounds(StackVariable v, RangeSsaDefinition phi) {
- exists(
- VariableAccess access, ComparisonOperation guard, boolean branch, float defLB, float guardLB
- |
+ exists(VariableAccess access, Expr guard, boolean branch, float defLB, float guardLB |
access = v.getAnAccess() and
phi.isGuardPhi(access, guard, branch) and
lowerBoundFromGuard(guard, access, guardLB, branch) and
@@ -1000,14 +1206,23 @@ private float getPhiLowerBounds(StackVariable v, RangeSsaDefinition phi) {
if guardLB > defLB then result = guardLB else result = defLB
)
or
+ exists(VariableAccess access, float neConstant, float lower |
+ isNEPhi(v, phi, access, neConstant) and
+ lower = getTruncatedLowerBounds(access) and
+ if lower = neConstant then result = lower + 1 else result = lower
+ )
+ or
+ exists(VariableAccess access |
+ isUnsupportedGuardPhi(v, phi, access) and
+ result = getTruncatedLowerBounds(access)
+ )
+ or
result = getDefLowerBounds(phi.getAPhiInput(v), v)
}
/** See comment for `getPhiLowerBounds`, above. */
private float getPhiUpperBounds(StackVariable v, RangeSsaDefinition phi) {
- exists(
- VariableAccess access, ComparisonOperation guard, boolean branch, float defUB, float guardUB
- |
+ exists(VariableAccess access, Expr guard, boolean branch, float defUB, float guardUB |
access = v.getAnAccess() and
phi.isGuardPhi(access, guard, branch) and
upperBoundFromGuard(guard, access, guardUB, branch) and
@@ -1017,6 +1232,17 @@ private float getPhiUpperBounds(StackVariable v, RangeSsaDefinition phi) {
if guardUB < defUB then result = guardUB else result = defUB
)
or
+ exists(VariableAccess access, float neConstant, float upper |
+ isNEPhi(v, phi, access, neConstant) and
+ upper = getTruncatedUpperBounds(access) and
+ if upper = neConstant then result = upper - 1 else result = upper
+ )
+ or
+ exists(VariableAccess access |
+ isUnsupportedGuardPhi(v, phi, access) and
+ result = getTruncatedUpperBounds(access)
+ )
+ or
result = getDefUpperBounds(phi.getAPhiInput(v), v)
}
@@ -1025,20 +1251,11 @@ private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) {
// Definitions with a defining value.
exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedLowerBounds(expr))
or
- exists(AssignAddExpr assignAdd, RangeSsaDefinition nextDef, float lhsLB, float rhsLB |
- def = assignAdd and
- assignAdd.getLValue() = nextDef.getAUse(v) and
- lhsLB = getDefLowerBounds(nextDef, v) and
- rhsLB = getFullyConvertedLowerBounds(assignAdd.getRValue()) and
- result = addRoundingDown(lhsLB, rhsLB)
- )
- or
- exists(AssignSubExpr assignSub, RangeSsaDefinition nextDef, float lhsLB, float rhsUB |
- def = assignSub and
- assignSub.getLValue() = nextDef.getAUse(v) and
- lhsLB = getDefLowerBounds(nextDef, v) and
- rhsUB = getFullyConvertedUpperBounds(assignSub.getRValue()) and
- result = addRoundingDown(lhsLB, -rhsUB)
+ // Assignment operations with a defining value
+ exists(AssignOperation assignOp |
+ def = assignOp and
+ assignOp.getLValue() = v.getAnAccess() and
+ result = getTruncatedLowerBounds(assignOp)
)
or
exists(IncrementOperation incr, float newLB |
@@ -1058,6 +1275,9 @@ private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) {
// Phi nodes.
result = getPhiLowerBounds(v, def)
or
+ // A modeled def for range analysis
+ result = def.(SimpleRangeAnalysisDefinition).getLowerBounds(v)
+ or
// Unanalyzable definitions.
unanalyzableDefBounds(def, v, result, _)
}
@@ -1067,20 +1287,11 @@ private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) {
// Definitions with a defining value.
exists(Expr expr | assignmentDef(def, v, expr) | result = getFullyConvertedUpperBounds(expr))
or
- exists(AssignAddExpr assignAdd, RangeSsaDefinition nextDef, float lhsUB, float rhsUB |
- def = assignAdd and
- assignAdd.getLValue() = nextDef.getAUse(v) and
- lhsUB = getDefUpperBounds(nextDef, v) and
- rhsUB = getFullyConvertedUpperBounds(assignAdd.getRValue()) and
- result = addRoundingUp(lhsUB, rhsUB)
- )
- or
- exists(AssignSubExpr assignSub, RangeSsaDefinition nextDef, float lhsUB, float rhsLB |
- def = assignSub and
- assignSub.getLValue() = nextDef.getAUse(v) and
- lhsUB = getDefUpperBounds(nextDef, v) and
- rhsLB = getFullyConvertedLowerBounds(assignSub.getRValue()) and
- result = addRoundingUp(lhsUB, -rhsLB)
+ // Assignment operations with a defining value
+ exists(AssignOperation assignOp |
+ def = assignOp and
+ assignOp.getLValue() = v.getAnAccess() and
+ result = getTruncatedUpperBounds(assignOp)
)
or
exists(IncrementOperation incr, float newUB |
@@ -1100,74 +1311,13 @@ private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) {
// Phi nodes.
result = getPhiUpperBounds(v, def)
or
+ // A modeled def for range analysis
+ result = def.(SimpleRangeAnalysisDefinition).getUpperBounds(v)
+ or
// Unanalyzable definitions.
unanalyzableDefBounds(def, v, _, result)
}
-/**
- * Get the lower bounds for a `RangeSsaDefinition`. Most of the work is
- * done by `getDefLowerBoundsImpl`, but this is where widening is applied
- * to prevent the analysis from exploding due to a recursive definition.
- */
-private float getDefLowerBounds(RangeSsaDefinition def, StackVariable v) {
- exists(float newLB, float truncatedLB |
- newLB = getDefLowerBoundsImpl(def, v) and
- if varMinVal(v) <= newLB and newLB <= varMaxVal(v)
- then truncatedLB = newLB
- else truncatedLB = varMinVal(v)
- |
- // Widening: check whether the new lower bound is from a source which
- // depends recursively on the current definition.
- if isRecursiveDef(def, v)
- then
- // The new lower bound is from a recursive source, so we round
- // down to one of a limited set of values to prevent the
- // recursion from exploding.
- result =
- max(float widenLB |
- widenLB = wideningLowerBounds(v.getUnspecifiedType()) and
- not widenLB > truncatedLB
- |
- widenLB
- )
- else result = truncatedLB
- )
- or
- // The definition might overflow positively and wrap. If so, the lower
- // bound is `typeLowerBound`.
- defMightOverflowPositively(def, v) and result = varMinVal(v)
-}
-
-/** See comment for `getDefLowerBounds`, above. */
-private float getDefUpperBounds(RangeSsaDefinition def, StackVariable v) {
- exists(float newUB, float truncatedUB |
- newUB = getDefUpperBoundsImpl(def, v) and
- if varMinVal(v) <= newUB and newUB <= varMaxVal(v)
- then truncatedUB = newUB
- else truncatedUB = varMaxVal(v)
- |
- // Widening: check whether the new upper bound is from a source which
- // depends recursively on the current definition.
- if isRecursiveDef(def, v)
- then
- // The new upper bound is from a recursive source, so we round
- // up to one of a fixed set of values to prevent the recursion
- // from exploding.
- result =
- min(float widenUB |
- widenUB = wideningUpperBounds(v.getUnspecifiedType()) and
- not widenUB < truncatedUB
- |
- widenUB
- )
- else result = truncatedUB
- )
- or
- // The definition might overflow negatively and wrap. If so, the upper
- // bound is `typeUpperBound`.
- defMightOverflowNegatively(def, v) and result = varMaxVal(v)
-}
-
/**
* Helper for `getDefLowerBounds` and `getDefUpperBounds`. Find the set of
* unanalyzable definitions (such as function parameters) and make their
@@ -1186,10 +1336,11 @@ private predicate unanalyzableDefBounds(RangeSsaDefinition def, StackVariable v,
* inferences about `v`.
*/
bindingset[guard, v, branch]
-predicate nonNanGuardedVariable(ComparisonOperation guard, VariableAccess v, boolean branch) {
- v.getUnspecifiedType() instanceof IntegralType
+predicate nonNanGuardedVariable(Expr guard, VariableAccess v, boolean branch) {
+ getVariableRangeType(v.getTarget()) instanceof IntegralType
or
- v.getUnspecifiedType() instanceof FloatingPointType and v instanceof NonNanVariableAccess
+ getVariableRangeType(v.getTarget()) instanceof FloatingPointType and
+ v instanceof NonNanVariableAccess
or
// The reason the following case is here is to ensure that when we say
// `if (x > 5) { ...then... } else { ...else... }`
@@ -1204,9 +1355,7 @@ predicate nonNanGuardedVariable(ComparisonOperation guard, VariableAccess v, boo
* predicate uses the bounds information for `r` to compute a lower bound
* for `v`.
*/
-private predicate lowerBoundFromGuard(
- ComparisonOperation guard, VariableAccess v, float lb, boolean branch
-) {
+private predicate lowerBoundFromGuard(Expr guard, VariableAccess v, float lb, boolean branch) {
exists(float childLB, RelationStrictness strictness |
boundFromGuard(guard, v, childLB, true, strictness, branch)
|
@@ -1214,7 +1363,7 @@ private predicate lowerBoundFromGuard(
then
if
strictness = Nonstrict() or
- not v.getUnspecifiedType() instanceof IntegralType
+ not getVariableRangeType(v.getTarget()) instanceof IntegralType
then lb = childLB
else lb = childLB + 1
else lb = varMinVal(v.getTarget())
@@ -1226,9 +1375,7 @@ private predicate lowerBoundFromGuard(
* predicate uses the bounds information for `r` to compute a upper bound
* for `v`.
*/
-private predicate upperBoundFromGuard(
- ComparisonOperation guard, VariableAccess v, float ub, boolean branch
-) {
+private predicate upperBoundFromGuard(Expr guard, VariableAccess v, float ub, boolean branch) {
exists(float childUB, RelationStrictness strictness |
boundFromGuard(guard, v, childUB, false, strictness, branch)
|
@@ -1236,7 +1383,7 @@ private predicate upperBoundFromGuard(
then
if
strictness = Nonstrict() or
- not v.getUnspecifiedType() instanceof IntegralType
+ not getVariableRangeType(v.getTarget()) instanceof IntegralType
then ub = childUB
else ub = childUB - 1
else ub = varMaxVal(v.getTarget())
@@ -1248,7 +1395,7 @@ private predicate upperBoundFromGuard(
* `linearBoundFromGuard`.
*/
private predicate boundFromGuard(
- ComparisonOperation guard, VariableAccess v, float boundValue, boolean isLowerBound,
+ Expr guard, VariableAccess v, float boundValue, boolean isLowerBound,
RelationStrictness strictness, boolean branch
) {
exists(float p, float q, float r, boolean isLB |
@@ -1261,6 +1408,15 @@ private predicate boundFromGuard(
or
p < 0 and isLowerBound = isLB.booleanNot()
)
+ or
+ // When `!e` is true, we know that `0 <= e <= 0`
+ exists(float p, float q, Expr e |
+ linearAccess(e, v, p, q) and
+ eqZeroWithNegate(guard, e, true, branch) and
+ boundValue = (0.0 - q) / p and
+ isLowerBound = [false, true] and
+ strictness = Nonstrict()
+ )
}
/**
@@ -1296,22 +1452,13 @@ private predicate linearBoundFromGuard(
// 1. x <= upperbound(RHS)
// 2. x >= lowerbound(RHS)
//
- // For x != RHS, we create trivial bounds:
- //
- // 1. x <= typeUpperBound(RHS.getUnspecifiedType())
- // 2. x >= typeLowerBound(RHS.getUnspecifiedType())
- //
- exists(Expr lhs, Expr rhs, boolean isEQ |
+ exists(Expr lhs, Expr rhs |
linearAccess(lhs, v, p, q) and
- eqOpWithSwapAndNegate(guard, lhs, rhs, isEQ, branch) and
+ eqOpWithSwapAndNegate(guard, lhs, rhs, true, branch) and
+ getBounds(rhs, boundValue, isLowerBound) and
strictness = Nonstrict()
- |
- // True branch
- isEQ = true and getBounds(rhs, boundValue, isLowerBound)
- or
- // False branch: set the bounds to the min/max for the type.
- isEQ = false and exprTypeBounds(rhs, boundValue, isLowerBound)
)
+ // x != RHS and !x are handled elsewhere
}
/** Utility for `linearBoundFromGuard`. */
@@ -1328,6 +1475,54 @@ private predicate exprTypeBounds(Expr expr, float boundValue, boolean isLowerBou
isLowerBound = false and boundValue = exprMaxVal(expr.getFullyConverted())
}
+/**
+ * Holds if `(v, phi)` ensures that `access` is not equal to `neConstant`. For
+ * example, the condition `if (x + 1 != 3)` ensures that `x` is not equal to 2.
+ * Only integral types are supported.
+ */
+private predicate isNEPhi(
+ Variable v, RangeSsaDefinition phi, VariableAccess access, float neConstant
+) {
+ exists(
+ ComparisonOperation cmp, boolean branch, Expr linearExpr, Expr rExpr, float p, float q, float r
+ |
+ access.getTarget() = v and
+ phi.isGuardPhi(access, cmp, branch) and
+ eqOpWithSwapAndNegate(cmp, linearExpr, rExpr, false, branch) and
+ v.getUnspecifiedType() instanceof IntegralOrEnumType and // Float `!=` is too imprecise
+ r = getValue(rExpr).toFloat() and
+ linearAccess(linearExpr, access, p, q) and
+ neConstant = (r - q) / p
+ )
+ or
+ exists(Expr op, boolean branch, Expr linearExpr, float p, float q |
+ access.getTarget() = v and
+ phi.isGuardPhi(access, op, branch) and
+ eqZeroWithNegate(op, linearExpr, false, branch) and
+ v.getUnspecifiedType() instanceof IntegralOrEnumType and // Float `!` is too imprecise
+ linearAccess(linearExpr, access, p, q) and
+ neConstant = (0.0 - q) / p
+ )
+}
+
+/**
+ * Holds if `(v, phi)` constrains the value of `access` but in a way that
+ * doesn't allow this library to constrain the upper or lower bounds of
+ * `access`. An example is `if (x != y)` if neither `x` nor `y` is a
+ * compile-time constant.
+ */
+private predicate isUnsupportedGuardPhi(Variable v, RangeSsaDefinition phi, VariableAccess access) {
+ exists(Expr cmp, boolean branch |
+ eqOpWithSwapAndNegate(cmp, _, _, false, branch)
+ or
+ eqZeroWithNegate(cmp, _, false, branch)
+ |
+ access.getTarget() = v and
+ phi.isGuardPhi(access, cmp, branch) and
+ not isNEPhi(v, phi, access, _)
+ )
+}
+
cached
private module SimpleRangeAnalysisCached {
/**
@@ -1480,3 +1675,89 @@ private module SimpleRangeAnalysisCached {
convertedExprMightOverflowPositively(expr)
}
}
+
+/**
+ * INTERNAL: do not use. This module contains utilities for use in the
+ * experimental `SimpleRangeAnalysisExpr` module.
+ */
+module SimpleRangeAnalysisInternal {
+ /**
+ * Gets the truncated lower bounds of the fully converted expression.
+ */
+ float getFullyConvertedLowerBounds(Expr expr) {
+ result = getTruncatedLowerBounds(expr.getFullyConverted())
+ }
+
+ /**
+ * Gets the truncated upper bounds of the fully converted expression.
+ */
+ float getFullyConvertedUpperBounds(Expr expr) {
+ result = getTruncatedUpperBounds(expr.getFullyConverted())
+ }
+
+ /**
+ * Get the lower bounds for a `RangeSsaDefinition`. Most of the work is
+ * done by `getDefLowerBoundsImpl`, but this is where widening is applied
+ * to prevent the analysis from exploding due to a recursive definition.
+ */
+ float getDefLowerBounds(RangeSsaDefinition def, StackVariable v) {
+ exists(float newLB, float truncatedLB |
+ newLB = getDefLowerBoundsImpl(def, v) and
+ if varMinVal(v) <= newLB and newLB <= varMaxVal(v)
+ then truncatedLB = newLB
+ else truncatedLB = varMinVal(v)
+ |
+ // Widening: check whether the new lower bound is from a source which
+ // depends recursively on the current definition.
+ if isRecursiveDef(def, v)
+ then
+ // The new lower bound is from a recursive source, so we round
+ // down to one of a limited set of values to prevent the
+ // recursion from exploding.
+ result =
+ max(float widenLB |
+ widenLB = wideningLowerBounds(getVariableRangeType(v)) and
+ not widenLB > truncatedLB
+ |
+ widenLB
+ )
+ else result = truncatedLB
+ )
+ or
+ // The definition might overflow positively and wrap. If so, the lower
+ // bound is `typeLowerBound`.
+ defMightOverflowPositively(def, v) and result = varMinVal(v)
+ }
+
+ /** See comment for `getDefLowerBounds`, above. */
+ float getDefUpperBounds(RangeSsaDefinition def, StackVariable v) {
+ exists(float newUB, float truncatedUB |
+ newUB = getDefUpperBoundsImpl(def, v) and
+ if varMinVal(v) <= newUB and newUB <= varMaxVal(v)
+ then truncatedUB = newUB
+ else truncatedUB = varMaxVal(v)
+ |
+ // Widening: check whether the new upper bound is from a source which
+ // depends recursively on the current definition.
+ if isRecursiveDef(def, v)
+ then
+ // The new upper bound is from a recursive source, so we round
+ // up to one of a fixed set of values to prevent the recursion
+ // from exploding.
+ result =
+ min(float widenUB |
+ widenUB = wideningUpperBounds(getVariableRangeType(v)) and
+ not widenUB < truncatedUB
+ |
+ widenUB
+ )
+ else result = truncatedUB
+ )
+ or
+ // The definition might overflow negatively and wrap. If so, the upper
+ // bound is `typeUpperBound`.
+ defMightOverflowNegatively(def, v) and result = varMaxVal(v)
+ }
+}
+
+private import SimpleRangeAnalysisInternal
diff --git a/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll
index 4c53f34c9367..f06b0180c231 100644
--- a/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/BufferWrite.qll
@@ -10,6 +10,7 @@ import semmle.code.cpp.commons.Alloc
import semmle.code.cpp.commons.Buffer
import semmle.code.cpp.commons.Scanf
import semmle.code.cpp.models.implementations.Strcat
+import semmle.code.cpp.models.implementations.Strcpy
/*
* --- BufferWrite framework ---
@@ -106,68 +107,19 @@ abstract class BufferWriteCall extends BufferWrite, FunctionCall { }
* A call to a variant of `strcpy`.
*/
class StrCopyBW extends BufferWriteCall {
- StrCopyBW() {
- exists(TopLevelFunction fn, string name | fn = getTarget() and name = fn.getName() |
- // strcpy(dst, src)
- name = "strcpy"
- or
- // wcscpy(dst, src)
- name = "wcscpy"
- or
- // _mbscpy(dst, src)
- name = "_mbscpy"
- or
- (
- name = "strcpy_s" or // strcpy_s(dst, max_amount, src)
- name = "wcscpy_s" or // wcscpy_s(dst, max_amount, src)
- name = "_mbscpy_s" // _mbscpy_s(dst, max_amount, src)
- ) and
- // exclude the 2-parameter template versions
- // that find the size of a fixed size destination buffer.
- fn.getNumberOfParameters() = 3
- or
- // strncpy(dst, src, max_amount)
- name = "strncpy"
- or
- // strncpy_l(dst, src, max_amount, locale)
- name = "strncpy_l"
- or
- // wcsncpy(dst, src, max_amount)
- name = "wcsncpy"
- or
- // _wcsncpy_l(dst, src, max_amount, locale)
- name = "_wcsncpy_l"
- or
- // _mbsncpy(dst, src, max_amount)
- name = "_mbsncpy"
- or
- // _mbsncpy_l(dst, src, max_amount, locale)
- name = "_mbsncpy_l"
- )
- }
+ StrcpyFunction f;
- int getParamSize() {
- exists(TopLevelFunction fn, string name |
- fn = getTarget() and
- name = fn.getName() and
- (
- if name.suffix(name.length() - 2) = "_s"
- then result = 1
- else
- if exists(name.indexOf("ncpy"))
- then result = 2
- else none()
- )
- )
- }
+ StrCopyBW() { getTarget() = f.(TopLevelFunction) }
- int getParamSrc() {
- exists(TopLevelFunction fn, string name |
- fn = getTarget() and
- name = fn.getName() and
- (if name.suffix(name.length() - 2) = "_s" then result = 2 else result = 1)
- )
- }
+ /**
+ * Gets the index of the parameter that is the maximum size of the copy (in characters).
+ */
+ int getParamSize() { result = f.getParamSize() }
+
+ /**
+ * Gets the index of the parameter that is the source of the copy.
+ */
+ int getParamSrc() { result = f.getParamSrc() }
override Type getBufferType() {
result = this.getTarget().getParameter(getParamSrc()).getUnspecifiedType()
@@ -175,7 +127,7 @@ class StrCopyBW extends BufferWriteCall {
override Expr getASource() { result = getArgument(getParamSrc()) }
- override Expr getDest() { result = getArgument(0) }
+ override Expr getDest() { result = getArgument(f.getParamDest()) }
override predicate hasExplicitLimit() { exists(getParamSize()) }
@@ -192,11 +144,19 @@ class StrCopyBW extends BufferWriteCall {
* A call to a variant of `strcat`.
*/
class StrCatBW extends BufferWriteCall {
- StrCatBW() { exists(TopLevelFunction fn | fn = getTarget() and fn instanceof StrcatFunction) }
+ StrcatFunction f;
+
+ StrCatBW() { getTarget() = f.(TopLevelFunction) }
- int getParamSize() { if exists(getArgument(2)) then result = 2 else none() }
+ /**
+ * Gets the index of the parameter that is the maximum size of the copy (in characters).
+ */
+ int getParamSize() { result = f.getParamSize() }
- int getParamSrc() { result = 1 }
+ /**
+ * Gets the index of the parameter that is the source of the copy.
+ */
+ int getParamSrc() { result = f.getParamSrc() }
override Type getBufferType() {
result = this.getTarget().getParameter(getParamSrc()).getUnspecifiedType()
@@ -204,7 +164,7 @@ class StrCatBW extends BufferWriteCall {
override Expr getASource() { result = getArgument(getParamSrc()) }
- override Expr getDest() { result = getArgument(0) }
+ override Expr getDest() { result = getArgument(f.getParamDest()) }
override predicate hasExplicitLimit() { exists(getParamSize()) }
@@ -221,8 +181,10 @@ class StrCatBW extends BufferWriteCall {
* A call to a variant of `sprintf`.
*/
class SprintfBW extends BufferWriteCall {
+ FormattingFunction f;
+
SprintfBW() {
- exists(TopLevelFunction fn, string name | fn = getTarget() and name = fn.getName() |
+ exists(string name | f = getTarget().(TopLevelFunction) and name = f.getName() |
/*
* C sprintf variants:
*/
@@ -258,10 +220,7 @@ class SprintfBW extends BufferWriteCall {
}
override Type getBufferType() {
- exists(FormattingFunction f |
- f = this.getTarget() and
- result = f.getParameter(f.getFormatParameterIndex()).getUnspecifiedType()
- )
+ result = f.getParameter(f.getFormatParameterIndex()).getUnspecifiedType()
}
override Expr getASource() {
@@ -270,7 +229,7 @@ class SprintfBW extends BufferWriteCall {
result = this.(FormattingFunctionCall).getFormatArgument(_)
}
- override Expr getDest() { result = getArgument(0) }
+ override Expr getDest() { result = getArgument(f.getOutputParameterIndex()) }
override int getMaxData() {
exists(FormatLiteral fl |
@@ -349,6 +308,9 @@ class SnprintfBW extends BufferWriteCall {
)
}
+ /**
+ * Gets the index of the parameter that is the size of the destination (in characters).
+ */
int getParamSize() { result = 1 }
override Type getBufferType() {
@@ -392,13 +354,15 @@ class SnprintfBW extends BufferWriteCall {
*/
class GetsBW extends BufferWriteCall {
GetsBW() {
- exists(TopLevelFunction fn, string name | fn = getTarget() and name = fn.getName() |
- name = "gets" or // gets(dst)
- name = "fgets" or // fgets(dst, max_amount, src_stream)
- name = "fgetws" // fgetws(dst, max_amount, src_stream)
- )
+ getTarget().(TopLevelFunction).getName() =
+ ["gets", // gets(dst)
+ "fgets", // fgets(dst, max_amount, src_stream)
+ "fgetws"] // fgetws(dst, max_amount, src_stream)
}
+ /**
+ * Gets the index of the parameter that is the maximum number of characters to be read.
+ */
int getParamSize() { if exists(getArgument(1)) then result = 1 else none() }
override Type getBufferType() { result = this.getTarget().getParameter(0).getUnspecifiedType() }
@@ -434,6 +398,9 @@ class ScanfBW extends BufferWrite {
)
}
+ /**
+ * Gets the index of the parameter that is the first format argument.
+ */
int getParamArgs() {
exists(FunctionCall fc |
this = fc.getArgument(_) and
diff --git a/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll b/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll
index 48fb60442c15..5a24184e1a22 100644
--- a/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll
@@ -2,21 +2,44 @@
import cpp
import semmle.code.cpp.security.FunctionWithWrappers
+import semmle.code.cpp.models.interfaces.SideEffect
/**
* A function for running a command using a command interpreter.
*/
-class SystemFunction extends FunctionWithWrappers {
+class SystemFunction extends FunctionWithWrappers, ArrayFunction, AliasFunction, SideEffectFunction {
SystemFunction() {
- hasGlobalOrStdName("system") or
- hasGlobalName("popen") or
+ hasGlobalOrStdName("system") or // system(command)
+ hasGlobalName("popen") or // popen(command, mode)
// Windows variants
- hasGlobalName("_popen") or
- hasGlobalName("_wpopen") or
- hasGlobalName("_wsystem")
+ hasGlobalName("_popen") or // _popen(command, mode)
+ hasGlobalName("_wpopen") or // _wpopen(command, mode)
+ hasGlobalName("_wsystem") // _wsystem(command)
}
override predicate interestingArg(int arg) { arg = 0 }
+
+ override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 or bufParam = 1 }
+
+ override predicate hasArrayInput(int bufParam) { bufParam = 0 or bufParam = 1 }
+
+ override predicate parameterNeverEscapes(int index) { index = 0 or index = 1 }
+
+ override predicate parameterEscapesOnlyViaReturn(int index) { none() }
+
+ override predicate parameterIsAlwaysReturned(int index) { none() }
+
+ override predicate hasOnlySpecificReadSideEffects() { any() }
+
+ override predicate hasOnlySpecificWriteSideEffects() {
+ hasGlobalOrStdName("system") or
+ hasGlobalName("_wsystem")
+ }
+
+ override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
+ (i = 0 or i = 1) and
+ buffer = true
+ }
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll
index 61d646733148..d8b2d44c923b 100644
--- a/cpp/ql/src/semmle/code/cpp/security/Encryption.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/Encryption.qll
@@ -1,8 +1,13 @@
-// Common predicates relating to encryption in C and C++
+/**
+ * Provides predicates relating to encryption in C and C++.
+ */
+
import cpp
-/** A blacklist of algorithms that are known to be insecure */
-string algorithmBlacklist() {
+/**
+ * Gets the name of an algorithm that is known to be insecure.
+ */
+string getAnInsecureAlgorithmName() {
result = "DES" or
result = "RC2" or
result = "RC4" or
@@ -10,29 +15,36 @@ string algorithmBlacklist() {
result = "ARCFOUR" // a variant of RC4
}
-// these are only bad if they're being used for encryption, and it's
-// hard to know when that's happening
-string hashAlgorithmBlacklist() {
+/**
+ * Gets the name of a hash algorithm that is insecure if it is being used for
+ * encryption (but it is hard to know when that is happening).
+ */
+string getAnInsecureHashAlgorithmName() {
result = "SHA1" or
result = "MD5"
}
-/** A regex for matching strings that look like they contain a blacklisted algorithm */
-string algorithmBlacklistRegex() {
+/**
+ * Gets the regular expression used for matching strings that look like they
+ * contain an algorithm that is known to be insecure.
+ */
+string getInsecureAlgorithmRegex() {
result =
// algorithms usually appear in names surrounded by characters that are not
// alphabetical characters in the same case. This handles the upper and lower
// case cases
- "(^|.*[^A-Z])(" + strictconcat(algorithmBlacklist(), "|") + ")([^A-Z].*|$)" + "|" +
+ "(^|.*[^A-Z])(" + strictconcat(getAnInsecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" +
// for lowercase, we want to be careful to avoid being confused by camelCase
// hence we require two preceding uppercase letters to be sure of a case switch,
// or a preceding non-alphabetic character
- "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(algorithmBlacklist().toLowerCase(), "|") +
+ "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getAnInsecureAlgorithmName().toLowerCase(), "|") +
")([^a-z].*|$)"
}
-/** A whitelist of algorithms that are known to be secure */
-string algorithmWhitelist() {
+/**
+ * Gets the name of an algorithm that is known to be secure.
+ */
+string getASecureAlgorithmName() {
result = "RSA" or
result = "SHA256" or
result = "CCM" or
@@ -42,17 +54,47 @@ string algorithmWhitelist() {
result = "ECIES"
}
-/** A regex for matching strings that look like they contain a whitelisted algorithm */
-string algorithmWhitelistRegex() {
- // The implementation of this is a duplicate of algorithmBlacklistRegex, as it isn't
- // possible to have string -> string functions at the moment
- // algorithms usually appear in names surrounded by characters that are not
- // alphabetical characters in the same case. This handles the upper and lower
- // case cases
- result = "(^|.*[^A-Z])" + algorithmWhitelist() + "([^A-Z].*|$)"
- or
- // for lowercase, we want to be careful to avoid being confused by camelCase
- // hence we require two preceding uppercase letters to be sure of a case switch,
- // or a preceding non-alphabetic character
- result = "(^|.*[A-Z]{2}|.*[^a-zA-Z])" + algorithmWhitelist().toLowerCase() + "([^a-z].*|$)"
+/**
+ * Gets a regular expression for matching strings that look like they
+ * contain an algorithm that is known to be secure.
+ */
+string getSecureAlgorithmRegex() {
+ result =
+ // algorithms usually appear in names surrounded by characters that are not
+ // alphabetical characters in the same case. This handles the upper and lower
+ // case cases
+ "(^|.*[^A-Z])(" + strictconcat(getASecureAlgorithmName(), "|") + ")([^A-Z].*|$)" + "|" +
+ // for lowercase, we want to be careful to avoid being confused by camelCase
+ // hence we require two preceding uppercase letters to be sure of a case
+ // switch, or a preceding non-alphabetic character
+ "(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(getASecureAlgorithmName().toLowerCase(), "|") +
+ ")([^a-z].*|$)"
}
+
+/**
+ * DEPRECATED: Terminology has been updated. Use `getAnInsecureAlgorithmName()`
+ * instead.
+ */
+deprecated string algorithmBlacklist() { result = getAnInsecureAlgorithmName() }
+
+/**
+ * DEPRECATED: Terminology has been updated. Use
+ * `getAnInsecureHashAlgorithmName()` instead.
+ */
+deprecated string hashAlgorithmBlacklist() { result = getAnInsecureHashAlgorithmName() }
+
+/**
+ * DEPRECATED: Terminology has been updated. Use `getInsecureAlgorithmRegex()` instead.
+ */
+deprecated string algorithmBlacklistRegex() { result = getInsecureAlgorithmRegex() }
+
+/**
+ * DEPRECATED: Terminology has been updated. Use `getASecureAlgorithmName()`
+ * instead.
+ */
+deprecated string algorithmWhitelist() { result = getASecureAlgorithmName() }
+
+/**
+ * DEPRECATED: Terminology has been updated. Use `getSecureAlgorithmRegex()` instead.
+ */
+deprecated string algorithmWhitelistRegex() { result = getSecureAlgorithmRegex() }
diff --git a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll
index 219f3d0a75b7..9f63ce99c0b8 100644
--- a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll
@@ -1,13 +1,23 @@
+/**
+ * Provides classes for modeling writing of data to files through various standard mechanisms such as `fprintf`, `fwrite` and `operator<<`.
+ */
+
import cpp
/**
- * A function call that writes to a file
+ * A function call that writes to a file.
*/
class FileWrite extends Expr {
FileWrite() { fileWrite(this, _, _) }
+ /**
+ * Gets a source expression of this write.
+ */
Expr getASource() { fileWrite(this, result, _) }
+ /**
+ * Gets the expression for the object being written to.
+ */
Expr getDest() { fileWrite(this, _, result) }
}
@@ -44,17 +54,17 @@ class BasicOStreamCall extends FunctionCall {
*/
abstract class ChainedOutputCall extends BasicOStreamCall {
/**
- * The source expression of this output.
+ * Gets the source expression of this output.
*/
abstract Expr getSource();
/**
- * The immediate destination expression of this output.
+ * Gets the immediate destination expression of this output.
*/
abstract Expr getDest();
/**
- * The destination at the far left-hand end of the output chain.
+ * Gets the destination at the far left-hand end of the output chain.
*/
Expr getEndDest() {
// recurse into the destination
@@ -108,14 +118,12 @@ class WriteFunctionCall extends ChainedOutputCall {
}
/**
- * Whether the function call is a call to << that eventually starts at the given file stream.
+ * Whether the function call is a call to `operator<<` or a similar function, that eventually starts at the given file stream.
*/
private predicate fileStreamChain(ChainedOutputCall out, Expr source, Expr dest) {
source = out.getSource() and
dest = out.getEndDest() and
- exists(string nme | nme = "basic_ofstream" or nme = "basic_fstream" |
- dest.getUnderlyingType().(Class).getSimpleName() = nme
- )
+ dest.getUnderlyingType().(Class).getSimpleName() = ["basic_ofstream", "basic_fstream"]
}
/**
@@ -129,15 +137,7 @@ private predicate fileWrite(Call write, Expr source, Expr dest) {
// named functions
name = "fwrite" and s = 0 and d = 3
or
- (
- name = "fputs" or
- name = "fputws" or
- name = "fputc" or
- name = "fputwc" or
- name = "putc" or
- name = "putwc" or
- name = "putw"
- ) and
+ name = ["fputs", "fputws", "fputc", "fputwc", "putc", "putwc", "putw"] and
s = 0 and
d = 1
)
diff --git a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll
index 23dda0ddb1e9..5451011b3511 100644
--- a/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/FunctionWithWrappers.qll
@@ -1,3 +1,20 @@
+/**
+ * Provides predicates for identifying functions that wrap other functions,
+ * passing the same arguments from the outer call into the inner call. In the
+ * following example `MyMalloc` wraps a call to `malloc`, passing in the `size`
+ * parameter:
+ * ```
+ * void *MyMalloc(size_t size)
+ * {
+ * void *ptr = malloc(size);
+ *
+ * // ... additional logic?
+ *
+ * return ptr;
+ * }
+ * ```
+ */
+
import cpp
import PrintfLike
private import TaintTracking
@@ -152,6 +169,9 @@ abstract class FunctionWithWrappers extends Function {
}
}
+/**
+ * A `printf`-like formatting function.
+ */
class PrintfLikeFunction extends FunctionWithWrappers {
PrintfLikeFunction() { printfLikeFunction(this, _) }
diff --git a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll
index 06abfdb454da..97635d92dbd3 100644
--- a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll
@@ -1,12 +1,19 @@
+/**
+ * Provides classes for modeling output to standard output / standard error through various mechanisms such as `printf`, `puts` and `operator<<`.
+ */
+
import cpp
import FileWrite
/**
- * A function call that writes to standard output or standard error
+ * A function call that writes to standard output or standard error.
*/
class OutputWrite extends Expr {
OutputWrite() { outputWrite(this, _) }
+ /**
+ * Gets a source expression for this output.
+ */
Expr getASource() { outputWrite(this, result) }
}
@@ -41,15 +48,12 @@ private predicate outputFile(Expr e) {
name = e.(VariableAccess).getTarget().(GlobalVariable).toString() or
name = e.findRootCause().(Macro).getName()
) and
- (
- name = "stdout" or
- name = "stderr"
- )
+ name = ["stdout", "stderr"]
)
}
/**
- * is the function call a write to standard output or standard error from 'source'
+ * Holds if the function call is a write to standard output or standard error from 'source'.
*/
private predicate outputWrite(Expr write, Expr source) {
exists(Function f, int arg |
diff --git a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll
index 59993954cc8d..e7ad1c559e63 100644
--- a/cpp/ql/src/semmle/code/cpp/security/Overflow.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/Overflow.qll
@@ -1,11 +1,14 @@
+/**
+ * Provides predicates for reasoning about when the value of an expression is
+ * guarded by an operation such as `<`, which confines its range.
+ */
+
import cpp
import semmle.code.cpp.controlflow.Dominance
-/*
- * Guarding
+/**
+ * Holds if the value of `use` is guarded using `abs`.
*/
-
-/** is the size of this use guarded using 'abs'? */
predicate guardedAbs(Operation e, Expr use) {
exists(FunctionCall fc | fc.getTarget().getName() = "abs" |
fc.getArgument(0).getAChild*() = use and
@@ -13,7 +16,10 @@ predicate guardedAbs(Operation e, Expr use) {
)
}
-/** This is `BasicBlock.getNode`, restricted to `Stmt` for performance. */
+/**
+ * Gets the position of `stmt` in basic block `block` (this is a thin layer
+ * over `BasicBlock.getNode`, intended to improve performance).
+ */
pragma[noinline]
private int getStmtIndexInBlock(BasicBlock block, Stmt stmt) { block.getNode(result) = stmt }
@@ -30,7 +36,9 @@ private predicate stmtDominates(Stmt dominator, Stmt dominated) {
bbStrictlyDominates(dominator.getBasicBlock(), dominated.getBasicBlock())
}
-/** is the size of this use guarded to be less than something? */
+/**
+ * Holds if the value of `use` is guarded to be less than something.
+ */
pragma[nomagic]
predicate guardedLesser(Operation e, Expr use) {
exists(IfStmt c, RelationalOperation guard |
@@ -54,7 +62,9 @@ predicate guardedLesser(Operation e, Expr use) {
guardedAbs(e, use)
}
-/** is the size of this use guarded to be greater than something? */
+/**
+ * Holds if the value of `use` is guarded to be greater than something.
+ */
pragma[nomagic]
predicate guardedGreater(Operation e, Expr use) {
exists(IfStmt c, RelationalOperation guard |
@@ -78,10 +88,14 @@ predicate guardedGreater(Operation e, Expr use) {
guardedAbs(e, use)
}
-/** a use of a given variable */
+/**
+ * Gets a use of a given variable `v`.
+ */
VariableAccess varUse(LocalScopeVariable v) { result = v.getAnAccess() }
-/** is e not guarded against overflow by use? */
+/**
+ * Holds if `e` is not guarded against overflow by `use`.
+ */
predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
use = e.getAnOperand() and
exists(LocalScopeVariable v | use.getTarget() = v |
@@ -100,7 +114,9 @@ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
)
}
-/** is e not guarded against underflow by use? */
+/**
+ * Holds if `e` is not guarded against underflow by `use`.
+ */
predicate missingGuardAgainstUnderflow(Operation e, VariableAccess use) {
use = e.getAnOperand() and
exists(LocalScopeVariable v | use.getTarget() = v |
diff --git a/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll b/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll
index 47d0ca3aa930..197a332aa1a7 100644
--- a/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/PrintfLike.qll
@@ -1,6 +1,18 @@
+/**
+ * Provides a predicate for identifying formatting functions like `printf`.
+ *
+ * Consider using the newer model in
+ * `semmle.code.cpp.models.interfaces.FormattingFunction` directly instead of
+ * this library.
+ */
+
import semmle.code.cpp.commons.Printf
import external.ExternalArtifact
+/**
+ * Holds if `func` is a `printf`-like formatting function and `formatArg` is
+ * the index of the format string argument.
+ */
predicate printfLikeFunction(Function func, int formatArg) {
formatArg = func.(FormattingFunction).getFormatParameterIndex() and
not func instanceof UserDefinedFormattingFunction
diff --git a/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll b/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll
index 8d35d80e613a..64babe419c36 100644
--- a/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/SecurityOptions.qll
@@ -1,4 +1,4 @@
-/*
+/**
* Security pack options.
*
* see https://semmle.com/wiki/display/SD/_Configuring+SecurityOptions+for+your+code+base
@@ -9,6 +9,10 @@
import semmle.code.cpp.security.Security
+/**
+ * This class overrides `SecurityOptions` and can be used to add project
+ * specific customization.
+ */
class CustomSecurityOptions extends SecurityOptions {
override predicate sqlArgument(string function, int arg) {
SecurityOptions.super.sqlArgument(function, arg)
diff --git a/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll b/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll
index 25d68fde7162..553cc98351cb 100644
--- a/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/SensitiveExprs.qll
@@ -1,5 +1,14 @@
+/**
+ * Provides classes for heuristically identifying variables and functions that
+ * might contain or return a password or other sensitive information.
+ */
+
import cpp
+/**
+ * Holds if the name `s` suggests something might contain or return a password
+ * or other sensitive information.
+ */
bindingset[s]
private predicate suspicious(string s) {
(
@@ -16,14 +25,23 @@ private predicate suspicious(string s) {
)
}
+/**
+ * A variable that might contain a password or other sensitive information.
+ */
class SensitiveVariable extends Variable {
SensitiveVariable() { suspicious(getName().toLowerCase()) }
}
+/**
+ * A function that might return a password or other sensitive information.
+ */
class SensitiveFunction extends Function {
SensitiveFunction() { suspicious(getName().toLowerCase()) }
}
+/**
+ * An expression whose value might be a password or other sensitive information.
+ */
class SensitiveExpr extends Expr {
SensitiveExpr() {
this.(VariableAccess).getTarget() instanceof SensitiveVariable or
diff --git a/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll
index 06cf4c456ce1..4b206d984dc4 100644
--- a/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/TaintTrackingImpl.qll
@@ -252,11 +252,10 @@ private predicate insideFunctionValueMoveTo(Element src, Element dest) {
copyValueBetweenArguments(c.getTarget(), sourceArg, destArg) and
// Only consider copies from `printf`-like functions if the format is a string
(
- exists(FormattingFunctionCall ffc, FormatLiteral format, string argFormat |
+ exists(FormattingFunctionCall ffc, FormatLiteral format |
ffc = c and
format = ffc.getFormat() and
- format.getConversionChar(sourceArg - ffc.getTarget().getNumberOfParameters()) = argFormat and
- (argFormat = "s" or argFormat = "S")
+ format.getConversionChar(sourceArg - ffc.getTarget().getNumberOfParameters()) = ["s", "S"]
)
or
not exists(FormatLiteral fl | fl = c.(FormattingFunctionCall).getFormat())
@@ -273,12 +272,12 @@ private predicate insideFunctionValueMoveTo(Element src, Element dest) {
dest = c
)
or
- exists(FormattingFunctionCall formattingSend, int arg, FormatLiteral format, string argFormat |
+ exists(FormattingFunctionCall formattingSend, int arg, FormatLiteral format |
dest = formattingSend and
formattingSend.getArgument(arg) = src and
format = formattingSend.getFormat() and
- format.getConversionChar(arg - formattingSend.getTarget().getNumberOfParameters()) = argFormat and
- (argFormat = "s" or argFormat = "S" or argFormat = "@")
+ format.getConversionChar(arg - formattingSend.getTarget().getNumberOfParameters()) =
+ ["s", "S", "@"]
)
or
// Expressions computed from tainted data are also tainted
diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Block.qll b/cpp/ql/src/semmle/code/cpp/stmts/Block.qll
index 4f69f52caf40..3bebc660456f 100644
--- a/cpp/ql/src/semmle/code/cpp/stmts/Block.qll
+++ b/cpp/ql/src/semmle/code/cpp/stmts/Block.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides a class to model C/C++ block statements, enclosed by `{` and `}`.
+ */
+
import semmle.code.cpp.Element
import semmle.code.cpp.stmts.Stmt
@@ -13,8 +17,8 @@ import semmle.code.cpp.stmts.Stmt
* }
* ```
*/
-class Block extends Stmt, @stmt_block {
- override string getCanonicalQLClass() { result = "Block" }
+class BlockStmt extends Stmt, @stmt_block {
+ override string getAPrimaryQlClass() { result = "BlockStmt" }
/**
* Gets a child declaration of this block.
@@ -72,8 +76,8 @@ class Block extends Stmt, @stmt_block {
* the result is the expression statement `a = b`.
*/
Stmt getLastStmtIn() {
- if getLastStmt() instanceof Block
- then result = getLastStmt().(Block).getLastStmtIn()
+ if getLastStmt() instanceof BlockStmt
+ then result = getLastStmt().(BlockStmt).getLastStmtIn()
else result = getLastStmt()
}
@@ -122,3 +126,9 @@ class Block extends Stmt, @stmt_block {
override predicate mayBeGloballyImpure() { this.getAStmt().mayBeGloballyImpure() }
}
+
+/**
+ * DEPRECATED: This is now called `BlockStmt` to avoid confusion with
+ * `BasicBlock`.
+ */
+deprecated class Block = BlockStmt;
diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
index 1640bee0f35d..08840f2c5054 100644
--- a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
+++ b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
@@ -1,3 +1,7 @@
+/**
+ * Provides a hierarchy of classes for modeling C/C++ statements.
+ */
+
import semmle.code.cpp.Element
private import semmle.code.cpp.Enclosing
private import semmle.code.cpp.internal.ResolveClass
@@ -21,10 +25,10 @@ class Stmt extends StmtParent, @stmt {
/**
* Gets the nearest enclosing block of this statement in the source, if any.
*/
- Block getEnclosingBlock() {
+ BlockStmt getEnclosingBlock() {
if
- getParentStmt() instanceof Block and
- not getParentStmt().(Block).getLocation() instanceof UnknownLocation
+ getParentStmt() instanceof BlockStmt and
+ not getParentStmt().(BlockStmt).getLocation() instanceof UnknownLocation
then result = getParentStmt()
else result = getParentStmt().getEnclosingBlock()
}
@@ -49,7 +53,7 @@ class Stmt extends StmtParent, @stmt {
* to trace the flow of control instead.
*/
Stmt getFollowingStmt() {
- exists(Block b, int i |
+ exists(BlockStmt b, int i |
this = b.getStmt(i) and
result = b.getStmt(i + 1)
)
@@ -133,12 +137,14 @@ class Stmt extends StmtParent, @stmt {
predicate isCompilerGenerated() { compgenerated(underlyingElement(this)) }
}
+private class TStmtParent = @stmt or @expr;
+
/**
* An element that is the parent of a statement in the C/C++ AST.
*
* This is normally a statement, but may be a `StmtExpr`.
*/
-abstract class StmtParent extends ControlFlowNode { }
+class StmtParent extends ControlFlowNode, TStmtParent { }
/**
* A C/C++ 'expression' statement.
@@ -150,7 +156,7 @@ abstract class StmtParent extends ControlFlowNode { }
* is an assignment expression inside an 'expression' statement.
*/
class ExprStmt extends Stmt, @stmt_expr {
- override string getCanonicalQLClass() { result = "ExprStmt" }
+ override string getAPrimaryQlClass() { result = "ExprStmt" }
/**
* Gets the expression of this 'expression' statement.
@@ -175,28 +181,32 @@ class ExprStmt extends Stmt, @stmt_expr {
}
}
+private class TControlStructure = TConditionalStmt or TLoop;
+
/**
* A C/C++ control structure, that is, either a conditional statement or
* a loop.
*/
-abstract class ControlStructure extends Stmt {
+class ControlStructure extends Stmt, TControlStructure {
/**
* Gets the controlling expression of this control structure.
*
* This is the condition of 'if' statements and loops, and the
* switched expression for 'switch' statements.
*/
- abstract Expr getControllingExpr();
+ Expr getControllingExpr() { none() } // overridden by subclasses
/** Gets a child declaration of this scope. */
Declaration getADeclaration() { none() }
}
+private class TConditionalStmt = @stmt_if or @stmt_constexpr_if or @stmt_switch;
+
/**
* A C/C++ conditional statement, that is, either an 'if' statement or a
* 'switch' statement.
*/
-abstract class ConditionalStmt extends ControlStructure { }
+class ConditionalStmt extends ControlStructure, TConditionalStmt { }
/**
* A C/C++ 'if' statement. For example, the `if` statement in the following
@@ -208,7 +218,7 @@ abstract class ConditionalStmt extends ControlStructure { }
* ```
*/
class IfStmt extends ConditionalStmt, @stmt_if {
- override string getCanonicalQLClass() { result = "IfStmt" }
+ override string getAPrimaryQlClass() { result = "IfStmt" }
/**
* Gets the condition expression of this 'if' statement.
@@ -230,7 +240,7 @@ class IfStmt extends ConditionalStmt, @stmt_if {
* ```
* if (b) { x = 1; }
* ```
- * the result is the `Block` `{ x = 1; }`.
+ * the result is the `BlockStmt` `{ x = 1; }`.
*/
Stmt getThen() { if_then(underlyingElement(this), unresolveElement(result)) }
@@ -241,7 +251,7 @@ class IfStmt extends ConditionalStmt, @stmt_if {
* ```
* if (b) { x = 1; } else { x = 2; }
* ```
- * the result is the `Block` `{ x = 2; }`, and for
+ * the result is the `BlockStmt` `{ x = 2; }`, and for
* ```
* if (b) { x = 1; }
* ```
@@ -294,7 +304,7 @@ class IfStmt extends ConditionalStmt, @stmt_if {
* ```
*/
class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
- override string getCanonicalQLClass() { result = "ConstexprIfStmt" }
+ override string getAPrimaryQlClass() { result = "ConstexprIfStmt" }
/**
* Gets the condition expression of this 'constexpr if' statement.
@@ -316,7 +326,7 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
* ```
* if constexpr (b) { x = 1; }
* ```
- * the result is the `Block` `{ x = 1; }`.
+ * the result is the `BlockStmt` `{ x = 1; }`.
*/
Stmt getThen() { constexpr_if_then(underlyingElement(this), unresolveElement(result)) }
@@ -327,7 +337,7 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
* ```
* if constexpr (b) { x = 1; } else { x = 2; }
* ```
- * the result is the `Block` `{ x = 2; }`, and for
+ * the result is the `BlockStmt` `{ x = 2; }`, and for
* ```
* if constexpr (b) { x = 1; }
* ```
@@ -370,16 +380,18 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
}
}
+private class TLoop = @stmt_while or @stmt_end_test_while or @stmt_range_based_for or @stmt_for;
+
/**
* A C/C++ loop, that is, either a 'while' loop, a 'for' loop, or a
* 'do' loop.
*/
-abstract class Loop extends ControlStructure {
+class Loop extends ControlStructure, TLoop {
/** Gets the condition expression of this loop. */
- abstract Expr getCondition();
+ Expr getCondition() { none() } // overridden in subclasses
/** Gets the body statement of this loop. */
- abstract Stmt getStmt();
+ Stmt getStmt() { none() } // overridden in subclasses
}
/**
@@ -393,7 +405,7 @@ abstract class Loop extends ControlStructure {
* ```
*/
class WhileStmt extends Loop, @stmt_while {
- override string getCanonicalQLClass() { result = "WhileStmt" }
+ override string getAPrimaryQlClass() { result = "WhileStmt" }
override Expr getCondition() { result = this.getChild(0) }
@@ -457,8 +469,8 @@ class WhileStmt extends Loop, @stmt_while {
/**
* A C/C++ jump statement.
*/
-abstract class JumpStmt extends Stmt, @jump {
- override string getCanonicalQLClass() { result = "JumpStmt" }
+class JumpStmt extends Stmt, @jump {
+ override string getAPrimaryQlClass() { result = "JumpStmt" }
/** Gets the target of this jump statement. */
Stmt getTarget() { jumpinfo(underlyingElement(this), _, unresolveElement(result)) }
@@ -475,7 +487,7 @@ abstract class JumpStmt extends Stmt, @jump {
* ```
*/
class GotoStmt extends JumpStmt, @stmt_goto {
- override string getCanonicalQLClass() { result = "GotoStmt" }
+ override string getAPrimaryQlClass() { result = "GotoStmt" }
/**
* Gets the name of the label this 'goto' statement refers to.
@@ -570,7 +582,7 @@ class ComputedGotoStmt extends Stmt, @stmt_assigned_goto {
* ```
*/
class ContinueStmt extends JumpStmt, @stmt_continue {
- override string getCanonicalQLClass() { result = "ContinueStmt" }
+ override string getAPrimaryQlClass() { result = "ContinueStmt" }
override string toString() { result = "continue;" }
@@ -602,7 +614,7 @@ private Stmt getEnclosingContinuable(Stmt s) {
* ```
*/
class BreakStmt extends JumpStmt, @stmt_break {
- override string getCanonicalQLClass() { result = "BreakStmt" }
+ override string getAPrimaryQlClass() { result = "BreakStmt" }
override string toString() { result = "break;" }
@@ -635,7 +647,7 @@ private Stmt getEnclosingBreakable(Stmt s) {
* ```
*/
class LabelStmt extends Stmt, @stmt_label {
- override string getCanonicalQLClass() { result = "LabelStmt" }
+ override string getAPrimaryQlClass() { result = "LabelStmt" }
/** Gets the name of this 'label' statement. */
string getName() { jumpinfo(underlyingElement(this), result, _) and result != "" }
@@ -650,6 +662,67 @@ class LabelStmt extends Stmt, @stmt_label {
override predicate mayBeGloballyImpure() { none() }
}
+/**
+ * A C/C++ `co_return` statement.
+ *
+ * For example:
+ * ```
+ * co_return 1+2;
+ * ```
+ * or
+ * ```
+ * co_return;
+ * ```
+ */
+class CoReturnStmt extends Stmt, @stmt_co_return {
+ override string getAPrimaryQlClass() { result = "CoReturnStmt" }
+
+ /**
+ * Gets the operand of this 'co_return' statement.
+ *
+ * For example, for
+ * ```
+ * co_return 1+2;
+ * ```
+ * the operand is a function call `return_value(1+2)`, and for
+ * ```
+ * co_return;
+ * ```
+ * the operand is a function call `return_void()`.
+ */
+ FunctionCall getOperand() { result = this.getChild(0) }
+
+ /**
+ * Gets the expression of this 'co_return' statement, if any.
+ *
+ * For example, for
+ * ```
+ * co_return 1+2;
+ * ```
+ * the result is `1+2`, and there is no result for
+ * ```
+ * co_return;
+ * ```
+ */
+ Expr getExpr() { result = this.getOperand().getArgument(0) }
+
+ /**
+ * Holds if this 'co_return' statement has an expression.
+ *
+ * For example, this holds for
+ * ```
+ * co_return 1+2;
+ * ```
+ * but not for
+ * ```
+ * co_return;
+ * ```
+ */
+ predicate hasExpr() { exists(this.getExpr()) }
+
+ override string toString() { result = "co_return ..." }
+}
+
/**
* A C/C++ 'return' statement.
*
@@ -663,7 +736,7 @@ class LabelStmt extends Stmt, @stmt_label {
* ```
*/
class ReturnStmt extends Stmt, @stmt_return {
- override string getCanonicalQLClass() { result = "ReturnStmt" }
+ override string getAPrimaryQlClass() { result = "ReturnStmt" }
/**
* Gets the expression of this 'return' statement.
@@ -711,7 +784,7 @@ class ReturnStmt extends Stmt, @stmt_return {
* ```
*/
class DoStmt extends Loop, @stmt_end_test_while {
- override string getCanonicalQLClass() { result = "DoStmt" }
+ override string getAPrimaryQlClass() { result = "DoStmt" }
override Expr getCondition() { result = this.getChild(0) }
@@ -760,7 +833,7 @@ class DoStmt extends Loop, @stmt_end_test_while {
* where `begin_expr` and `end_expr` depend on the type of `xs`.
*/
class RangeBasedForStmt extends Loop, @stmt_range_based_for {
- override string getCanonicalQLClass() { result = "RangeBasedForStmt" }
+ override string getAPrimaryQlClass() { result = "RangeBasedForStmt" }
/**
* Gets the 'body' statement of this range-based 'for' statement.
@@ -769,7 +842,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
* ```
* for (int x : xs) { y += x; }
* ```
- * the result is the `Block` `{ y += x; }`.
+ * the result is the `BlockStmt` `{ y += x; }`.
*/
override Stmt getStmt() { result = this.getChild(5) }
@@ -847,7 +920,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
* ```
*/
class ForStmt extends Loop, @stmt_for {
- override string getCanonicalQLClass() { result = "ForStmt" }
+ override string getAPrimaryQlClass() { result = "ForStmt" }
/**
* Gets the initialization statement of this 'for' statement.
@@ -1078,7 +1151,7 @@ private predicate inForUpdate(Expr forUpdate, Expr child) {
* ```
*/
class SwitchCase extends Stmt, @stmt_switch_case {
- override string getCanonicalQLClass() { result = "SwitchCase" }
+ override string getAPrimaryQlClass() { result = "SwitchCase" }
/**
* Gets the expression of this 'switch case' statement (or the start of
@@ -1156,7 +1229,7 @@ class SwitchCase extends Stmt, @stmt_switch_case {
* DEPRECATED: use `SwitchCase.getAStmt` or `ControlFlowNode.getASuccessor`
* rather than this predicate.
*
- * Gets the `Block` statement immediately following this 'switch case'
+ * Gets the `BlockStmt` statement immediately following this 'switch case'
* statement, if any.
*
* For example, for
@@ -1177,7 +1250,7 @@ class SwitchCase extends Stmt, @stmt_switch_case {
* the `case 7:` has result `{ x = 2; break; }`, `default:` has result
* `{ x = 3; }`, and the others have no result.
*/
- deprecated Block getLabelledStmt() {
+ deprecated BlockStmt getLabelledStmt() {
exists(int i, Stmt parent |
this = parent.getChild(i) and
result = parent.getChild(i + 1)
@@ -1258,7 +1331,7 @@ class SwitchCase extends Stmt, @stmt_switch_case {
* `default:` has results `{ x = 3; }, `x = 4;` and `break;`.
*/
Stmt getAStmt() {
- exists(Block b, int i, int j |
+ exists(BlockStmt b, int i, int j |
b.getStmt(i) = this and
b.getStmt(j) = result and
i < j and
@@ -1297,8 +1370,8 @@ class SwitchCase extends Stmt, @stmt_switch_case {
exists(Stmt lastStmt |
lastStmt = this.getAStmt() and
not lastStmt.getFollowingStmt() = this.getAStmt() and
- if lastStmt instanceof Block
- then result = lastStmt.(Block).getLastStmtIn()
+ if lastStmt instanceof BlockStmt
+ then result = lastStmt.(BlockStmt).getLastStmtIn()
else result = lastStmt
)
}
@@ -1431,7 +1504,7 @@ class DefaultCase extends SwitchCase {
* ```
*/
class SwitchStmt extends ConditionalStmt, @stmt_switch {
- override string getCanonicalQLClass() { result = "SwitchStmt" }
+ override string getAPrimaryQlClass() { result = "SwitchStmt" }
/**
* Gets the expression that this 'switch' statement switches on.
@@ -1642,7 +1715,7 @@ class EnumSwitch extends SwitchStmt {
class Handler extends Stmt, @stmt_handler {
override string toString() { result = "" }
- override string getCanonicalQLClass() { result = "Handler" }
+ override string getAPrimaryQlClass() { result = "Handler" }
/**
* Gets the block containing the implementation of this handler.
@@ -1695,7 +1768,7 @@ deprecated class FinallyEnd extends Stmt {
* ```
*/
class TryStmt extends Stmt, @stmt_try_block {
- override string getCanonicalQLClass() { result = "TryStmt" }
+ override string getAPrimaryQlClass() { result = "TryStmt" }
override string toString() { result = "try { ... }" }
@@ -1770,7 +1843,7 @@ class TryStmt extends Stmt, @stmt_try_block {
class FunctionTryStmt extends TryStmt {
FunctionTryStmt() { not exists(this.getEnclosingBlock()) }
- override string getCanonicalQLClass() { result = "FunctionTryStmt" }
+ override string getAPrimaryQlClass() { result = "FunctionTryStmt" }
}
/**
@@ -1786,8 +1859,8 @@ class FunctionTryStmt extends TryStmt {
* }
* ```
*/
-class CatchBlock extends Block {
- override string getCanonicalQLClass() { result = "CatchBlock" }
+class CatchBlock extends BlockStmt {
+ override string getAPrimaryQlClass() { result = "CatchBlock" }
CatchBlock() { ishandler(underlyingElement(this)) }
@@ -1818,7 +1891,7 @@ class CatchBlock extends Block {
class CatchAnyBlock extends CatchBlock {
CatchAnyBlock() { not exists(this.getParameter()) }
- override string getCanonicalQLClass() { result = "CatchAnyBlock" }
+ override string getAPrimaryQlClass() { result = "CatchAnyBlock" }
}
/**
@@ -1852,10 +1925,10 @@ class MicrosoftTryExceptStmt extends MicrosoftTryStmt {
/** Gets the expression guarding the `__except` statement. */
Expr getCondition() { result = getChild(1) }
- /** Gets the `__except` statement (usually a `Block`). */
+ /** Gets the `__except` statement (usually a `BlockStmt`). */
Stmt getExcept() { result = getChild(2) }
- override string getCanonicalQLClass() { result = "MicrosoftTryExceptStmt" }
+ override string getAPrimaryQlClass() { result = "MicrosoftTryExceptStmt" }
}
/**
@@ -1876,10 +1949,10 @@ class MicrosoftTryFinallyStmt extends MicrosoftTryStmt {
override string toString() { result = "__try { ... } __finally { ... }" }
- /** Gets the `__finally` statement (usually a `Block`). */
+ /** Gets the `__finally` statement (usually a `BlockStmt`). */
Stmt getFinally() { result = getChild(1) }
- override string getCanonicalQLClass() { result = "MicrosoftTryFinallyStmt" }
+ override string getAPrimaryQlClass() { result = "MicrosoftTryFinallyStmt" }
}
/**
@@ -1891,7 +1964,7 @@ class MicrosoftTryFinallyStmt extends MicrosoftTryStmt {
* ```
*/
class DeclStmt extends Stmt, @stmt_decl {
- override string getCanonicalQLClass() { result = "DeclStmt" }
+ override string getAPrimaryQlClass() { result = "DeclStmt" }
/**
* Gets the `i`th declaration entry declared by this 'declaration' statement.
@@ -1972,7 +2045,7 @@ class DeclStmt extends Stmt, @stmt_decl {
* ```
*/
class EmptyStmt extends Stmt, @stmt_empty {
- override string getCanonicalQLClass() { result = "EmptyStmt" }
+ override string getAPrimaryQlClass() { result = "EmptyStmt" }
override string toString() { result = ";" }
@@ -1992,7 +2065,7 @@ class EmptyStmt extends Stmt, @stmt_empty {
class AsmStmt extends Stmt, @stmt_asm {
override string toString() { result = "asm statement" }
- override string getCanonicalQLClass() { result = "AsmStmt" }
+ override string getAPrimaryQlClass() { result = "AsmStmt" }
}
/**
@@ -2009,7 +2082,7 @@ class AsmStmt extends Stmt, @stmt_asm {
class VlaDimensionStmt extends Stmt, @stmt_set_vla_size {
override string toString() { result = "VLA dimension size" }
- override string getCanonicalQLClass() { result = "VlaDimensionStmt" }
+ override string getAPrimaryQlClass() { result = "VlaDimensionStmt" }
/** Gets the expression which gives the size. */
Expr getDimensionExpr() { result = this.getChild(0) }
@@ -2028,14 +2101,14 @@ class VlaDimensionStmt extends Stmt, @stmt_set_vla_size {
class VlaDeclStmt extends Stmt, @stmt_vla_decl {
override string toString() { result = "VLA declaration" }
- override string getCanonicalQLClass() { result = "VlaDeclStmt" }
+ override string getAPrimaryQlClass() { result = "VlaDeclStmt" }
/**
* Gets the number of VLA dimension statements in this VLA
* declaration statement.
*/
int getNumberOfVlaDimensionStmts() {
- exists(Block b, int j |
+ exists(BlockStmt b, int j |
this = b.getStmt(j) and
result =
j - 1 -
@@ -2052,7 +2125,7 @@ class VlaDeclStmt extends Stmt, @stmt_vla_decl {
*/
VlaDimensionStmt getVlaDimensionStmt(int i) {
i in [0 .. this.getNumberOfVlaDimensionStmts() - 1] and
- exists(Block b, int j |
+ exists(BlockStmt b, int j |
this = b.getStmt(j) and
result = b.getStmt(j - this.getNumberOfVlaDimensionStmts() + i)
)
diff --git a/cpp/ql/src/semmle/uml/MagicDraw.qll b/cpp/ql/src/semmle/uml/MagicDraw.qll
index 3cd4701a05dd..cb8fb761c1f4 100644
--- a/cpp/ql/src/semmle/uml/MagicDraw.qll
+++ b/cpp/ql/src/semmle/uml/MagicDraw.qll
@@ -65,15 +65,8 @@ class UMLElement extends XMLElement {
*/
class UMLType extends UMLElement {
UMLType() {
- exists(string type |
- this.getName() = "packagedElement" and
- this.getAttribute("type").getValue() = type and
- (
- type = "uml:Class" or
- type = "uml:Interface" or
- type = "uml:PrimitiveType"
- )
- )
+ this.getName() = "packagedElement" and
+ this.getAttribute("type").getValue() = ["uml:Class", "uml:Interface", "uml:PrimitiveType"]
}
/**
diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme b/cpp/ql/src/semmlecode.cpp.dbscheme
index 282c13bfdbcb..ef73d8cf906d 100644
--- a/cpp/ql/src/semmlecode.cpp.dbscheme
+++ b/cpp/ql/src/semmlecode.cpp.dbscheme
@@ -414,13 +414,35 @@ function_entry_point(int id: @function ref, unique int entry_point: @stmt ref);
function_return_type(int id: @function ref, int return_type: @type ref);
+/** If `function` is a coroutine, then this gives the
+ std::experimental::resumable_traits instance associated with it,
+ and the variables representing the `handle` and `promise` for it. */
+coroutine(
+ unique int function: @function ref,
+ int traits: @type ref,
+ int handle: @variable ref,
+ int promise: @variable ref
+);
+
+/** The `new` function used for allocating the coroutine state, if any. */
+coroutine_new(
+ unique int function: @function ref,
+ int new: @function ref
+);
+
+/** The `delete` function used for deallocating the coroutine state, if any. */
+coroutine_delete(
+ unique int function: @function ref,
+ int delete: @function ref
+);
+
purefunctions(unique int id: @function ref);
function_deleted(unique int id: @function ref);
function_defaulted(unique int id: @function ref);
-
+member_function_this_type(unique int id: @function ref, int this_type: @type ref);
#keyset[id, type_id]
fun_decls(
@@ -508,7 +530,8 @@ static_asserts(
unique int id: @static_assert,
int condition : @expr ref,
string message : string ref,
- int location: @location_default ref
+ int location: @location_default ref,
+ int enclosing : @element ref
);
// each function has an ordered list of parameters
@@ -1227,6 +1250,8 @@ funbind(
| @builtinaddressof
| @vec_fill
| @un_log_op_expr
+ | @co_await
+ | @co_yield
;
@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr;
@@ -1646,6 +1671,8 @@ case @expr.kind of
| 324 = @builtinconvertvector
| 325 = @builtincomplex
| 326 = @spaceshipexpr
+| 327 = @co_await
+| 328 = @co_yield
;
@var_args_expr = @vastartexpr
@@ -1850,6 +1877,7 @@ case @stmt.kind of
| 33 = @stmt_handler
// ... 34 @stmt_finally_end deprecated
| 35 = @stmt_constexpr_if
+| 37 = @stmt_co_return
;
type_vla(
@@ -1934,20 +1962,6 @@ stmtparents(
ishandler(unique int block: @stmt_block ref);
@cfgnode = @stmt | @expr | @function | @initialiser ;
-successors(
- int from: @cfgnode ref,
- int to: @cfgnode ref
-);
-
-truecond(
- unique int from: @cfgnode ref,
- int to: @cfgnode ref
-);
-
-falsecond(
- unique int from: @cfgnode ref,
- int to: @cfgnode ref
-);
stmt_decl_bind(
int stmt: @stmt_decl ref,
diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme.stats b/cpp/ql/src/semmlecode.cpp.dbscheme.stats
index 49f84494fd76..eb699cba62e0 100644
--- a/cpp/ql/src/semmlecode.cpp.dbscheme.stats
+++ b/cpp/ql/src/semmlecode.cpp.dbscheme.stats
@@ -1,7 +1,7 @@
@compilation
-9550
+9552
@externalDataElement
@@ -9,7 +9,7 @@
@duplication
-185065
+185064
@similarity
@@ -25,11 +25,11 @@
@location_default
-8811984
+8813832
@location_stmt
-2176425
+2176409
@location_expr
@@ -37,67 +37,67 @@
@diagnostic
-68684
+68699
@file
-60011
+60023
@folder
-10943
+10945
@macroinvocation
-35573465
+35580051
@function
-3467587
+3468314
@fun_decl
-3539879
+3540621
@var_decl
-5359112
+5360115
@type_decl
-1331883
+1332162
@namespace_decl
-136842
+136871
@using
-291383
+291444
@static_assert
-130659
+130658
@parameter
-4627013
+4627983
@membervariable
-305626
+305691
@globalvariable
-301146
+301143
@localvariable
-521220
+521215
@enumconstant
-93840
+93839
@builtintype
@@ -105,31 +105,31 @@
@derivedtype
-4416924
+4463123
@decltype
-46995
+47005
@usertype
-4193074
+4193942
@mangledname
-483588
+483689
@type_mention
-1677549
+1677532
@routinetype
-430397
+430487
@ptrtomember
-12631
+12634
@specifier
@@ -137,7 +137,7 @@
@gnuattribute
-413730
+413727
@stdattribute
@@ -145,7 +145,7 @@
@declspec
-57828
+57827
@msattribute
@@ -165,7 +165,7 @@
@attribute_arg_constant
-146276
+146275
@attribute_arg_type
@@ -173,19 +173,19 @@
@derivation
-390188
+390270
@frienddecl
-240297
+240347
@comment
-1580009
+1580341
@namespace
-7686
+7688
@specialnamequalifyingelement
@@ -193,7 +193,7 @@
@namequalifier
-1114421
+1114410
@value
@@ -201,11 +201,11 @@
@initialiser
-1664822
+1664806
@errorexpr
-48706
+48716
@address_of
@@ -213,15 +213,15 @@
@reference_to
-1058087
+1057914
@indirect
-294273
+294271
@ref_indirect
-1253933
+1253867
@array_to_pointer
@@ -229,7 +229,7 @@
@vacuous_destructor_call
-5120
+5121
@assume
@@ -237,7 +237,7 @@
@parexpr
-2996467
+2996445
@arithnegexpr
@@ -253,7 +253,7 @@
@notexpr
-337996
+337994
@conjugation
@@ -277,31 +277,31 @@
@preincrexpr
-62511
+62524
@predecrexpr
-24791
+24797
@conditionalexpr
-154262
+154261
@addexpr
-246479
+246477
@subexpr
-134812
+134811
@mulexpr
-140470
+140469
@divexpr
-63655
+63654
@remexpr
@@ -333,7 +333,7 @@
@paddexpr
-87144
+87143
@psubexpr
@@ -341,11 +341,11 @@
@pdiffexpr
-25076
+25082
@lshiftexpr
-350150
+350147
@rshiftexpr
@@ -353,11 +353,11 @@
@andexpr
-257685
+257683
@orexpr
-143373
+143371
@xorexpr
@@ -365,15 +365,15 @@
@eqexpr
-212358
+212356
@neexpr
-88347
+88346
@gtexpr
-43882
+43881
@ltexpr
@@ -385,7 +385,7 @@
@leexpr
-213634
+213633
@minexpr
@@ -397,7 +397,7 @@
@assignexpr
-550955
+550951
@assignaddexpr
@@ -409,7 +409,7 @@
@assignmulexpr
-6907
+6909
@assigndivexpr
@@ -449,19 +449,19 @@
@andlogicalexpr
-130796
+130795
@orlogicalexpr
-74562
+74561
@commaexpr
-10734
+10726
@subscriptexpr
-165978
+165977
@virtfunptrexpr
@@ -469,7 +469,7 @@
@callexpr
-226788
+226781
@vastartexpr
@@ -477,7 +477,7 @@
@vaargexpr
-986
+987
@vaendexpr
@@ -489,27 +489,27 @@
@varaccess
-5318255
+5318202
@thisaccess
-1167122
+1167110
@new_expr
-32127
+32134
@delete_expr
-5964
+5966
@throw_expr
-22313
+22318
@condition_decl
-7028
+7008
@braced_init_list
@@ -517,11 +517,11 @@
@type_id
-4418
+4419
@runtime_sizeof
-278886
+278884
@runtime_alignof
@@ -533,11 +533,11 @@
@expr_stmt
-156384
+156383
@routineexpr
-2265935
+2265785
@type_operand
@@ -661,15 +661,15 @@
@ctordirectinit
-90175
+90194
@ctorvirtualinit
-6228
+6229
@ctorfieldinit
-196009
+196051
@ctordelegatinginit
@@ -677,7 +677,7 @@
@dtordirectdestruct
-29133
+29140
@dtorvirtualdestruct
@@ -685,11 +685,11 @@
@dtorfielddestruct
-29901
+29907
@static_cast
-214716
+214761
@reinterpret_cast
@@ -697,11 +697,11 @@
@const_cast
-5307
+5308
@dynamic_cast
-986
+987
@c_style_cast
@@ -713,7 +713,7 @@
@param_ref
-85877
+85895
@noopexpr
@@ -817,7 +817,7 @@
@noexceptexpr
-17522
+17525
@builtinshufflevector
@@ -829,7 +829,7 @@
@builtinaddressof
-3969
+3970
@vec_fill
@@ -848,20 +848,28 @@
1
+@co_await
+6
+
+
+@co_yield
+1
+
+
@lambdacapture
-21653
+21652
@stmt_expr
-1269739
+1269727
@stmt_if
-523914
+523910
@stmt_while
-30997
+30993
@stmt_goto
@@ -873,23 +881,23 @@
@stmt_return
-1130006
+1130211
@stmt_block
-1324997
+1325121
@stmt_end_test_while
-149714
+149713
@stmt_for
-32154
+32153
@stmt_switch_case
-271391
+271389
@stmt_switch
@@ -897,11 +905,11 @@
@stmt_asm
-241255
+241253
@stmt_try_block
-17960
+17964
@stmt_microsoft_try
@@ -909,7 +917,7 @@
@stmt_decl
-613063
+613092
@stmt_set_vla_size
@@ -925,7 +933,7 @@
@stmt_empty
-102357
+102356
@stmt_continue
@@ -933,7 +941,7 @@
@stmt_break
-223843
+223842
@stmt_range_based_for
@@ -948,40 +956,44 @@
3
+@stmt_co_return
+2
+
+
@ppd_if
-156064
+156097
@ppd_ifdef
-61074
+61087
@ppd_ifndef
-83311
+83329
@ppd_elif
-20625
+20629
@ppd_else
-57653
+57665
@ppd_endif
-300451
+300514
@ppd_plain_include
-290703
+290764
@ppd_define
-318006
+318073
@ppd_undef
-19243
+19247
@ppd_line
@@ -1038,11 +1050,11 @@
compilations
-9550
+9552
id
-9550
+9552
cwd
@@ -1060,7 +1072,7 @@
1
2
-9550
+9552
@@ -1086,7 +1098,7 @@
compilation_args
-375671
+375669
id
@@ -1098,7 +1110,7 @@
arg
-18603
+18602
@@ -1382,11 +1394,11 @@
compilation_compiling_files
-9550
+9552
id
-9550
+9552
num
@@ -1394,7 +1406,7 @@
file
-4846
+4847
@@ -1408,7 +1420,7 @@
1
2
-9550
+9552
@@ -1424,7 +1436,7 @@
1
2
-9550
+9552
@@ -1477,7 +1489,7 @@
2
3
-4550
+4551
3
@@ -1498,7 +1510,7 @@
1
2
-4846
+4847
@@ -1508,11 +1520,11 @@
compilation_time
-38114
+38122
id
-9528
+9530
num
@@ -1524,7 +1536,7 @@
seconds
-12094
+12700
@@ -1538,7 +1550,7 @@
1
2
-9528
+9530
@@ -1554,7 +1566,7 @@
4
5
-9528
+9530
@@ -1570,17 +1582,17 @@
2
3
-10
+21
3
4
-2675
+2599
4
5
-6842
+6909
@@ -1626,8 +1638,8 @@
12
-1103
-1104
+1158
+1159
10
@@ -1684,13 +1696,13 @@
10
-579
-580
+590
+591
10
-670
-671
+694
+695
10
@@ -1707,22 +1719,22 @@
1
2
-7949
+8653
2
3
-2401
+2588
3
-4
-932
+5
+1074
-4
-627
-811
+5
+629
+383
@@ -1738,7 +1750,7 @@
1
2
-12094
+12700
@@ -1754,17 +1766,17 @@
1
2
-10285
+11131
2
3
-1798
+1535
3
4
-10
+32
@@ -1774,15 +1786,15 @@
diagnostic_for
-846935
+847112
diagnostic
-68684
+68699
compilation
-9221
+9223
file_number
@@ -1790,7 +1802,7 @@
file_number_diagnostic_number
-6502
+6503
@@ -1804,12 +1816,12 @@
1
2
-9254
+9256
2
3
-56732
+56744
254
@@ -1830,7 +1842,7 @@
1
2
-68684
+68699
@@ -1846,7 +1858,7 @@
1
2
-68684
+68699
@@ -1867,7 +1879,7 @@
7
8
-5811
+5812
8
@@ -1903,7 +1915,7 @@
1
2
-9221
+9223
@@ -1924,7 +1936,7 @@
7
8
-5811
+5812
8
@@ -2008,7 +2020,7 @@
1
2
-2675
+2676
2
@@ -2018,7 +2030,7 @@
5
6
-953
+954
7
@@ -2069,7 +2081,7 @@
10
11
-953
+954
14
@@ -2104,7 +2116,7 @@
254
255
-2620
+2621
309
@@ -2125,7 +2137,7 @@
1
2
-6502
+6503
@@ -2135,19 +2147,19 @@
compilation_finished
-9550
+9552
id
-9550
+9552
cpu_seconds
-8157
+8093
elapsed_seconds
-186
+219
@@ -2161,7 +2173,7 @@
1
2
-9550
+9552
@@ -2177,7 +2189,7 @@
1
2
-9550
+9552
@@ -2193,17 +2205,17 @@
1
2
-7160
+6931
2
3
-756
+954
3
-6
-241
+7
+208
@@ -2219,12 +2231,12 @@
1
2
-7675
+7764
2
3
-482
+329
@@ -2240,17 +2252,22 @@
1
2
-32
+54
2
3
-10
+21
3
4
-32
+10
+
+
+5
+6
+10
7
@@ -2258,48 +2275,53 @@
10
-8
-9
+9
+10
10
-21
-22
+11
+12
10
-26
-27
+17
+18
10
-31
-32
+41
+42
10
-104
-105
+46
+47
10
-137
-138
+47
+48
10
-144
-145
+86
+87
10
-173
-174
+157
+158
10
-206
-207
+183
+184
+10
+
+
+250
+251
10
@@ -2316,17 +2338,22 @@
1
2
-32
+54
2
3
-10
+21
3
4
-32
+10
+
+
+5
+6
+10
7
@@ -2334,38 +2361,38 @@
10
-8
-9
+9
+10
10
-21
-22
+11
+12
10
-25
-26
+17
+18
10
-29
-30
+40
+41
10
-84
-85
+44
+45
10
-122
-123
+46
+47
10
-132
-133
+73
+74
10
@@ -2374,8 +2401,13 @@
10
-196
-197
+164
+165
+10
+
+
+190
+191
10
@@ -2624,11 +2656,11 @@
duplicateCode
-185065
+185064
id
-185065
+185064
relativePath
@@ -2650,7 +2682,7 @@
1
2
-185065
+185064
@@ -2666,7 +2698,7 @@
1
2
-185065
+185064
@@ -3146,11 +3178,11 @@
tokens
-39551856
+39551564
id
-278635
+278633
offset
@@ -3158,7 +3190,7 @@
beginLine
-784463
+784457
beginColumn
@@ -3166,7 +3198,7 @@
endLine
-784463
+784457
endColumn
@@ -3270,7 +3302,7 @@
5
6
-109619
+109618
6
@@ -3285,7 +3317,7 @@
8
12
-23598
+23597
12
@@ -3310,7 +3342,7 @@
28
151
-14527
+14526
@@ -3341,7 +3373,7 @@
32
33
-163370
+163368
33
@@ -3361,7 +3393,7 @@
80
132
-2860
+2859
@@ -3382,7 +3414,7 @@
5
6
-109619
+109618
6
@@ -3397,7 +3429,7 @@
8
12
-23598
+23597
12
@@ -3422,7 +3454,7 @@
28
151
-14527
+14526
@@ -3453,7 +3485,7 @@
32
33
-163658
+163657
33
@@ -3819,12 +3851,12 @@
1
2
-403450
+403447
2
3
-103003
+103002
3
@@ -3834,7 +3866,7 @@
4
6
-70444
+70443
6
@@ -3844,7 +3876,7 @@
8
13
-65884
+65883
13
@@ -3870,7 +3902,7 @@
7
12
-64233
+64232
12
@@ -3885,17 +3917,17 @@
32
33
-266937
+266935
33
41
-62613
+62612
41
55
-61281
+61280
55
@@ -3931,7 +3963,7 @@
5
9
-62576
+62575
9
@@ -3951,7 +3983,7 @@
32
33
-348865
+348862
33
@@ -3961,12 +3993,12 @@
37
42
-61459
+61458
42
122
-53542
+53541
@@ -3982,7 +4014,7 @@
1
2
-784463
+784457
@@ -4023,7 +4055,7 @@
32
33
-349865
+349862
33
@@ -4038,7 +4070,7 @@
43
123
-47190
+47189
@@ -4454,12 +4486,12 @@
1
2
-403450
+403447
2
3
-103003
+103002
3
@@ -4469,7 +4501,7 @@
4
6
-70444
+70443
6
@@ -4479,7 +4511,7 @@
8
13
-65884
+65883
13
@@ -4505,7 +4537,7 @@
7
12
-64233
+64232
12
@@ -4520,17 +4552,17 @@
32
33
-266937
+266935
33
41
-62613
+62612
41
55
-61281
+61280
55
@@ -4561,7 +4593,7 @@
1
2
-784463
+784457
@@ -4582,7 +4614,7 @@
5
9
-62576
+62575
9
@@ -4602,7 +4634,7 @@
32
33
-348865
+348862
33
@@ -4612,12 +4644,12 @@
37
42
-61459
+61458
42
122
-53542
+53541
@@ -4658,7 +4690,7 @@
32
33
-349865
+349862
33
@@ -4673,7 +4705,7 @@
43
123
-47190
+47189
@@ -5279,11 +5311,11 @@
header_to_external_package
-8530
+8532
fileid
-8530
+8532
package
@@ -5301,7 +5333,7 @@
1
2
-8530
+8532
@@ -6570,31 +6602,31 @@
locations_default
-8811984
+8813832
id
-8811984
+8813832
container
-70954
+70969
startLine
-150604
+150635
startColumn
-5460
+5461
endLine
-150428
+150460
endColumn
-10625
+10627
@@ -6608,7 +6640,7 @@
1
2
-8811984
+8813832
@@ -6624,7 +6656,7 @@
1
2
-8811984
+8813832
@@ -6640,7 +6672,7 @@
1
2
-8811984
+8813832
@@ -6656,7 +6688,7 @@
1
2
-8811984
+8813832
@@ -6672,7 +6704,7 @@
1
2
-8811984
+8813832
@@ -6688,62 +6720,62 @@
1
2
-11524
+11526
2
19
-6096
+6097
19
25
-5482
+5483
25
31
-5515
+5516
31
41
-5822
+5823
41
54
-5526
+5527
54
72
-5603
+5604
72
99
-5416
+5417
99
137
-5383
+5384
137
220
-5328
+5330
220
430
-5328
+5330
430
20913
-3925
+3926
@@ -6759,57 +6791,57 @@
1
2
-11524
+11526
2
15
-6008
+6010
15
20
-6140
+6141
20
25
-5548
+5549
25
32
-6096
+6097
32
41
-5679
+5681
41
53
-5723
+5724
53
71
-5592
+5593
71
99
-5449
+5450
99
158
-5328
+5330
158
351
-5361
+5363
351
@@ -6830,57 +6862,57 @@
1
2
-11524
+11526
2
4
-6008
+6010
4
8
-6546
+6547
8
11
-5394
+5395
11
14
-5778
+5779
14
18
-6250
+6251
18
23
-5756
+5757
23
29
-5833
+5834
29
37
-5690
+5692
37
50
-5668
+5670
50
78
-5383
+5384
78
@@ -6901,62 +6933,62 @@
1
2
-11524
+11526
2
15
-5986
+5988
15
20
-6162
+6163
20
25
-5493
+5494
25
32
-6107
+6108
32
41
-5668
+5670
41
53
-5723
+5724
53
70
-5383
+5384
70
96
-5339
+5341
96
153
-5394
+5395
153
333
-5350
+5352
333
9356
-2817
+2818
@@ -6972,57 +7004,57 @@
1
2
-11524
+11526
2
14
-5734
+5735
14
19
-6118
+6119
19
23
-5712
+5713
23
28
-6414
+6415
28
33
-5515
+5516
33
40
-5953
+5955
40
47
-5350
+5352
47
57
-5603
+5604
57
69
-5635
+5637
69
91
-5328
+5330
91
@@ -7043,52 +7075,52 @@
1
2
-30844
+30850
2
3
-18026
+18030
3
4
-17949
+17953
4
5
-9714
+9717
5
7
-13706
+13709
7
9
-13410
+13412
9
13
-13201
+13204
13
32
-11568
+11570
32
127
-11304
+11307
127
6472
-10877
+10879
@@ -7104,42 +7136,42 @@
1
2
-55647
+55658
2
3
-33432
+33439
3
4
-9901
+9903
4
5
-8432
+8433
5
8
-12905
+12908
8
27
-11491
+11493
27
123
-11337
+11340
123
6472
-7456
+7457
@@ -7155,52 +7187,52 @@
1
2
-31951
+31958
2
3
-17861
+17865
3
4
-19791
+19795
4
5
-9583
+9585
5
7
-13914
+13917
7
9
-13826
+13829
9
13
-12708
+12711
13
27
-11480
+11482
27
62
-11348
+11351
62
153
-8136
+8137
@@ -7216,22 +7248,22 @@
1
2
-112950
+112973
2
3
-17412
+17416
3
7
-12532
+12535
7
184
-7708
+7709
@@ -7247,52 +7279,52 @@
1
2
-31798
+31805
2
3
-17774
+17777
3
4
-18640
+18644
4
5
-9868
+9870
5
7
-13728
+13731
7
9
-13772
+13774
9
13
-12730
+12733
13
29
-11655
+11658
29
74
-11348
+11351
74
258
-9287
+9289
@@ -7435,7 +7467,7 @@
2
3
-997
+998
3
@@ -7501,7 +7533,7 @@
2
3
-997
+998
3
@@ -7613,52 +7645,52 @@
1
2
-30559
+30565
2
3
-18059
+18063
3
4
-17807
+17810
4
5
-9868
+9870
5
7
-13772
+13774
7
9
-13454
+13456
9
13
-13048
+13051
13
31
-11458
+11460
31
124
-11293
+11296
124
6472
-11107
+11109
@@ -7674,42 +7706,42 @@
1
2
-55362
+55373
2
3
-33333
+33340
3
4
-9868
+9870
4
5
-8541
+8543
5
8
-13103
+13105
8
27
-11326
+11329
27
121
-11293
+11296
121
6472
-7598
+7600
@@ -7725,22 +7757,22 @@
1
2
-112160
+112184
2
3
-17247
+17251
3
7
-12072
+12074
7
46
-8947
+8949
@@ -7756,57 +7788,57 @@
1
2
-31644
+31651
2
3
-17938
+17942
3
4
-19736
+19741
4
5
-9605
+9607
5
6
-7861
+7863
6
7
-6129
+6130
7
9
-13837
+13840
9
13
-12708
+12711
13
27
-11491
+11493
27
62
-11348
+11351
62
153
-8125
+8126
@@ -7822,52 +7854,52 @@
1
2
-31546
+31552
2
3
-17741
+17745
3
4
-18629
+18633
4
5
-9934
+9936
5
7
-13815
+13818
7
9
-13772
+13774
9
13
-12785
+12787
13
29
-11513
+11515
29
74
-11359
+11362
74
258
-9331
+9333
@@ -7883,7 +7915,7 @@
1
2
-4210
+4211
2
@@ -7898,7 +7930,7 @@
4
5
-646
+647
5
@@ -7939,17 +7971,17 @@
1
2
-4835
+4836
2
3
-1282
+1283
3
4
-953
+954
4
@@ -7990,7 +8022,7 @@
1
2
-4243
+4244
2
@@ -8005,7 +8037,7 @@
4
5
-635
+636
5
@@ -8046,17 +8078,17 @@
1
2
-4868
+4869
2
3
-1326
+1327
3
4
-942
+943
4
@@ -8097,7 +8129,7 @@
1
2
-4243
+4244
2
@@ -8112,7 +8144,7 @@
4
5
-635
+636
5
@@ -8147,19 +8179,19 @@
locations_stmt
-2176425
+2176409
id
-2176425
+2176409
container
-3173
+3172
startLine
-296354
+296351
startColumn
@@ -8167,7 +8199,7 @@
endLine
-294555
+294553
endColumn
@@ -8185,7 +8217,7 @@
1
2
-2176425
+2176409
@@ -8201,7 +8233,7 @@
1
2
-2176425
+2176409
@@ -8217,7 +8249,7 @@
1
2
-2176425
+2176409
@@ -8233,7 +8265,7 @@
1
2
-2176425
+2176409
@@ -8249,7 +8281,7 @@
1
2
-2176425
+2176409
@@ -8650,7 +8682,7 @@
1
2
-113657
+113656
2
@@ -8660,7 +8692,7 @@
3
4
-31282
+31281
4
@@ -8696,12 +8728,12 @@
1
2
-175337
+175336
2
3
-51977
+51976
3
@@ -8737,7 +8769,7 @@
1
2
-130651
+130650
2
@@ -8778,7 +8810,7 @@
1
2
-191442
+191440
2
@@ -8814,7 +8846,7 @@
1
2
-155827
+155826
2
@@ -8829,7 +8861,7 @@
4
9
-24672
+24671
9
@@ -9255,7 +9287,7 @@
6
16
-23107
+23106
16
@@ -9281,7 +9313,7 @@
1
2
-175049
+175048
2
@@ -9317,7 +9349,7 @@
1
2
-193725
+193723
2
@@ -9337,7 +9369,7 @@
8
32
-14613
+14612
@@ -9353,22 +9385,22 @@
1
2
-132812
+132811
2
3
-67971
+67970
3
4
-32442
+32441
4
7
-26685
+26684
7
@@ -9378,7 +9410,7 @@
16
46
-12514
+12513
@@ -9394,7 +9426,7 @@
1
2
-155722
+155721
2
@@ -11616,23 +11648,23 @@
numlines
-499158
+499263
element_id
-492196
+492299
num_lines
-9342
+9344
num_code
-7291
+7293
num_comment
-3936
+3937
@@ -11646,12 +11678,12 @@
1
2
-485310
+485411
2
7
-6886
+6887
@@ -11667,12 +11699,12 @@
1
2
-485364
+485466
2
7
-6831
+6832
@@ -11688,7 +11720,7 @@
1
2
-492119
+492222
2
@@ -11709,7 +11741,7 @@
1
2
-4254
+4255
2
@@ -11760,12 +11792,12 @@
1
2
-4320
+4321
2
3
-1260
+1261
3
@@ -11811,12 +11843,12 @@
1
2
-4309
+4310
2
3
-1260
+1261
3
@@ -11857,7 +11889,7 @@
1
2
-3179
+3180
2
@@ -11892,7 +11924,7 @@
101
7978
-328
+329
@@ -11908,7 +11940,7 @@
1
2
-3201
+3202
2
@@ -11918,7 +11950,7 @@
3
4
-635
+636
4
@@ -11928,7 +11960,7 @@
6
10
-635
+636
10
@@ -11959,7 +11991,7 @@
1
2
-3190
+3191
2
@@ -11969,7 +12001,7 @@
3
4
-635
+636
4
@@ -11994,7 +12026,7 @@
27
34
-317
+318
@@ -12010,7 +12042,7 @@
1
2
-1885
+1886
2
@@ -12025,12 +12057,12 @@
4
7
-328
+329
7
13
-328
+329
14
@@ -12061,7 +12093,7 @@
1
2
-1896
+1897
2
@@ -12081,7 +12113,7 @@
7
13
-328
+329
13
@@ -12112,7 +12144,7 @@
1
2
-1896
+1897
2
@@ -12127,7 +12159,7 @@
4
7
-328
+329
7
@@ -12157,11 +12189,11 @@
diagnostics
-68684
+68699
id
-68684
+68699
severity
@@ -12177,7 +12209,7 @@
full_error_message
-59474
+59486
location
@@ -12195,7 +12227,7 @@
1
2
-68684
+68699
@@ -12211,7 +12243,7 @@
1
2
-68684
+68699
@@ -12227,7 +12259,7 @@
1
2
-68684
+68699
@@ -12243,7 +12275,7 @@
1
2
-68684
+68699
@@ -12259,7 +12291,7 @@
1
2
-68684
+68699
@@ -12675,7 +12707,7 @@
1
2
-59463
+59475
841
@@ -12696,7 +12728,7 @@
1
2
-59474
+59486
@@ -12712,7 +12744,7 @@
1
2
-59474
+59486
@@ -12728,7 +12760,7 @@
1
2
-59474
+59486
@@ -12744,7 +12776,7 @@
1
2
-59474
+59486
@@ -12854,19 +12886,19 @@
files
-60011
+60023
id
-60011
+60023
name
-60011
+60023
simple
-41052
+41061
ext
@@ -12888,7 +12920,7 @@
1
2
-60011
+60023
@@ -12904,7 +12936,7 @@
1
2
-60011
+60023
@@ -12920,7 +12952,7 @@
1
2
-60011
+60023
@@ -12936,7 +12968,7 @@
1
2
-60011
+60023
@@ -12952,7 +12984,7 @@
1
2
-60011
+60023
@@ -12968,7 +13000,7 @@
1
2
-60011
+60023
@@ -12984,7 +13016,7 @@
1
2
-60011
+60023
@@ -13000,7 +13032,7 @@
1
2
-60011
+60023
@@ -13016,17 +13048,17 @@
1
2
-31173
+31179
2
3
-6195
+6196
3
7
-3234
+3235
7
@@ -13047,17 +13079,17 @@
1
2
-31173
+31179
2
3
-6195
+6196
3
7
-3234
+3235
7
@@ -13078,12 +13110,12 @@
1
2
-36699
+36707
2
3
-3782
+3783
3
@@ -13104,7 +13136,7 @@
1
2
-41052
+41061
@@ -13362,19 +13394,19 @@
folders
-10943
+10945
id
-10943
+10945
name
-10943
+10945
simple
-3135
+3136
@@ -13388,7 +13420,7 @@
1
2
-10943
+10945
@@ -13404,7 +13436,7 @@
1
2
-10943
+10945
@@ -13420,7 +13452,7 @@
1
2
-10943
+10945
@@ -13436,7 +13468,7 @@
1
2
-10943
+10945
@@ -13457,7 +13489,7 @@
2
3
-668
+669
3
@@ -13493,7 +13525,7 @@
2
3
-668
+669
3
@@ -13518,15 +13550,15 @@
containerparent
-70932
+70947
parent
-10943
+10945
child
-70932
+70947
@@ -13540,7 +13572,7 @@
1
2
-4989
+4990
2
@@ -13591,7 +13623,7 @@
1
2
-70932
+70947
@@ -13601,11 +13633,11 @@
fileannotations
-5149615
+5150695
id
-4824
+4825
kind
@@ -13613,11 +13645,11 @@
name
-54616
+54628
value
-44638
+44647
@@ -13852,42 +13884,42 @@
1
2
-8925
+8927
2
3
-6030
+6032
3
6
-4517
+4518
6
8
-4451
+4452
8
14
-4407
+4408
14
18
-3914
+3915
18
21
-4243
+4244
21
34
-4396
+4397
34
@@ -13897,12 +13929,12 @@
129
236
-4155
+4156
236
395
-4111
+4112
395
@@ -13923,7 +13955,7 @@
1
2
-54616
+54628
@@ -13939,12 +13971,12 @@
1
2
-9978
+9980
2
3
-8048
+8049
3
@@ -13954,42 +13986,42 @@
4
6
-3958
+3959
6
10
-4879
+4880
10
14
-3486
+3487
14
18
-4495
+4496
18
23
-4199
+4200
23
44
-4396
+4397
44
97
-4155
+4156
97
405
-4100
+4101
421
@@ -14010,7 +14042,7 @@
1
2
-6896
+6898
2
@@ -14020,7 +14052,7 @@
5
8
-3234
+3235
8
@@ -14040,22 +14072,22 @@
25
40
-3234
+3235
40
195
-3519
+3520
195
207
-3508
+3509
207
273
-3662
+3663
273
@@ -14065,12 +14097,12 @@
328
407
-3837
+3838
407
441
-1282
+1283
@@ -14086,7 +14118,7 @@
1
2
-44627
+44636
2
@@ -14107,12 +14139,12 @@
1
2
-6918
+6920
2
5
-2510
+2511
5
@@ -14122,12 +14154,12 @@
8
16
-3497
+3498
16
18
-3015
+3016
18
@@ -14137,17 +14169,17 @@
21
31
-3881
+3882
31
41
-3530
+3531
41
54
-3530
+3531
54
@@ -14177,15 +14209,15 @@
inmacroexpansion
-60859049
+60858600
id
-15688248
+15688133
inv
-2531860
+2531842
@@ -14199,47 +14231,47 @@
1
2
-3618172
+3618145
2
3
-2444422
+2444404
3
4
-1897977
+1897963
4
5
-1930674
+1930660
5
6
-1866536
+1866522
6
7
-968687
+968680
7
8
-1531676
+1531664
8
11
-1254555
+1254546
11
6193
-175545
+175544
@@ -14255,52 +14287,52 @@
1
2
-616471
+616466
2
3
-369752
+369750
3
4
-160128
+160127
4
5
-256555
+256553
5
7
-183476
+183475
7
9
-150758
+150757
9
12
-221730
+221729
12
22
-202906
+202905
22
45
-191200
+191199
45
153127
-178879
+178878
@@ -14310,15 +14342,15 @@
affectedbymacroexpansion
-35484840
+35484578
id
-4079168
+4079138
inv
-3279104
+3279080
@@ -14332,37 +14364,37 @@
1
2
-1340494
+1340484
2
3
-718492
+718487
3
4
-682067
+682062
4
6
-320608
+320606
6
10
-313182
+313180
10
24
-307677
+307675
24
64
-309561
+309559
64
@@ -14383,72 +14415,72 @@
1
2
-252913
+252912
2
3
-209805
+209803
3
4
-202323
+202322
4
5
-257768
+257766
5
6
-301208
+301206
6
7
-247568
+247566
7
8
-230831
+230830
8
9
-227720
+227718
9
10
-182291
+182290
10
12
-285325
+285323
12
16
-297059
+297057
16
23
-275039
+275036
23
60
-248654
+248652
60
526
-60594
+60593
@@ -14458,19 +14490,19 @@
macroinvocations
-35573465
+35580051
id
-35573465
+35580051
macro_id
-81217
+81234
location
-761079
+761238
kind
@@ -14488,7 +14520,7 @@
1
2
-35573465
+35580051
@@ -14504,7 +14536,7 @@
1
2
-35573465
+35580051
@@ -14520,7 +14552,7 @@
1
2
-35573465
+35580051
@@ -14536,52 +14568,52 @@
1
2
-17017
+17021
2
3
-16557
+16560
3
4
-3574
+3575
4
6
-7193
+7194
6
11
-6896
+6898
11
19
-6173
+6174
19
40
-6173
+6174
40
105
-6195
+6196
105
487
-6107
+6108
488
196960
-5328
+5330
@@ -14597,32 +14629,32 @@
1
2
-43212
+43222
2
3
-10745
+10747
3
4
-5339
+5341
4
6
-6973
+6975
6
13
-6721
+6722
13
66
-6107
+6108
66
@@ -14643,12 +14675,12 @@
1
2
-75077
+75092
2
3
-6140
+6141
@@ -14664,42 +14696,42 @@
1
2
-284332
+284425
2
3
-177479
+177483
3
4
-43443
+43452
4
5
-58585
+58598
5
8
-63563
+63577
8
17
-61151
+61164
17
80
-57149
+57161
80
258137
-15372
+15376
@@ -14715,12 +14747,12 @@
1
2
-712383
+712533
2
354
-48695
+48705
@@ -14736,7 +14768,7 @@
1
2
-761079
+761238
@@ -14755,8 +14787,8 @@
10
-3216594
-3216595
+3216514
+3216515
10
@@ -14809,15 +14841,15 @@
macroparent
-31697444
+31703249
id
-31697444
+31703249
parent_id
-24675437
+24680109
@@ -14831,7 +14863,7 @@
1
2
-31697444
+31703249
@@ -14847,17 +14879,17 @@
1
2
-18985994
+18989648
2
3
-4852266
+4853131
3
88
-837176
+837329
@@ -14867,15 +14899,15 @@
macrolocationbind
-4013345
+4013316
id
-2798451
+2798431
location
-2003426
+2003411
@@ -14889,17 +14921,17 @@
1
2
-2198274
+2198258
2
3
-338898
+338895
3
7
-231525
+231523
7
@@ -14920,17 +14952,17 @@
1
2
-1601810
+1601798
2
3
-170839
+170837
3
8
-155317
+155316
8
@@ -14945,11 +14977,11 @@
macro_argument_unexpanded
-92630091
+92648459
invocation
-27414544
+27419703
argument_index
@@ -14957,7 +14989,7 @@
text
-312611
+312677
@@ -14971,22 +15003,22 @@
1
2
-7655047
+7656521
2
3
-11464654
+11466609
3
4
-6260126
+6261428
4
67
-2034716
+2035143
@@ -15002,22 +15034,22 @@
1
2
-7722986
+7724474
2
3
-11618525
+11620513
3
4
-6088743
+6090010
4
67
-1984288
+1984704
@@ -15033,7 +15065,7 @@
50787
50788
-635
+636
50989
@@ -15041,8 +15073,8 @@
54
-756485
-2500192
+756484
+2500138
32
@@ -15059,7 +15091,7 @@
2
3
-635
+636
13
@@ -15085,57 +15117,57 @@
1
2
-37894
+37902
2
3
-61118
+61164
3
4
-14089
+14092
4
5
-42138
+42136
5
8
-25296
+25290
8
12
-15120
+15112
12
16
-21765
+21781
16
21
-23936
+23941
21
41
-24967
+24972
41
121
-23794
+23788
121
567376
-22489
+22493
@@ -15151,17 +15183,17 @@
1
2
-226021
+226068
2
3
-76294
+76310
3
9
-10296
+10298
@@ -15171,11 +15203,11 @@
macro_argument_expanded
-92630091
+92648459
invocation
-27414544
+27419703
argument_index
@@ -15183,7 +15215,7 @@
text
-189409
+189448
@@ -15197,22 +15229,22 @@
1
2
-7655047
+7656521
2
3
-11464654
+11466609
3
4
-6260126
+6261428
4
67
-2034716
+2035143
@@ -15228,22 +15260,22 @@
1
2
-11162952
+11165031
2
3
-9892725
+9894482
3
4
-5268749
+5269844
4
9
-1090116
+1090345
@@ -15259,7 +15291,7 @@
50787
50788
-635
+636
50989
@@ -15267,8 +15299,8 @@
54
-756485
-2500192
+756484
+2500138
32
@@ -15311,22 +15343,22 @@
1
2
-22861
+22866
2
3
-38180
+38220
3
4
-6239
+6240
4
5
-15186
+15178
5
@@ -15336,37 +15368,37 @@
6
7
-22039
+22033
7
9
-15471
+15463
9
15
-16480
+16494
15
28
-14254
+14257
28
77
-14331
+14323
77
337
-14320
+14323
338
-1133308
-7291
+1133296
+7293
@@ -15382,17 +15414,17 @@
1
2
-95614
+95634
2
3
-79474
+79490
3
6
-14243
+14246
6
@@ -15407,15 +15439,15 @@
functions
-3467587
+3468314
id
-3467587
+3468314
name
-288335
+288395
kind
@@ -15433,7 +15465,7 @@
1
2
-3467587
+3468314
@@ -15449,7 +15481,7 @@
1
2
-3467587
+3468314
@@ -15465,27 +15497,27 @@
1
2
-195363
+195404
2
3
-28311
+28317
3
5
-26074
+26080
5
13
-22160
+22164
13
123517
-16425
+16428
@@ -15501,7 +15533,7 @@
1
2
-286876
+286936
2
@@ -15608,15 +15640,15 @@
function_entry_point
-1008372
+1008551
id
-1005444
+1005633
entry_point
-1008372
+1008551
@@ -15630,12 +15662,12 @@
1
2
-1002791
+1002990
2
9
-2653
+2643
@@ -15651,7 +15683,7 @@
1
2
-1008372
+1008551
@@ -15661,15 +15693,15 @@
function_return_type
-3477751
+3478481
id
-3467115
+3467843
return_type
-1023372
+1023587
@@ -15683,12 +15715,12 @@
1
2
-3456973
+3457698
2
6
-10142
+10144
@@ -15704,17 +15736,329 @@
1
2
-299102
+299165
2
3
-662569
+662708
3
84263
-61699
+61712
+
+
+
+
+
+
+
+
+coroutine
+2
+
+
+function
+2
+
+
+traits
+2
+
+
+handle
+2
+
+
+promise
+2
+
+
+
+
+function
+traits
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+function
+handle
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+function
+promise
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+traits
+function
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+traits
+handle
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+traits
+promise
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+handle
+function
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+handle
+traits
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+handle
+promise
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+promise
+function
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+promise
+traits
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+promise
+handle
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+
+
+coroutine_new
+2
+
+
+function
+2
+
+
+new
+1
+
+
+
+
+function
+new
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+new
+function
+
+
+12
+
+
+2
+3
+1
+
+
+
+
+
+
+
+
+coroutine_delete
+2
+
+
+function
+2
+
+
+delete
+1
+
+
+
+
+function
+delete
+
+
+12
+
+
+1
+2
+2
+
+
+
+
+
+
+delete
+function
+
+
+12
+
+
+2
+3
+1
@@ -15735,49 +16079,127 @@
function_deleted
-56173
+56185
id
-56173
+56185
function_defaulted
-12927
+12930
id
-12927
+12930
+member_function_this_type
+523358
+
+
+id
+523358
+
+
+this_type
+171374
+
+
+
+
+id
+this_type
+
+
+12
+
+
+1
+2
+523358
+
+
+
+
+
+
+this_type
+id
+
+
+12
+
+
+1
+2
+62546
+
+
+2
+3
+44560
+
+
+3
+4
+22296
+
+
+4
+5
+15014
+
+
+5
+7
+13687
+
+
+7
+36
+12875
+
+
+40
+87
+394
+
+
+
+
+
+
+
+
fun_decls
-3543146
+3543890
id
-3539879
+3540621
function
-3374154
+3374862
type_id
-1010302
+1010514
name
-256788
+256842
location
-795290
+795456
@@ -15791,7 +16213,7 @@
1
2
-3539879
+3540621
@@ -15807,12 +16229,12 @@
1
2
-3536896
+3537638
2
4
-2982
+2983
@@ -15828,7 +16250,7 @@
1
2
-3539879
+3540621
@@ -15844,7 +16266,7 @@
1
2
-3539879
+3540621
@@ -15860,12 +16282,12 @@
1
2
-3238156
+3238835
2
9
-135998
+136027
@@ -15881,12 +16303,12 @@
1
2
-3357498
+3358203
2
6
-16655
+16659
@@ -15902,7 +16324,7 @@
1
2
-3374154
+3374862
@@ -15918,12 +16340,12 @@
1
2
-3286533
+3287222
2
9
-87621
+87639
@@ -15939,17 +16361,17 @@
1
2
-280275
+280334
2
3
-660947
+661085
3
89606
-69079
+69093
@@ -15965,17 +16387,17 @@
1
2
-292107
+292168
2
3
-657295
+657433
3
83378
-60899
+60912
@@ -15991,12 +16413,12 @@
1
2
-943777
+943975
2
7512
-66524
+66538
@@ -16012,17 +16434,17 @@
1
2
-912922
+913113
2
6
-80132
+80148
6
22467
-17247
+17251
@@ -16038,32 +16460,32 @@
1
2
-153520
+153552
2
3
-29298
+29304
3
4
-15932
+15935
4
6
-19517
+19521
6
13
-20197
+20201
13
123766
-18322
+18326
@@ -16079,32 +16501,32 @@
1
2
-164145
+164180
2
3
-28476
+28482
3
4
-14451
+14454
4
7
-21633
+21638
7
25
-19638
+19642
25
123501
-8443
+8444
@@ -16120,17 +16542,17 @@
1
2
-224288
+224335
2
5
-20866
+20870
5
63265
-11633
+11636
@@ -16146,27 +16568,27 @@
1
2
-164639
+164673
2
3
-43454
+43463
3
4
-16436
+16439
4
8
-20570
+20574
8
8921
-11688
+11691
@@ -16182,27 +16604,27 @@
1
2
-522656
+522766
2
3
-143860
+143890
3
5
-64846
+64860
5
125
-59682
+59694
125
3043
-4243
+4244
@@ -16218,22 +16640,22 @@
1
2
-537580
+537692
2
3
-156821
+156854
3
9
-64331
+64345
9
3043
-36557
+36564
@@ -16249,17 +16671,17 @@
1
2
-701660
+701807
2
4
-61776
+61789
4
1522
-31853
+31859
@@ -16275,12 +16697,12 @@
1
2
-770289
+770451
2
134
-25000
+25005
@@ -16290,11 +16712,11 @@
fun_def
-1230544
+1230803
id
-1230544
+1230803
@@ -16323,11 +16745,11 @@
fun_decl_specifiers
-485338
+485334
id
-260688
+260686
name
@@ -16345,12 +16767,12 @@
1
2
-69263
+69262
2
3
-158200
+158199
3
@@ -16517,26 +16939,26 @@
fun_decl_empty_throws
-1420688
+1420986
fun_decl
-1420688
+1420986
fun_decl_noexcept
-32905
+32912
fun_decl
-32116
+32123
constant
-32785
+32792
@@ -16550,7 +16972,7 @@
1
2
-31326
+31333
2
@@ -16571,7 +16993,7 @@
1
2
-32664
+32671
2
@@ -16586,11 +17008,11 @@
fun_decl_empty_noexcept
-391877
+391959
fun_decl
-391877
+391959
@@ -16645,11 +17067,11 @@
param_decl_bind
-4650555
+4651530
id
-4650555
+4651530
index
@@ -16657,7 +17079,7 @@
fun_decl
-3071115
+3071759
@@ -16671,7 +17093,7 @@
1
2
-4650555
+4651530
@@ -16687,7 +17109,7 @@
1
2
-4650555
+4651530
@@ -16785,22 +17207,22 @@
1
2
-2194827
+2195287
2
3
-478478
+478579
3
4
-242665
+242716
4
65
-155143
+155176
@@ -16816,22 +17238,22 @@
1
2
-2194827
+2195287
2
3
-478478
+478579
3
4
-242665
+242716
4
65
-155143
+155176
@@ -16841,27 +17263,27 @@
var_decls
-5367960
+5368966
id
-5359112
+5360115
variable
-5131248
+5132204
type_id
-2007358
+2007769
name
-126283
+126310
location
-1232189
+1232448
@@ -16875,7 +17297,7 @@
1
2
-5359112
+5360115
@@ -16891,12 +17313,12 @@
1
2
-5350449
+5351451
2
4
-8662
+8664
@@ -16912,7 +17334,7 @@
1
2
-5359112
+5360115
@@ -16928,7 +17350,7 @@
1
2
-5359068
+5360071
2
@@ -16949,12 +17371,12 @@
1
2
-4943758
+4944675
2
9
-187490
+187529
@@ -16970,12 +17392,12 @@
1
2
-5095886
+5096835
2
7
-35362
+35369
@@ -16991,12 +17413,12 @@
1
2
-5113682
+5114635
2
3
-17565
+17569
@@ -17012,12 +17434,12 @@
1
2
-5023057
+5023990
2
9
-108191
+108214
@@ -17033,22 +17455,22 @@
1
2
-1580525
+1580878
2
3
-228861
+228876
3
11
-156941
+156974
11
5924
-41030
+41039
@@ -17064,22 +17486,22 @@
1
2
-1604713
+1605072
2
3
-219628
+219641
3
13
-151108
+151129
13
5424
-31908
+31925
@@ -17095,17 +17517,17 @@
1
2
-1832533
+1832906
2
5
-151218
+151238
5
772
-23607
+23623
@@ -17121,17 +17543,17 @@
1
2
-1758157
+1758515
2
4
-154441
+154496
4
3608
-94759
+94757
@@ -17147,42 +17569,42 @@
1
2
-52083
+52160
2
3
-19276
+19236
3
4
-10910
+10890
4
5
-7686
+7688
5
8
-10515
+10517
8
15
-9506
+9508
15
47
-9528
+9530
47
165630
-6776
+6777
@@ -17198,37 +17620,37 @@
1
2
-54901
+54978
2
3
-18826
+18786
3
4
-11809
+11789
4
6
-11195
+11197
6
11
-10734
+10736
11
27
-9484
+9486
27
164602
-9331
+9333
@@ -17244,32 +17666,32 @@
1
2
-76294
+76321
2
3
-16886
+16878
3
4
-8848
+8861
4
7
-10482
+10473
7
27
-9517
+9519
27
125807
-4254
+4255
@@ -17285,32 +17707,32 @@
1
2
-72807
+72822
2
3
-19024
+19028
3
4
-6973
+6975
4
7
-11184
+11186
7
21
-9747
+9749
21
10073
-6546
+6547
@@ -17326,22 +17748,22 @@
1
2
-892231
+892506
2
3
-149123
+149067
3
6
-113388
+113423
6
128450
-77445
+77450
@@ -17357,22 +17779,22 @@
1
2
-940981
+941266
2
3
-114551
+114487
3
6
-102632
+102664
6
128224
-74024
+74029
@@ -17388,17 +17810,17 @@
1
2
-1055160
+1055403
2
3
-85099
+85095
3
118388
-91930
+91949
@@ -17414,12 +17836,12 @@
1
2
-1223220
+1223476
2
52
-8969
+8971
@@ -17429,11 +17851,11 @@
var_def
-2437164
+2437554
id
-2437164
+2437554
@@ -17503,19 +17925,19 @@
type_decls
-1331883
+1332162
id
-1331883
+1332162
type_id
-1300139
+1300412
location
-1086618
+1086846
@@ -17529,7 +17951,7 @@
1
2
-1331883
+1332162
@@ -17545,7 +17967,7 @@
1
2
-1331883
+1332162
@@ -17561,12 +17983,12 @@
1
2
-1277047
+1277315
2
24
-23092
+23097
@@ -17582,12 +18004,12 @@
1
2
-1278341
+1278609
2
24
-21798
+21802
@@ -17603,12 +18025,12 @@
1
2
-1031168
+1031384
2
506
-55449
+55461
@@ -17624,12 +18046,12 @@
1
2
-1032648
+1032865
2
506
-53969
+53980
@@ -17639,45 +18061,45 @@
type_def
-937549
+937746
id
-937549
+937746
type_decl_top
-268642
+268698
type_decl
-268642
+268698
namespace_decls
-136842
+136871
id
-136842
+136871
namespace_id
-7675
+7677
location
-122226
+122252
bodylocation
-122555
+122581
@@ -17691,7 +18113,7 @@
1
2
-136842
+136871
@@ -17707,7 +18129,7 @@
1
2
-136842
+136871
@@ -17723,7 +18145,7 @@
1
2
-136842
+136871
@@ -17739,7 +18161,7 @@
1
2
-3618
+3619
2
@@ -17754,7 +18176,7 @@
4
7
-646
+647
7
@@ -17790,7 +18212,7 @@
1
2
-3618
+3619
2
@@ -17805,7 +18227,7 @@
4
7
-646
+647
7
@@ -17841,7 +18263,7 @@
1
2
-3618
+3619
2
@@ -17856,7 +18278,7 @@
4
7
-646
+647
7
@@ -17892,12 +18314,12 @@
1
2
-113849
+113873
2
8
-8377
+8379
@@ -17913,12 +18335,12 @@
1
2
-113849
+113873
2
8
-8377
+8379
@@ -17934,7 +18356,7 @@
1
2
-121481
+121506
2
@@ -17955,12 +18377,12 @@
1
2
-114540
+114564
2
11
-8015
+8017
@@ -17976,12 +18398,12 @@
1
2
-114540
+114564
2
9
-8015
+8017
@@ -17997,7 +18419,7 @@
1
2
-122160
+122186
2
@@ -18012,19 +18434,19 @@
usings
-291383
+291444
id
-291383
+291444
element_id
-46392
+46402
location
-23859
+23864
@@ -18038,7 +18460,7 @@
1
2
-291383
+291444
@@ -18054,7 +18476,7 @@
1
2
-291383
+291444
@@ -18070,7 +18492,7 @@
1
2
-39386
+39394
2
@@ -18080,7 +18502,7 @@
4
127
-3256
+3257
@@ -18096,7 +18518,7 @@
1
2
-39386
+39394
2
@@ -18106,7 +18528,7 @@
4
127
-3256
+3257
@@ -18122,12 +18544,12 @@
1
2
-18059
+18063
2
3
-2236
+2237
3
@@ -18153,12 +18575,12 @@
1
2
-18059
+18063
2
3
-2236
+2237
3
@@ -18178,15 +18600,15 @@
using_container
-458007
+458103
parent
-11228
+11230
child
-291383
+291444
@@ -18200,12 +18622,12 @@
1
2
-3212
+3213
2
4
-942
+943
4
@@ -18230,7 +18652,7 @@
178
179
-1260
+1261
179
@@ -18256,17 +18678,17 @@
1
2
-215812
+215858
2
3
-50362
+50372
3
11
-23136
+23140
13
@@ -18281,15 +18703,15 @@
static_asserts
-130659
+130658
id
-130659
+130658
condition
-130659
+130658
message
@@ -18299,6 +18721,10 @@
location
16721
+
+enclosing
+1724
+
@@ -18311,7 +18737,7 @@
1
2
-130659
+130658
@@ -18327,7 +18753,7 @@
1
2
-130659
+130658
@@ -18343,7 +18769,23 @@
1
2
-130659
+130658
+
+
+
+
+
+
+id
+enclosing
+
+
+12
+
+
+1
+2
+130658
@@ -18359,7 +18801,7 @@
1
2
-130659
+130658
@@ -18375,7 +18817,7 @@
1
2
-130659
+130658
@@ -18391,7 +18833,23 @@
1
2
-130659
+130658
+
+
+
+
+
+
+condition
+enclosing
+
+
+12
+
+
+1
+2
+130658
@@ -18489,7 +18947,7 @@
1
2
-27554
+27553
2
@@ -18501,6 +18959,42 @@
+message
+enclosing
+
+
+12
+
+
+1
+2
+23513
+
+
+2
+3
+182
+
+
+3
+4
+2654
+
+
+4
+11
+1301
+
+
+12
+21
+2055
+
+
+
+
+
+
location
id
@@ -18643,19 +19137,209 @@
+
+location
+enclosing
+
+
+12
+
+
+1
+2
+3441
+
+
+2
+3
+6193
+
+
+3
+4
+1125
+
+
+4
+5
+3715
+
+
+5
+6
+188
+
+
+13
+14
+2036
+
+
+16
+21
+19
+
+
+
+
+
+
+enclosing
+id
+
+
+12
+
+
+1
+2
+1171
+
+
+2
+3
+130
+
+
+3
+7
+136
+
+
+9
+108
+136
+
+
+170
+347
+130
+
+
+348
+10697
+19
+
+
+
+
+
+
+enclosing
+condition
+
+
+12
+
+
+1
+2
+1171
+
+
+2
+3
+130
+
+
+3
+7
+136
+
+
+9
+108
+136
+
+
+170
+347
+130
+
+
+348
+10697
+19
+
+
+
+
+
+
+enclosing
+message
+
+
+12
+
+
+1
+2
+1327
+
+
+2
+5
+136
+
+
+5
+180
+130
+
+
+211
+2870
+130
+
+
+
+
+
+
+enclosing
+location
+
+
+12
+
+
+1
+2
+1314
+
+
+2
+5
+149
+
+
+5
+180
+130
+
+
+211
+1886
+130
+
+
+
+
+
params
-4644074
+4645049
id
-4627013
+4627983
function
-3043297
+3043935
index
@@ -18663,7 +19347,7 @@
type_id
-1856458
+1856848
@@ -18677,12 +19361,12 @@
1
2
-4626344
+4627314
2
69
-668
+669
@@ -18698,7 +19382,7 @@
1
2
-4627013
+4627983
@@ -18714,12 +19398,12 @@
1
2
-4611969
+4612936
2
4
-15043
+15047
@@ -18735,22 +19419,22 @@
1
2
-2166219
+2166673
2
3
-475024
+475124
3
4
-244409
+244460
4
65
-157643
+157676
@@ -18766,22 +19450,22 @@
1
2
-2166219
+2166673
2
3
-475024
+475124
3
4
-244409
+244460
4
65
-157643
+157676
@@ -18797,22 +19481,22 @@
1
2
-2282777
+2283256
2
3
-470880
+470978
3
5
-254277
+254331
5
20
-35362
+35369
@@ -18951,22 +19635,22 @@
1
2
-1504450
+1504765
2
3
-186678
+186717
3
14
-139463
+139492
14
5175
-25866
+25871
@@ -18982,22 +19666,22 @@
1
2
-1524307
+1524627
2
3
-179814
+179852
3
23
-139693
+139723
23
4690
-12642
+12645
@@ -19013,12 +19697,12 @@
1
2
-1744791
+1745157
2
65
-111667
+111690
@@ -19028,11 +19712,11 @@
overrides
-159059
+159058
new
-124550
+124549
old
@@ -19050,7 +19734,7 @@
1
2
-90047
+90046
2
@@ -19116,19 +19800,19 @@
membervariables
-310243
+310308
id
-305626
+305691
type_id
-132895
+132923
name
-53213
+53224
@@ -19142,12 +19826,12 @@
1
2
-301164
+301227
2
7
-4462
+4463
@@ -19163,7 +19847,7 @@
1
2
-305626
+305691
@@ -19179,22 +19863,22 @@
1
2
-108048
+108071
2
3
-12269
+12272
3
9
-10054
+10056
9
1712
-2521
+2522
@@ -19210,17 +19894,17 @@
1
2
-115099
+115123
2
3
-9089
+9091
3
266
-8706
+8708
@@ -19236,32 +19920,32 @@
1
2
-28092
+28098
2
3
-8168
+8170
3
4
-5372
+5373
4
6
-4013
+4014
6
15
-4089
+4090
15
1967
-3475
+3476
@@ -19277,12 +19961,12 @@
1
2
-34287
+34294
2
3
-6853
+6854
3
@@ -19292,12 +19976,12 @@
4
7
-4298
+4299
7
242
-3991
+3992
262
@@ -19312,11 +19996,11 @@
globalvariables
-301154
+301151
id
-301146
+301143
type_id
@@ -19338,7 +20022,7 @@
1
2
-301138
+301135
2
@@ -19359,7 +20043,7 @@
1
2
-301146
+301143
@@ -19447,12 +20131,12 @@
1
2
-291380
+291383
2
33
-3766
+3763
@@ -19468,12 +20152,12 @@
1
2
-294542
+294545
2
12
-604
+601
@@ -19483,11 +20167,11 @@
localvariables
-521298
+521293
id
-521220
+521215
type_id
@@ -19495,7 +20179,7 @@
name
-75239
+75238
@@ -19509,7 +20193,7 @@
1
2
-521142
+521137
2
@@ -19530,7 +20214,7 @@
1
2
-521220
+521215
@@ -19546,12 +20230,12 @@
1
2
-26474
+26473
2
3
-6760
+6759
3
@@ -19592,7 +20276,7 @@
1
2
-35700
+35699
2
@@ -19623,12 +20307,12 @@
1
2
-42961
+42960
2
3
-12915
+12914
3
@@ -19638,12 +20322,12 @@
4
7
-6721
+6720
7
31
-5706
+5705
31
@@ -19752,11 +20436,11 @@
enumconstants
-93840
+93839
id
-93840
+93839
parent
@@ -19772,11 +20456,11 @@
name
-72903
+72902
location
-75870
+75869
@@ -19790,7 +20474,7 @@
1
2
-93840
+93839
@@ -19806,7 +20490,7 @@
1
2
-93840
+93839
@@ -19822,7 +20506,7 @@
1
2
-93840
+93839
@@ -19838,7 +20522,7 @@
1
2
-93840
+93839
@@ -19854,7 +20538,7 @@
1
2
-93840
+93839
@@ -20620,7 +21304,7 @@
1
2
-65544
+65543
2
@@ -20651,7 +21335,7 @@
2
3
-6194
+6193
3
@@ -20672,7 +21356,7 @@
1
2
-67880
+67879
2
@@ -20693,7 +21377,7 @@
1
2
-61146
+61145
2
@@ -20740,7 +21424,7 @@
1
2
-70580
+70579
2
@@ -20761,12 +21445,12 @@
1
2
-70691
+70690
2
229
-5179
+5178
@@ -20782,7 +21466,7 @@
1
2
-72272
+72271
2
@@ -21556,15 +22240,15 @@
derivedtypes
-4416924
+4463123
id
-4416924
+4463123
name
-2172217
+2202887
kind
@@ -21572,7 +22256,7 @@
type_id
-2605685
+2610563
@@ -21586,7 +22270,7 @@
1
2
-4416924
+4463123
@@ -21602,7 +22286,7 @@
1
2
-4416924
+4463123
@@ -21618,7 +22302,7 @@
1
2
-4416924
+4463123
@@ -21634,17 +22318,17 @@
1
2
-1570229
+1593501
2
3
-487437
+493889
3
45177
-114551
+115496
@@ -21660,7 +22344,7 @@
1
2
-2172184
+2202855
2
@@ -21681,17 +22365,17 @@
1
2
-1570470
+1593743
2
3
-487206
+493659
3
45159
-114540
+115485
@@ -21720,8 +22404,8 @@
10
-27116
-27117
+31133
+31134
10
@@ -21735,8 +22419,8 @@
10
-96890
-96891
+97001
+97002
10
@@ -21776,8 +22460,8 @@
10
-14760
-14761
+17432
+17433
10
@@ -21786,8 +22470,8 @@
10
-48258
-48259
+48341
+48342
10
@@ -21822,8 +22506,8 @@
10
-27116
-27117
+31133
+31134
10
@@ -21837,8 +22521,8 @@
10
-96559
-96560
+96670
+96671
10
@@ -21860,22 +22544,22 @@
1
2
-1545963
+1541056
2
3
-372633
+369487
3
4
-633753
+628085
4
202
-53333
+71934
@@ -21891,22 +22575,22 @@
1
2
-1547180
+1542273
2
3
-372480
+369334
3
4
-632690
+627021
4
198
-53333
+71934
@@ -21922,22 +22606,22 @@
1
2
-1547498
+1542591
2
3
-373763
+370617
3
4
-632646
+626977
4
7
-51776
+70377
@@ -21947,11 +22631,11 @@
pointerishsize
-3331139
+3375893
id
-3331139
+3375893
size
@@ -21973,7 +22657,7 @@
1
2
-3331139
+3375893
@@ -21989,7 +22673,7 @@
1
2
-3331139
+3375893
@@ -22008,8 +22692,8 @@
10
-303777
-303778
+307794
+307795
10
@@ -22040,8 +22724,8 @@
12
-303798
-303799
+307815
+307816
10
@@ -22068,11 +22752,11 @@
arraysizes
-17193
+17196
id
-17193
+17196
num_elements
@@ -22080,7 +22764,7 @@
bytesize
-2510
+2511
alignment
@@ -22098,7 +22782,7 @@
1
2
-17193
+17196
@@ -22114,7 +22798,7 @@
1
2
-17193
+17196
@@ -22130,7 +22814,7 @@
1
2
-17193
+17196
@@ -22151,7 +22835,7 @@
2
3
-1271
+1272
3
@@ -22192,7 +22876,7 @@
1
2
-1589
+1590
2
@@ -22228,7 +22912,7 @@
1
2
-1589
+1590
2
@@ -22315,7 +22999,7 @@
1
2
-1907
+1908
2
@@ -22346,7 +23030,7 @@
1
2
-1951
+1952
2
@@ -22504,15 +23188,15 @@
typedefbase
-1819320
+1819702
id
-1819320
+1819702
type_id
-847209
+847375
@@ -22526,7 +23210,7 @@
1
2
-1819320
+1819702
@@ -22542,22 +23226,22 @@
1
2
-656517
+656643
2
3
-87895
+87913
3
6
-69583
+69598
6
5503
-33212
+33219
@@ -22567,19 +23251,19 @@
decltypes
-46995
+47005
id
-46995
+47005
expr
-43432
+43441
base_type
-8629
+8631
parentheses_would_change_meaning
@@ -22597,7 +23281,7 @@
1
2
-46995
+47005
@@ -22613,7 +23297,7 @@
1
2
-46995
+47005
@@ -22629,7 +23313,7 @@
1
2
-46995
+47005
@@ -22645,12 +23329,12 @@
1
2
-40153
+40162
2
4
-3278
+3279
@@ -22666,12 +23350,12 @@
1
2
-40153
+40162
2
4
-3278
+3279
@@ -22687,7 +23371,7 @@
1
2
-43432
+43441
@@ -22703,12 +23387,12 @@
1
2
-5822
+5823
2
3
-2521
+2522
3
@@ -22729,12 +23413,12 @@
1
2
-2313
+2314
2
3
-5701
+5702
3
@@ -22755,7 +23439,7 @@
1
2
-8629
+8631
@@ -22828,15 +23512,15 @@
usertypes
-4193074
+4193942
id
-4193074
+4193942
name
-879226
+879411
kind
@@ -22854,7 +23538,7 @@
1
2
-4193074
+4193942
@@ -22870,7 +23554,7 @@
1
2
-4193074
+4193942
@@ -22886,22 +23570,22 @@
1
2
-577350
+577471
2
3
-194759
+194800
3
7
-69298
+69313
7
32744
-37818
+37826
@@ -22917,12 +23601,12 @@
1
2
-826595
+826768
2
10
-52631
+52642
@@ -22981,8 +23665,8 @@
10
-95210
-95211
+95209
+95210
10
@@ -23059,11 +23743,11 @@
usertypesize
-1386324
+1386604
id
-1386324
+1386604
size
@@ -23085,7 +23769,7 @@
1
2
-1386324
+1386604
@@ -23101,7 +23785,7 @@
1
2
-1386324
+1386604
@@ -23161,7 +23845,7 @@
284
-99381
+99380
109
@@ -23237,8 +23921,8 @@
10
-113882
-113883
+113881
+113882
10
@@ -23354,15 +24038,15 @@
mangled_name
-4189795
+4190663
id
-4189795
+4190663
mangled_name
-483588
+483689
@@ -23376,7 +24060,7 @@
1
2
-4189795
+4190663
@@ -23392,32 +24076,32 @@
1
2
-292030
+292091
2
3
-62281
+62294
3
4
-33333
+33340
4
7
-36962
+36970
7
24
-37061
+37069
24
8580
-21918
+21923
@@ -23427,59 +24111,59 @@
is_pod_class
-589104
+589228
id
-589104
+589228
is_standard_layout_class
-1158976
+1159208
id
-1158976
+1159208
is_complete
-1364822
+1365097
id
-1364822
+1365097
is_class_template
-225308
+225355
id
-225308
+225355
class_instantiation
-1157583
+1157815
to
-1156015
+1156247
from
-68015
+68030
@@ -23493,7 +24177,7 @@
1
2
-1154535
+1154766
2
@@ -23514,47 +24198,47 @@
1
2
-19956
+19960
2
3
-11995
+11998
3
4
-6853
+6854
4
5
-4616
+4617
5
7
-5635
+5637
7
11
-6052
+6053
11
20
-5197
+5198
20
84
-5109
+5110
84
4845
-2598
+2599
@@ -23564,11 +24248,11 @@
class_template_argument
-3035457
+3036083
type_id
-1392563
+1392844
index
@@ -23576,7 +24260,7 @@
arg_type
-860750
+860931
@@ -23590,27 +24274,27 @@
1
2
-567010
+567118
2
3
-433654
+433744
3
4
-244935
+244987
4
7
-122785
+122811
7
113
-24177
+24182
@@ -23626,22 +24310,22 @@
1
2
-593238
+593351
2
3
-445879
+445973
3
4
-258389
+258443
4
113
-95055
+95075
@@ -23686,7 +24370,7 @@
13712
-123926
+123925
43
@@ -23749,27 +24433,27 @@
1
2
-522185
+522294
2
3
-187402
+187441
3
4
-56699
+56711
4
11
-67379
+67393
11
-11852
-27083
+11851
+27089
@@ -23785,17 +24469,17 @@
1
2
-746320
+746476
2
3
-95526
+95546
3
22
-18903
+18907
@@ -23805,11 +24489,11 @@
class_template_argument_value
-345736
+345798
type_id
-223806
+223842
index
@@ -23817,7 +24501,7 @@
arg_value
-328181
+328239
@@ -23831,17 +24515,17 @@
1
2
-201448
+201479
2
3
-13706
+13709
3
14
-8651
+8653
@@ -23857,17 +24541,17 @@
1
2
-189825
+189854
2
3
-16918
+16922
3
37
-16787
+16790
44
@@ -23931,8 +24615,8 @@
10
-9813
-9814
+9812
+9813
10
@@ -23992,8 +24676,8 @@
10
-12053
-12054
+12052
+12053
10
@@ -24010,12 +24694,12 @@
1
2
-310944
+310999
2
4
-17236
+17240
@@ -24031,7 +24715,7 @@
1
2
-328181
+328239
@@ -24041,15 +24725,15 @@
is_proxy_class_for
-46370
+46380
id
-46370
+46380
templ_param_id
-46370
+46380
@@ -24063,7 +24747,7 @@
1
2
-46370
+46380
@@ -24079,7 +24763,7 @@
1
2
-46370
+46380
@@ -24089,19 +24773,19 @@
type_mentions
-1677549
+1677532
id
-1677549
+1677532
type_id
-67210
+67209
location
-1647041
+1647024
kind
@@ -24119,7 +24803,7 @@
1
2
-1677549
+1677532
@@ -24135,7 +24819,7 @@
1
2
-1677549
+1677532
@@ -24151,7 +24835,7 @@
1
2
-1677549
+1677532
@@ -24167,7 +24851,7 @@
1
2
-30072
+30071
2
@@ -24177,7 +24861,7 @@
3
4
-3637
+3636
4
@@ -24192,7 +24876,7 @@
13
35
-5192
+5191
35
@@ -24213,7 +24897,7 @@
1
2
-30072
+30071
2
@@ -24223,7 +24907,7 @@
3
4
-3637
+3636
4
@@ -24238,7 +24922,7 @@
13
35
-5192
+5191
35
@@ -24259,7 +24943,7 @@
1
2
-65967
+65966
2
@@ -24280,7 +24964,7 @@
1
2
-1616871
+1616855
2
@@ -24301,7 +24985,7 @@
1
2
-1616871
+1616855
2
@@ -24322,7 +25006,7 @@
1
2
-1647041
+1647024
@@ -24395,26 +25079,26 @@
is_function_template
-983580
+983786
id
-983580
+983786
function_instantiation
-708184
+708332
to
-708184
+708332
from
-129310
+129337
@@ -24428,7 +25112,7 @@
1
2
-708184
+708332
@@ -24444,37 +25128,37 @@
1
2
-60998
+61010
2
3
-30833
+30839
3
4
-7346
+7348
4
5
-8815
+8817
5
10
-10000
+10002
10
71
-9704
+9706
71
653
-1611
+1612
@@ -24484,11 +25168,11 @@
function_template_argument
-1910187
+1910587
function_id
-1054173
+1054394
index
@@ -24496,7 +25180,7 @@
arg_type
-338390
+338461
@@ -24510,22 +25194,22 @@
1
2
-583479
+583601
2
3
-290955
+291016
3
4
-127742
+127768
4
21
-51995
+52006
@@ -24541,22 +25225,22 @@
1
2
-597898
+598023
2
3
-288642
+288702
3
4
-110921
+110945
4
21
-56710
+56722
@@ -24794,27 +25478,27 @@
1
2
-226086
+226134
2
3
-45109
+45119
3
6
-27664
+27670
6
19
-25603
+25608
19
2030
-13925
+13928
@@ -24830,12 +25514,12 @@
1
2
-314771
+314837
2
12
-23618
+23623
@@ -24845,11 +25529,11 @@
function_template_argument_value
-198027
+198069
function_id
-107062
+107084
index
@@ -24857,7 +25541,7 @@
arg_value
-170571
+170607
@@ -24871,12 +25555,12 @@
1
2
-101546
+101567
2
14
-5515
+5516
@@ -24892,17 +25576,17 @@
1
2
-84945
+84963
2
3
-16173
+16176
3
113
-5943
+5944
@@ -25030,12 +25714,12 @@
1
2
-143619
+143649
2
3
-26447
+26453
3
@@ -25056,7 +25740,7 @@
1
2
-170571
+170607
@@ -25066,26 +25750,26 @@
is_variable_template
-17807
+17810
id
-17807
+17810
variable_instantiation
-35800
+35808
to
-35800
+35808
from
-6469
+6470
@@ -25099,7 +25783,7 @@
1
2
-35800
+35808
@@ -25115,12 +25799,12 @@
1
2
-2236
+2237
2
3
-1907
+1908
3
@@ -25507,15 +26191,15 @@
routinetypes
-430397
+430487
id
-430397
+430487
return_type
-177533
+177571
@@ -25529,7 +26213,7 @@
1
2
-430397
+430487
@@ -25545,22 +26229,22 @@
1
2
-143082
+143112
2
3
-18125
+18128
3
9
-13322
+13325
9
8267
-3004
+3005
@@ -25570,11 +26254,11 @@
routinetypeargs
-719708
+719859
routine
-351778
+351852
index
@@ -25582,7 +26266,7 @@
type_id
-205527
+205570
@@ -25596,27 +26280,27 @@
1
2
-161897
+161931
2
3
-95932
+95952
3
4
-53925
+53937
4
6
-32324
+32331
6
33
-7697
+7699
@@ -25632,22 +26316,22 @@
1
2
-186711
+186750
2
3
-96689
+96709
3
4
-47653
+47663
4
22
-20723
+20728
@@ -25805,27 +26489,27 @@
1
2
-122391
+122416
2
3
-40833
+40842
3
4
-13212
+13215
4
7
-16633
+16637
7
1349
-12456
+12458
@@ -25841,17 +26525,17 @@
1
2
-154079
+154112
2
3
-39430
+39438
3
33
-12017
+12020
@@ -25861,19 +26545,19 @@
ptrtomembers
-12631
+12634
id
-12631
+12634
type_id
-9396
+9398
class_id
-6359
+6361
@@ -25887,7 +26571,7 @@
1
2
-12631
+12634
@@ -25903,7 +26587,7 @@
1
2
-12631
+12634
@@ -25919,7 +26603,7 @@
1
2
-9035
+9037
2
@@ -25940,7 +26624,7 @@
1
2
-9035
+9037
2
@@ -25961,7 +26645,7 @@
1
2
-5339
+5341
2
@@ -25987,7 +26671,7 @@
1
2
-5339
+5341
2
@@ -26055,11 +26739,11 @@
typespecifiers
-1331696
+1333215
type_id
-1324832
+1326328
spec_id
@@ -26077,12 +26761,12 @@
1
2
-1317968
+1319440
2
3
-6864
+6887
@@ -26111,8 +26795,8 @@
10
-955
-956
+957
+958
10
@@ -26126,8 +26810,8 @@
10
-96425
-96426
+96536
+96537
10
@@ -26138,11 +26822,11 @@
funspecifiers
-11101011
+11122577
func_id
-3417148
+3417865
spec_id
@@ -26160,27 +26844,27 @@
1
2
-342249
+342321
2
3
-436910
+421977
3
4
-842560
+853879
4
5
-1677016
+1680922
5
8
-118410
+118764
@@ -26254,8 +26938,8 @@
10
-137685
-137686
+139439
+139440
10
@@ -26281,11 +26965,11 @@
varspecifiers
-1112020
+1112009
var_id
-924807
+924798
spec_id
@@ -26303,7 +26987,7 @@
1
2
-786197
+786189
2
@@ -26384,11 +27068,11 @@
attributes
-413730
+413727
id
-413730
+413727
kind
@@ -26404,7 +27088,7 @@
location
-90510
+90509
@@ -26418,7 +27102,7 @@
1
2
-413730
+413727
@@ -26434,7 +27118,7 @@
1
2
-413730
+413727
@@ -26450,7 +27134,7 @@
1
2
-413730
+413727
@@ -26466,7 +27150,7 @@
1
2
-413730
+413727
@@ -26825,7 +27509,7 @@
1
2
-90510
+90509
@@ -26877,7 +27561,7 @@
1
2
-90510
+90509
@@ -26887,11 +27571,11 @@
attribute_args
-152615
+152614
id
-152615
+152614
kind
@@ -26899,7 +27583,7 @@
attribute
-151316
+151315
index
@@ -26907,7 +27591,7 @@
location
-57362
+57361
@@ -26921,7 +27605,7 @@
1
2
-152615
+152614
@@ -26937,7 +27621,7 @@
1
2
-152615
+152614
@@ -26953,7 +27637,7 @@
1
2
-152615
+152614
@@ -26969,7 +27653,7 @@
1
2
-152615
+152614
@@ -27069,7 +27753,7 @@
1
2
-150538
+150537
2
@@ -27090,7 +27774,7 @@
1
2
-150826
+150825
2
@@ -27111,7 +27795,7 @@
1
2
-150538
+150537
2
@@ -27132,7 +27816,7 @@
1
2
-150543
+150542
2
@@ -27272,7 +27956,7 @@
2
3
-8429
+8428
3
@@ -27303,7 +27987,7 @@
1
2
-51567
+51566
2
@@ -27324,7 +28008,7 @@
1
2
-27281
+27280
2
@@ -27360,7 +28044,7 @@
1
2
-57357
+57356
3
@@ -27375,11 +28059,11 @@
attribute_arg_value
-152591
+152590
arg
-152591
+152590
value
@@ -27397,7 +28081,7 @@
1
2
-152591
+152590
@@ -27544,15 +28228,15 @@
typeattributes
-19320
+19324
type_id
-17938
+17942
spec_id
-19320
+19324
@@ -27566,7 +28250,7 @@
1
2
-17236
+17240
2
@@ -27587,7 +28271,7 @@
1
2
-19320
+19324
@@ -27597,15 +28281,15 @@
funcattributes
-304333
+304396
func_id
-164211
+164245
spec_id
-304333
+304396
@@ -27619,22 +28303,22 @@
1
2
-89671
+89690
2
3
-12576
+12579
3
4
-59956
+59969
4
14
-2006
+2007
@@ -27650,7 +28334,7 @@
1
2
-304333
+304396
@@ -27660,7 +28344,7 @@
varattributes
-371224
+371223
var_id
@@ -27668,7 +28352,7 @@
spec_id
-371224
+371223
@@ -27682,12 +28366,12 @@
1
2
-273654
+273655
2
3
-48764
+48763
14
@@ -27708,7 +28392,7 @@
1
2
-371224
+371223
@@ -27766,15 +28450,15 @@
unspecifiedtype
-9068477
+9115641
type_id
-9068477
+9115641
unspecified_type_id
-4975940
+5012847
@@ -27788,7 +28472,7 @@
1
2
-9068477
+9115641
@@ -27804,17 +28488,17 @@
1
2
-2708920
+2736095
2
3
-1952501
+1962057
3
7950
-314519
+314695
@@ -27824,19 +28508,19 @@
member
-4920765
+4921797
parent
-814336
+814507
index
-2675
+2676
child
-4905205
+4906234
@@ -27850,47 +28534,47 @@
1
2
-42313
+42322
2
3
-223740
+223787
3
4
-204584
+204627
4
5
-86908
+86926
5
7
-65888
+65902
7
9
-61601
+61614
9
15
-62039
+62052
15
47
-61173
+61186
47
245
-6085
+6086
@@ -27906,47 +28590,47 @@
1
2
-41655
+41664
2
3
-223564
+223611
3
4
-199343
+199385
4
5
-89715
+89734
5
7
-66360
+66373
7
9
-61678
+61690
9
15
-62960
+62974
15
42
-61261
+61274
42
281
-7796
+7797
@@ -28104,7 +28788,7 @@
1
2
-4905205
+4906234
@@ -28120,12 +28804,12 @@
1
2
-4889854
+4890880
2
7
-15350
+15354
@@ -28135,15 +28819,15 @@
enclosingfunction
-125055
+125081
child
-125055
+125081
parent
-71392
+71407
@@ -28157,7 +28841,7 @@
1
2
-125055
+125081
@@ -28173,22 +28857,22 @@
1
2
-38333
+38341
2
3
-21074
+21079
3
4
-6535
+6536
4
7
-5372
+5373
7
@@ -28203,15 +28887,15 @@
derivations
-390188
+390270
derivation
-390188
+390270
sub
-364377
+364453
index
@@ -28219,11 +28903,11 @@
super
-235659
+235708
location
-86963
+86981
@@ -28237,7 +28921,7 @@
1
2
-390188
+390270
@@ -28253,7 +28937,7 @@
1
2
-390188
+390270
@@ -28269,7 +28953,7 @@
1
2
-390188
+390270
@@ -28285,7 +28969,7 @@
1
2
-390188
+390270
@@ -28301,12 +28985,12 @@
1
2
-341811
+341883
2
7
-22565
+22570
@@ -28322,12 +29006,12 @@
1
2
-351427
+351501
2
7
-12949
+12952
@@ -28343,12 +29027,12 @@
1
2
-341822
+341893
2
7
-22554
+22559
@@ -28364,12 +29048,12 @@
1
2
-351416
+351490
2
7
-12960
+12963
@@ -28549,12 +29233,12 @@
1
2
-220878
+220924
2
1142
-14780
+14783
@@ -28570,12 +29254,12 @@
1
2
-220889
+220935
2
1142
-14769
+14772
@@ -28591,7 +29275,7 @@
1
2
-235209
+235259
2
@@ -28612,12 +29296,12 @@
1
2
-228301
+228349
2
439
-7357
+7359
@@ -28633,22 +29317,22 @@
1
2
-66338
+66352
2
3
-8388
+8389
3
7
-6611
+6613
7
795
-5625
+5626
@@ -28664,22 +29348,22 @@
1
2
-68574
+68589
2
3
-6370
+6371
3
8
-7039
+7040
8
795
-4978
+4979
@@ -28695,7 +29379,7 @@
1
2
-86941
+86959
2
@@ -28716,22 +29400,22 @@
1
2
-69276
+69291
2
3
-8201
+8203
3
9
-6524
+6525
9
795
-2960
+2961
@@ -28741,11 +29425,11 @@
derspecifiers
-392568
+392650
der_id
-390155
+390237
spec_id
@@ -28763,7 +29447,7 @@
1
2
-387743
+387824
2
@@ -28809,11 +29493,11 @@
direct_base_offsets
-310495
+310560
der_id
-310495
+310560
offset
@@ -28831,7 +29515,7 @@
1
2
-310495
+310560
@@ -28917,11 +29601,11 @@
virtual_base_offsets
-6337
+6339
sub
-3508
+3509
super
@@ -28974,7 +29658,7 @@
1
2
-2960
+2961
2
@@ -29208,23 +29892,23 @@
frienddecls
-240297
+240347
id
-240297
+240347
type_id
-27280
+27286
decl_id
-49035
+49045
location
-7302
+7304
@@ -29238,7 +29922,7 @@
1
2
-240297
+240347
@@ -29254,7 +29938,7 @@
1
2
-240297
+240347
@@ -29270,7 +29954,7 @@
1
2
-240297
+240347
@@ -29286,17 +29970,17 @@
1
2
-6217
+6218
2
3
-10230
+10232
3
5
-1984
+1985
5
@@ -29306,7 +29990,7 @@
6
8
-2313
+2314
8
@@ -29337,17 +30021,17 @@
1
2
-6217
+6218
2
3
-10230
+10232
3
5
-1984
+1985
5
@@ -29357,7 +30041,7 @@
6
8
-2313
+2314
8
@@ -29388,12 +30072,12 @@
1
2
-25701
+25707
2
31
-1578
+1579
@@ -29409,12 +30093,12 @@
1
2
-33684
+33691
2
3
-4989
+4990
3
@@ -29424,12 +30108,12 @@
7
23
-3804
+3805
23
394
-2532
+2533
@@ -29445,12 +30129,12 @@
1
2
-33684
+33691
2
3
-4989
+4990
3
@@ -29460,12 +30144,12 @@
7
23
-3804
+3805
23
394
-2532
+2533
@@ -29481,7 +30165,7 @@
1
2
-48498
+48508
2
@@ -29502,12 +30186,12 @@
1
2
-6239
+6240
2
3
-942
+943
3
@@ -29528,7 +30212,7 @@
1
2
-6864
+6865
2
@@ -29549,7 +30233,7 @@
1
2
-6250
+6251
2
@@ -29569,19 +30253,19 @@
comments
-1580009
+1580341
id
-1580009
+1580341
contents
-784018
+784182
location
-1580009
+1580341
@@ -29595,7 +30279,7 @@
1
2
-1580009
+1580341
@@ -29611,7 +30295,7 @@
1
2
-1580009
+1580341
@@ -29627,17 +30311,17 @@
1
2
-663819
+663959
2
3
-75088
+75103
3
10738
-45109
+45119
@@ -29653,17 +30337,17 @@
1
2
-663819
+663959
2
3
-75088
+75103
3
10738
-45109
+45119
@@ -29679,7 +30363,7 @@
1
2
-1580009
+1580341
@@ -29695,7 +30379,7 @@
1
2
-1580009
+1580341
@@ -29705,15 +30389,15 @@
commentbinding
-713206
+713311
id
-618260
+618433
element
-684434
+684533
@@ -29727,17 +30411,17 @@
1
2
-556867
+557093
2
3
-49079
+49045
3
97
-12313
+12294
@@ -29753,12 +30437,12 @@
1
2
-655661
+655755
2
3
-28772
+28778
@@ -29768,15 +30452,15 @@
exprconv
-6443933
+6443797
converted
-6443639
+6443503
conversion
-6443933
+6443797
@@ -29790,7 +30474,7 @@
1
2
-6443346
+6443210
2
@@ -29811,7 +30495,7 @@
1
2
-6443933
+6443797
@@ -29821,22 +30505,22 @@
compgenerated
-6708045
+6707808
id
-6708045
+6707808
synthetic_destructor_call
-59605
+59596
element
-46392
+46380
i
@@ -29844,7 +30528,7 @@
destructor_call
-49463
+49451
@@ -29858,17 +30542,17 @@
1
2
-36951
+36937
2
3
-6765
+6766
3
29
-2675
+2676
@@ -29884,17 +30568,17 @@
1
2
-36951
+36937
2
3
-6765
+6766
3
29
-2675
+2676
@@ -29928,8 +30612,8 @@
21
-4231
-4232
+4229
+4230
10
@@ -29964,8 +30648,8 @@
21
-3565
-3566
+3563
+3564
10
@@ -29982,17 +30666,17 @@
1
2
-43618
+43605
2
3
-3618
+3619
3
26
-2225
+2226
@@ -30008,7 +30692,7 @@
1
2
-49463
+49451
@@ -30018,15 +30702,15 @@
namespaces
-7686
+7688
id
-7686
+7688
name
-4133
+4134
@@ -30040,7 +30724,7 @@
1
2
-7686
+7688
@@ -30056,7 +30740,7 @@
1
2
-3475
+3476
2
@@ -30087,15 +30771,15 @@
namespacembrs
-1603036
+1603361
parentid
-7160
+7161
memberid
-1603036
+1603361
@@ -30169,7 +30853,7 @@
778
39485
-317
+318
@@ -30185,7 +30869,7 @@
1
2
-1603036
+1603361
@@ -30195,11 +30879,11 @@
exprparents
-13453568
+13453475
expr_id
-13453379
+13453286
child_index
@@ -30207,7 +30891,7 @@
parent_id
-9530495
+9530420
@@ -30221,7 +30905,7 @@
1
2
-13453373
+13453279
2
@@ -30242,7 +30926,7 @@
1
2
-13453197
+13453104
2
@@ -30292,7 +30976,7 @@
6420
-1188854
+1188857
45
@@ -30338,7 +31022,7 @@
6405
-1188867
+1188870
45
@@ -30355,17 +31039,17 @@
1
2
-6759182
+6759115
2
3
-2187728
+2187726
3
1681
-583583
+583578
@@ -30381,17 +31065,17 @@
1
2
-6759202
+6759135
2
3
-2187709
+2187707
3
480
-583583
+583578
@@ -30401,11 +31085,11 @@
expr_isload
-5007422
+5007373
expr_id
-5007422
+5007373
@@ -30458,8 +31142,8 @@
1
-13857
-13858
+13714
+13715
1
@@ -30473,8 +31157,8 @@
1
-4155321
-4155322
+4155464
+4155465
1
@@ -30485,11 +31169,11 @@
iscall
-2320354
+2320215
caller
-2320354
+2320215
kind
@@ -30507,7 +31191,7 @@
1
2
-2320354
+2320215
@@ -30526,13 +31210,13 @@
10
-6429
-6430
+6428
+6429
10
-203800
-203801
+203744
+203745
10
@@ -30543,11 +31227,11 @@
numtemplatearguments
-164694
+164695
expr_id
-164694
+164695
num
@@ -30565,7 +31249,7 @@
1
2
-164694
+164695
@@ -30594,8 +31278,8 @@
10
-14307
-14308
+14304
+14305
10
@@ -30654,23 +31338,23 @@
namequalifiers
-1114421
+1114410
id
-1114421
+1114410
qualifiableelement
-1114421
+1114410
qualifyingelement
-38862
+38861
location
-504707
+504702
@@ -30684,7 +31368,7 @@
1
2
-1114421
+1114410
@@ -30700,7 +31384,7 @@
1
2
-1114421
+1114410
@@ -30716,7 +31400,7 @@
1
2
-1114421
+1114410
@@ -30732,7 +31416,7 @@
1
2
-1114421
+1114410
@@ -30748,7 +31432,7 @@
1
2
-1114421
+1114410
@@ -30764,7 +31448,7 @@
1
2
-1114421
+1114410
@@ -30862,7 +31546,7 @@
1
2
-24789
+24788
2
@@ -30882,7 +31566,7 @@
11
16728
-2596
+2595
@@ -30898,17 +31582,17 @@
1
2
-383625
+383621
2
3
-56715
+56714
3
7
-38758
+38757
7
@@ -30929,17 +31613,17 @@
1
2
-383625
+383621
2
3
-56715
+56714
3
7
-38758
+38757
7
@@ -30960,7 +31644,7 @@
1
2
-447849
+447844
2
@@ -30980,15 +31664,15 @@
varbind
-5434639
+5434586
expr
-5434516
+5434462
var
-1532809
+1532794
@@ -31002,7 +31686,7 @@
1
2
-5434392
+5434339
2
@@ -31023,32 +31707,32 @@
1
2
-679968
+679961
2
3
-308900
+308897
3
4
-232743
+232741
4
5
-92819
+92818
5
9
-133060
+133059
9
6150
-85317
+85316
@@ -31058,15 +31742,15 @@
funbind
-2416575
+2416551
expr
-2119711
+2119690
fun
-434036
+434031
@@ -31080,12 +31764,12 @@
1
2
-1823088
+1823070
2
3
-296447
+296444
3
@@ -31106,7 +31790,7 @@
1
2
-252054
+252052
2
@@ -31141,11 +31825,11 @@
expr_allocator
-30329
+30335
expr
-30329
+30335
func
@@ -31167,7 +31851,7 @@
1
2
-30329
+30335
@@ -31183,7 +31867,7 @@
1
2
-30329
+30335
@@ -31292,11 +31976,11 @@
expr_deallocator
-33300
+33307
expr
-33300
+33307
func
@@ -31318,7 +32002,7 @@
1
2
-33300
+33307
@@ -31334,7 +32018,7 @@
1
2
-33300
+33307
@@ -31464,15 +32148,15 @@
expr_cond_guard
-154262
+154261
cond
-154262
+154261
guard
-154262
+154261
@@ -31486,7 +32170,7 @@
1
2
-154262
+154261
@@ -31502,7 +32186,7 @@
1
2
-154262
+154261
@@ -31512,15 +32196,15 @@
expr_cond_true
-154262
+154261
cond
-154262
+154261
true
-154262
+154261
@@ -31534,7 +32218,7 @@
1
2
-154262
+154261
@@ -31550,7 +32234,7 @@
1
2
-154262
+154261
@@ -31560,15 +32244,15 @@
expr_cond_false
-154262
+154261
cond
-154262
+154261
false
-154262
+154261
@@ -31582,7 +32266,7 @@
1
2
-154262
+154261
@@ -31598,7 +32282,7 @@
1
2
-154262
+154261
@@ -31787,11 +32471,11 @@
fieldoffsets
-251150
+251147
id
-251150
+251147
byteoffset
@@ -31813,7 +32497,7 @@
1
2
-251150
+251147
@@ -31829,7 +32513,7 @@
1
2
-251150
+251147
@@ -32214,23 +32898,23 @@
initialisers
-1664822
+1664806
init
-1664822
+1664806
var
-642778
+642772
expr
-1664822
+1664806
location
-318685
+318682
@@ -32244,7 +32928,7 @@
1
2
-1664822
+1664806
@@ -32260,7 +32944,7 @@
1
2
-1664822
+1664806
@@ -32276,7 +32960,7 @@
1
2
-1664822
+1664806
@@ -32292,7 +32976,7 @@
1
2
-556218
+556212
2
@@ -32302,12 +32986,12 @@
16
17
-49051
+49050
17
53
-8803
+8802
@@ -32323,7 +33007,7 @@
1
2
-556218
+556212
2
@@ -32333,12 +33017,12 @@
16
17
-49051
+49050
17
53
-8803
+8802
@@ -32354,7 +33038,7 @@
1
2
-642765
+642758
2
@@ -32375,7 +33059,7 @@
1
2
-1664822
+1664806
@@ -32391,7 +33075,7 @@
1
2
-1664822
+1664806
@@ -32407,7 +33091,7 @@
1
2
-1664822
+1664806
@@ -32423,7 +33107,7 @@
1
2
-246094
+246092
2
@@ -32433,7 +33117,7 @@
3
7
-24249
+24248
7
@@ -32459,12 +33143,12 @@
1
2
-268678
+268675
2
3
-24971
+24970
3
@@ -32490,7 +33174,7 @@
1
2
-246094
+246092
2
@@ -32500,7 +33184,7 @@
3
7
-24249
+24248
7
@@ -32520,15 +33204,15 @@
expr_ancestor
-66085
+66077
exp
-65384
+65375
ancestor
-47105
+47093
@@ -32542,12 +33226,12 @@
1
2
-64748
+64739
2
4
-635
+636
@@ -32563,12 +33247,12 @@
1
2
-34945
+34930
2
3
-9704
+9706
3
@@ -32583,11 +33267,11 @@
exprs
-18434237
+18434101
id
-18434237
+18434101
kind
@@ -32595,7 +33279,7 @@
location
-3622478
+3623249
@@ -32609,7 +33293,7 @@
1
2
-18434237
+18434101
@@ -32625,7 +33309,7 @@
1
2
-18434237
+18434101
@@ -32660,7 +33344,7 @@
306
-472
+471
87
@@ -32684,23 +33368,23 @@
87
-4718
+4717
6425
87
-6723
-13441
+6722
+13439
87
17876
-114359
+114329
87
-192896
-428379
+192871
+428312
43
@@ -32775,7 +33459,7 @@
87
-72726
+72772
118610
21
@@ -32793,37 +33477,37 @@
1
2
-1679637
+1680483
2
3
-738677
+738624
3
4
-319727
+319926
4
5
-277074
+276890
5
9
-301811
+301720
9
53
-272074
+272120
53
-144742
-33476
+144478
+33483
@@ -32839,17 +33523,17 @@
1
2
-2586627
+2586676
2
3
-806737
+807411
3
30
-229113
+229161
@@ -32859,15 +33543,15 @@
expr_types
-18573393
+18573385
id
-18430421
+18430284
typeid
-1322332
+1322456
value_category
@@ -32885,12 +33569,12 @@
1
2
-18288907
+18288641
2
4
-141514
+141642
@@ -32906,7 +33590,7 @@
1
2
-18430421
+18430284
@@ -32922,42 +33606,42 @@
1
2
-513731
+513839
2
3
-252041
+251951
3
4
-108355
+108499
4
5
-86009
+86038
5
8
-114200
+114147
8
14
-106042
+106075
14
45
-99759
+99747
45
-126323
-42193
+126297
+42158
@@ -32973,17 +33657,17 @@
1
2
-1170807
+1170932
2
3
-143125
+143123
3
4
-8399
+8400
@@ -33002,13 +33686,13 @@
10
-370541
-370542
+370443
+370444
10
-1304856
-1304857
+1304589
+1304590
10
@@ -33028,13 +33712,13 @@
10
-30957
-30958
+30955
+30956
10
-102771
-102772
+102756
+102757
10
@@ -33045,15 +33729,15 @@
new_allocated_type
-32127
+32134
expr
-32127
+32134
type_id
-16513
+16516
@@ -33067,7 +33751,7 @@
1
2
-32127
+32134
@@ -33083,7 +33767,7 @@
1
2
-10339
+10342
2
@@ -33708,15 +34392,15 @@
condition_decl_bind
-7028
+7008
expr
-7028
+7008
decl
-7028
+7008
@@ -33730,7 +34414,7 @@
1
2
-7028
+7008
@@ -33746,7 +34430,7 @@
1
2
-7028
+7008
@@ -33756,11 +34440,11 @@
typeid_bind
-4418
+4419
expr
-4418
+4419
type_id
@@ -33778,7 +34462,7 @@
1
2
-4418
+4419
@@ -33877,11 +34561,11 @@
sizeof_bind
-156888
+156887
expr
-156888
+156887
type_id
@@ -33899,7 +34583,7 @@
1
2
-156888
+156887
@@ -34144,11 +34828,11 @@
lambda_capture
-21653
+21652
id
-21653
+21652
lambda
@@ -34160,7 +34844,7 @@
field
-21653
+21652
captured_by_reference
@@ -34186,7 +34870,7 @@
1
2
-21653
+21652
@@ -34202,7 +34886,7 @@
1
2
-21653
+21652
@@ -34218,7 +34902,7 @@
1
2
-21653
+21652
@@ -34234,7 +34918,7 @@
1
2
-21653
+21652
@@ -34250,7 +34934,7 @@
1
2
-21653
+21652
@@ -34266,7 +34950,7 @@
1
2
-21653
+21652
@@ -34894,7 +35578,7 @@
1
2
-21653
+21652
@@ -34910,7 +35594,7 @@
1
2
-21653
+21652
@@ -34926,7 +35610,7 @@
1
2
-21653
+21652
@@ -34942,7 +35626,7 @@
1
2
-21653
+21652
@@ -34958,7 +35642,7 @@
1
2
-21653
+21652
@@ -34974,7 +35658,7 @@
1
2
-21653
+21652
@@ -35473,11 +36157,11 @@
stmts
-4688614
+4688994
id
-4688614
+4688994
kind
@@ -35485,7 +36169,7 @@
location
-1193856
+1194106
@@ -35499,7 +36183,7 @@
1
2
-4688614
+4688994
@@ -35515,7 +36199,7 @@
1
2
-4688614
+4688994
@@ -35549,8 +36233,8 @@
10
-736
-737
+735
+736
10
@@ -35564,13 +36248,13 @@
10
-2237
-2238
+2235
+2236
10
-2267
-2268
+2266
+2267
10
@@ -35579,13 +36263,13 @@
10
-2827
-2828
+2826
+2827
10
-3121
-3122
+3119
+3120
10
@@ -35594,33 +36278,33 @@
10
-4775
-4776
+4772
+4773
10
-30484
-30485
+30477
+30478
10
-55911
-55912
+55902
+55903
10
-90778
-90779
+90766
+90767
10
-103056
-103057
+103053
+103054
10
-120839
-120840
+120825
+120826
10
@@ -35743,22 +36427,22 @@
1
2
-677438
+678008
2
3
-181952
+181585
3
4
-107851
+107907
4
6
-102116
+102105
6
@@ -35768,7 +36452,7 @@
22
5041
-22960
+22965
@@ -35784,12 +36468,12 @@
1
2
-1170270
+1170515
2
9
-23585
+23590
@@ -35895,15 +36579,15 @@
if_then
-523914
+523910
if_stmt
-523914
+523910
then_id
-523914
+523910
@@ -35917,7 +36601,7 @@
1
2
-523914
+523910
@@ -35933,7 +36617,7 @@
1
2
-523914
+523910
@@ -35943,15 +36627,15 @@
if_else
-148100
+148099
if_stmt
-148100
+148099
else_id
-148100
+148099
@@ -35965,7 +36649,7 @@
1
2
-148100
+148099
@@ -35981,7 +36665,7 @@
1
2
-148100
+148099
@@ -36087,15 +36771,15 @@
while_body
-30997
+30993
while_stmt
-30997
+30993
body_id
-30997
+30993
@@ -36109,7 +36793,7 @@
1
2
-30997
+30993
@@ -36125,7 +36809,7 @@
1
2
-30997
+30993
@@ -36135,15 +36819,15 @@
do_body
-149714
+149713
do_stmt
-149714
+149713
body_id
-149714
+149713
@@ -36157,7 +36841,7 @@
1
2
-149714
+149713
@@ -36173,7 +36857,7 @@
1
2
-149714
+149713
@@ -36183,7 +36867,7 @@
switch_case
-271391
+271389
switch_stmt
@@ -36195,7 +36879,7 @@
case_id
-271391
+271389
@@ -36214,7 +36898,7 @@
5
6
-47131
+47130
6
@@ -36240,7 +36924,7 @@
5
6
-47131
+47130
6
@@ -36353,7 +37037,7 @@
1
2
-271391
+271389
@@ -36369,7 +37053,7 @@
1
2
-271391
+271389
@@ -36523,15 +37207,15 @@
for_update
-29454
+29453
for_stmt
-29454
+29453
update_id
-29454
+29453
@@ -36545,7 +37229,7 @@
1
2
-29454
+29453
@@ -36561,7 +37245,7 @@
1
2
-29454
+29453
@@ -36571,15 +37255,15 @@
for_body
-32154
+32153
for_stmt
-32154
+32153
body_id
-32154
+32153
@@ -36593,7 +37277,7 @@
1
2
-32154
+32153
@@ -36609,7 +37293,7 @@
1
2
-32154
+32153
@@ -36619,11 +37303,11 @@
stmtparents
-4119908
+4119868
id
-4119908
+4119868
index
@@ -36631,7 +37315,7 @@
parent
-1741441
+1741423
@@ -36645,7 +37329,7 @@
1
2
-4119908
+4119868
@@ -36661,7 +37345,7 @@
1
2
-4119908
+4119868
@@ -36799,27 +37483,27 @@
1
2
-991907
+991897
2
3
-382688
+382684
3
4
-108382
+108381
4
6
-114023
+114022
6
17
-131785
+131784
17
@@ -36840,27 +37524,27 @@
1
2
-991907
+991897
2
3
-382688
+382684
3
4
-108382
+108381
4
6
-114023
+114022
6
17
-131785
+131784
17
@@ -36885,181 +37569,12 @@
-successors
-16889416
-
-
-from
-16062711
-
-
-to
-16041364
-
-
-
-
-from
-to
-
-
-12
-
-
-1
-2
-15355208
-
-
-2
-647
-707503
-
-
-
-
-
-
-to
-from
-
-
-12
-
-
-1
-2
-15497963
-
-
-2
-647
-543400
-
-
-
-
-
-
-
-
-truecond
-965686
-
-
-from
-965686
-
-
-to
-936031
-
-
-
-
-from
-to
-
-
-12
-
-
-1
-2
-965686
-
-
-
-
-
-
-to
-from
-
-
-12
-
-
-1
-2
-912488
-
-
-2
-21
-23542
-
-
-
-
-
-
-
-
-falsecond
-965686
-
-
-from
-965686
-
-
-to
-811117
-
-
-
-
-from
-to
-
-
-12
-
-
-1
-2
-965686
-
-
-
-
-
-
-to
-from
-
-
-12
-
-
-1
-2
-697840
-
-
-2
-3
-86960
-
-
-3
-25
-26316
-
-
-
-
-
-
-
-
stmt_decl_bind
-532229
+532224
stmt
-525358
+525353
num
@@ -37067,7 +37582,7 @@
decl
-532229
+532224
@@ -37081,7 +37596,7 @@
1
2
-519919
+519914
2
@@ -37102,7 +37617,7 @@
1
2
-519900
+519894
2
@@ -37225,7 +37740,7 @@
1
2
-532229
+532224
@@ -37241,7 +37756,7 @@
1
2
-532229
+532224
@@ -37251,11 +37766,11 @@
stmt_decl_entry_bind
-497400
+497397
stmt
-453689
+453686
num
@@ -37263,7 +37778,7 @@
decl_entry
-473433
+473430
@@ -37277,7 +37792,7 @@
1
2
-422159
+422156
2
@@ -37298,7 +37813,7 @@
1
2
-422159
+422156
2
@@ -37386,7 +37901,7 @@
1
2
-461768
+461765
2
@@ -37407,7 +37922,7 @@
1
2
-473354
+473350
2
@@ -37422,15 +37937,15 @@
blockscope
-1324975
+1325099
block
-1324975
+1325099
enclosing
-1186772
+1186944
@@ -37444,7 +37959,7 @@
1
2
-1324975
+1325099
@@ -37460,12 +37975,12 @@
1
2
-1106388
+1106598
2
509
-80384
+80346
@@ -37475,11 +37990,11 @@
jumpinfo
-350611
+350608
id
-350611
+350608
str
@@ -37501,7 +38016,7 @@
1
2
-350611
+350608
@@ -37517,7 +38032,7 @@
1
2
-350611
+350608
@@ -37661,11 +38176,11 @@
preprocdirects
-1323352
+1323630
id
-1323352
+1323630
kind
@@ -37673,7 +38188,7 @@
location
-1317036
+1317312
@@ -37687,7 +38202,7 @@
1
2
-1323352
+1323630
@@ -37703,7 +38218,7 @@
1
2
-1323352
+1323630
@@ -37871,12 +38386,12 @@
1
2
-1316707
+1316983
2
235
-328
+329
@@ -37892,7 +38407,7 @@
1
2
-1317036
+1317312
@@ -37902,15 +38417,15 @@
preprocpair
-378730
+378809
begin
-300451
+300514
elseelifend
-378730
+378809
@@ -37924,17 +38439,17 @@
1
2
-238576
+238626
2
3
-54539
+54551
3
53
-7335
+7337
@@ -37950,7 +38465,7 @@
1
2
-378730
+378809
@@ -37960,41 +38475,41 @@
preproctrue
-166536
+166571
branch
-166536
+166571
preprocfalse
-119101
+119126
branch
-119101
+119126
preproctext
-965236
+965438
id
-965236
+965438
head
-463478
+463575
body
-175549
+175586
@@ -38008,7 +38523,7 @@
1
2
-965236
+965438
@@ -38024,7 +38539,7 @@
1
2
-965236
+965438
@@ -38040,22 +38555,22 @@
1
2
-345901
+345973
2
3
-78158
+78174
3
19
-34769
+34777
19
752
-4649
+4650
@@ -38071,12 +38586,12 @@
1
2
-441800
+441893
2
38
-21677
+21682
@@ -38092,12 +38607,12 @@
1
2
-164979
+165013
2
64816
-10570
+10572
@@ -38113,12 +38628,12 @@
1
2
-166459
+166494
2
21810
-9089
+9091
@@ -38128,15 +38643,15 @@
includes
-290791
+290852
id
-290791
+290852
included
-54594
+54606
@@ -38150,7 +38665,7 @@
1
2
-290791
+290852
@@ -38166,37 +38681,37 @@
1
2
-26886
+26891
2
3
-8958
+8960
3
4
-4616
+4617
4
6
-4846
+4847
6
11
-4188
+4189
11
41
-4111
+4112
41
763
-986
+987
@@ -38254,11 +38769,11 @@
link_parent
-18149936
+18153711
element
-4991686
+4992722
link_target
@@ -38276,32 +38791,32 @@
1
2
-1493156
+1493469
2
3
-1882851
+1883246
3
4
-718710
+718850
4
6
-400791
+400876
6
29
-398072
+398156
29
45
-98103
+98124
@@ -38351,7 +38866,7 @@
27154
-32621
+32620
43
@@ -38366,7 +38881,7 @@
46593
-56499
+56498
43
@@ -38375,8 +38890,8 @@
43
-370737
-370738
+370736
+370737
10
diff --git a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
index bbd9090c8e61..9e30de327f83 100644
--- a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
+++ b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
@@ -2,6 +2,11 @@
* Provides a library for writing QL tests whose success or failure is based on expected results
* embedded in the test source code as comments, rather than a `.expected` file.
*
+ * To add this framework to a new language:
+ * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `LineComment` class. This class
+ * must support a `getContents` method that returns the contents of the given comment, _excluding_
+ * the comment indicator itself. It should also define `toString` and `getLocation` as usual.
+ *
* To create a new inline expectations test:
* - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the
* new class, bind `this` to a unique string (usually the name of the test).
@@ -13,7 +18,7 @@
* `hasActualResult()`. Often this is just a single tag.
*
* Example:
- * ```
+ * ```ql
* class ConstantValueTest extends InlineExpectationsTest {
* ConstantValueTest() { this = "ConstantValueTest" }
*
@@ -23,11 +28,11 @@
* }
*
* override predicate hasActualResult(
- * Location location, string element, string tag, string valuesasas
+ * Location location, string element, string tag, string value
* ) {
* exists(Expr e |
* tag = "const" and // The tag for this test.
- * valuesasas = e.getValue() and // The expected value. Will only hold for constant expressions.
+ * value = e.getValue() and // The expected value. Will only hold for constant expressions.
* location = e.getLocation() and // The location of the result to be reported.
* element = e.toString() // The display text for the result.
* )
@@ -38,10 +43,10 @@
* There is no need to write a `select` clause or query predicate. All of the differences between
* expected results and actual results will be reported in the `failures()` query predicate.
*
- * To annotate the test source code with an expected result, place a C++-style (`//`) comment on the
+ * To annotate the test source code with an expected result, place a comment on the
* same line as the expected result, with text of the following format as the body of the comment:
*
- * `// $tag=expected-value`
+ * `$tag=expected-value`
*
* Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is
* the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be
@@ -53,7 +58,7 @@
* "Missing result: tag=expected-value".
*
* Example:
- * ```
+ * ```cpp
* int i = x + 5; // $const=5
* int j = y + (7 - 3) // $const=7 $const=3 $const=4 // The result of the subtraction is a constant.
* ```
@@ -62,8 +67,8 @@
* annotate that a particular expected result is known to be a false positive, or that a particular
* missing result is known to be a false negative:
*
- * `// $f+:tag=expected-value` // False positive
- * `// $f-:tag=expected-value` // False negative
+ * `$f+:tag=expected-value` // False positive
+ * `$f-:tag=expected-value` // False negative
*
* A false positive expectation is treated as any other expected result, except that if there is no
* matching actual result, the message will be of the form "Fixed false positive: tag=value". A
@@ -74,14 +79,14 @@
* If the same result value is expected for two or more tags on the same line, there is a shorthand
* notation available:
*
- * `// $tag1,tag2=expected-value`
+ * `$tag1,tag2=expected-value`
*
* is equivalent to:
*
- * `// $tag1=expected-value $tag2=expected-value`
+ * `$tag1=expected-value $tag2=expected-value`
*/
-import cpp
+private import InlineExpectationsTestPrivate
/**
* Base class for tests with inline expectations. The test extends this class to provide the actual
@@ -150,12 +155,12 @@ abstract class InlineExpectationsTest extends string {
}
/**
- * RegEx pattern to match a comment containing one or more expected results. The comment must be a
- * C++-style (`//`) comment with `$` as its first non-whitespace character. Any subsequent character
+ * RegEx pattern to match a comment containing one or more expected results. The comment must have
+ * `$` as its first non-whitespace character. Any subsequent character
* is treated as part of the expected results, except that the comment may contain a `//` sequence
* to treat the remainder of the line as a regular (non-interpreted) comment.
*/
-private string expectationCommentPattern() { result = "//\\s*(\\$(?:[^/]|/[^/])*)(?://.*)?" }
+private string expectationCommentPattern() { result = "\\s*(\\$(?:[^/]|/[^/])*)(?://.*)?" }
/**
* RegEx pattern to match a single expected result, not including the leading `$`. It starts with an
@@ -166,7 +171,7 @@ private string expectationPattern() {
result = "(?:(f(?:\\+|-)):)?((?:[A-Za-z-_]+)(?:\\s*,\\s*[A-Za-z-_]+)*)(?:=(.*))?"
}
-private string getAnExpectation(CppStyleComment comment) {
+private string getAnExpectation(LineComment comment) {
result = comment.getContents().regexpCapture(expectationCommentPattern(), 1).splitAt("$").trim() and
result != ""
}
@@ -177,7 +182,7 @@ private newtype TFailureLocatable =
) {
test.hasActualResult(location, element, tag, value)
} or
- TValidExpectation(CppStyleComment comment, string tag, string value, string knownFailure) {
+ TValidExpectation(LineComment comment, string tag, string value, string knownFailure) {
exists(string expectation |
expectation = getAnExpectation(comment) and
expectation.regexpMatch(expectationPattern()) and
@@ -194,7 +199,7 @@ private newtype TFailureLocatable =
)
)
} or
- TInvalidExpectation(CppStyleComment comment, string expectation) {
+ TInvalidExpectation(LineComment comment, string expectation) {
expectation = getAnExpectation(comment) and
not expectation.regexpMatch(expectationPattern())
}
@@ -232,7 +237,7 @@ class ActualResult extends FailureLocatable, TActualResult {
}
abstract private class Expectation extends FailureLocatable {
- CppStyleComment comment;
+ LineComment comment;
override string toString() { result = comment.toString() }
diff --git a/cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll
new file mode 100644
index 000000000000..f3aae029eb4a
--- /dev/null
+++ b/cpp/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll
@@ -0,0 +1,23 @@
+import cpp
+
+private newtype TLineComment = MkLineComment(CppStyleComment c)
+
+/**
+ * Represents a line comment in the CPP style.
+ * Unlike the `CppStyleComment` class, however, the string returned by `getContents` does _not_
+ * include the preceding comment marker (`//`).
+ */
+class LineComment extends TLineComment {
+ CppStyleComment comment;
+
+ LineComment() { this = MkLineComment(comment) }
+
+ /** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */
+ string getContents() { result = comment.getContents().suffix(2) }
+
+ /** Gets a textual representation of this element. */
+ string toString() { result = comment.toString() }
+
+ /** Gets the location of this comment. */
+ Location getLocation() { result = comment.getLocation() }
+}
diff --git a/cpp/ql/test/examples/expressions/PrintAST.expected b/cpp/ql/test/examples/expressions/PrintAST.expected
index 585ebae6ff4f..61c20b14921c 100644
--- a/cpp/ql/test/examples/expressions/PrintAST.expected
+++ b/cpp/ql/test/examples/expressions/PrintAST.expected
@@ -19,7 +19,7 @@ AddressOf.c:
# 1| params:
# 1| 0: [Parameter] i
# 1| Type = [IntType] int
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [DeclStmt] declaration
# 2| 0: [VariableDeclarationEntry] definition of j
# 2| Type = [IntPointerType] int *
@@ -34,7 +34,7 @@ AddressOf.c:
ArrayToPointer.c:
# 5| [TopLevelFunction] void ArrayToPointer()
# 5| params:
-# 6| body: [Block] { ... }
+# 6| body: [BlockStmt] { ... }
# 7| 0: [DeclStmt] declaration
# 7| 0: [VariableDeclarationEntry] definition of c
# 7| Type = [ArrayType] char[]
@@ -70,7 +70,7 @@ Cast.c:
# 1| Type = [CharPointerType] char *
# 1| 1: [Parameter] v
# 1| Type = [VoidPointerType] void *
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [CharPointerType] char *
@@ -89,7 +89,7 @@ Cast.c:
ConditionDecl.cpp:
# 1| [TopLevelFunction] void ConditionDecl()
# 1| params:
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [DeclStmt] declaration
# 2| 0: [VariableDeclarationEntry] definition of j
# 2| Type = [IntType] int
@@ -109,7 +109,7 @@ ConditionDecl.cpp:
# 3| expr: [VariableAccess] k
# 3| Type = [IntType] int
# 3| ValueCategory = prvalue(load)
-# 3| 1: [Block] { ... }
+# 3| 1: [BlockStmt] { ... }
# 5| 2: [ReturnStmt] return ...
ConstructorCall.cpp:
# 1| [CopyAssignmentOperator] C& C::operator=(C const&)
@@ -133,7 +133,7 @@ ConstructorCall.cpp:
# 3| 0: [Parameter] i
# 3| Type = [IntType] int
# 3| initializations:
-# 3| body: [Block] { ... }
+# 3| body: [BlockStmt] { ... }
# 4| 0: [ReturnStmt] return ...
# 7| [CopyAssignmentOperator] D& D::operator=(D const&)
# 7| params:
@@ -154,7 +154,7 @@ ConstructorCall.cpp:
# 9| [Constructor] void D::D()
# 9| params:
# 9| initializations:
-# 9| body: [Block] { ... }
+# 9| body: [BlockStmt] { ... }
# 10| 0: [ReturnStmt] return ...
# 13| [CopyAssignmentOperator] E& E::operator=(E const&)
# 13| params:
@@ -172,7 +172,7 @@ ConstructorCall.cpp:
# 17| Type = [PointerType] D *
# 17| 2: [Parameter] e
# 17| Type = [PointerType] E *
-# 17| body: [Block] { ... }
+# 17| body: [BlockStmt] { ... }
# 18| 0: [ExprStmt] ExprStmt
# 18| 0: [AssignExpr] ... = ...
# 18| Type = [PointerType] C *
@@ -221,7 +221,7 @@ ConstructorCall.cpp:
Conversion1.c:
# 1| [TopLevelFunction] void Conversion1()
# 1| params:
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [DeclStmt] declaration
# 2| 0: [VariableDeclarationEntry] definition of i
# 2| Type = [IntType] int
@@ -241,7 +241,7 @@ Conversion2.c:
# 1| params:
# 1| 0: [Parameter] x
# 1| Type = [IntType] int
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -277,7 +277,7 @@ Conversion3.cpp:
# 1| params:
# 1| 0: [Parameter] x
# 1| Type = [IntType] int
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -327,7 +327,7 @@ Conversion4.c:
# 1| params:
# 1| 0: [Parameter] x
# 1| Type = [IntType] int
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -350,9 +350,11 @@ Conversion4.c:
# 2| ValueCategory = prvalue
# 3| 1: [ReturnStmt] return ...
DestructorCall.cpp:
+# 1| [Constructor] void C::C()
+# 1| params:
# 3| [Destructor] void C::~C()
# 3| params:
-# 3| body: [Block] { ... }
+# 3| body: [BlockStmt] { ... }
# 4| 0: [ReturnStmt] return ...
# 3| destructions:
# 11| [TopLevelFunction] void DestructorCall(C*, D*)
@@ -361,7 +363,7 @@ DestructorCall.cpp:
# 11| Type = [PointerType] C *
# 11| 1: [Parameter] d
# 11| Type = [PointerType] D *
-# 11| body: [Block] { ... }
+# 11| body: [BlockStmt] { ... }
# 12| 0: [ExprStmt] ExprStmt
# 12| 0: [DeleteExpr] delete
# 12| Type = [VoidType] void
@@ -385,7 +387,7 @@ DynamicCast.cpp:
# 1| params:
#-----| 0: [Parameter] p#0
#-----| Type = [LValueReferenceType] const Base &
-#-----| body: [Block] { ... }
+#-----| body: [BlockStmt] { ... }
#-----| 0: [ReturnStmt] return ...
#-----| 0: [ReferenceToExpr] (reference to)
#-----| Type = [LValueReferenceType] Base &
@@ -412,13 +414,13 @@ DynamicCast.cpp:
#-----| Type = [RValueReferenceType] Base &&
# 2| [VirtualFunction] void Base::f()
# 2| params:
-# 2| body: [Block] { ... }
+# 2| body: [BlockStmt] { ... }
# 2| 0: [ReturnStmt] return ...
# 4| [CopyAssignmentOperator] Derived& Derived::operator=(Derived const&)
# 4| params:
#-----| 0: [Parameter] p#0
#-----| Type = [LValueReferenceType] const Derived &
-#-----| body: [Block] { ... }
+#-----| body: [BlockStmt] { ... }
#-----| 0: [ExprStmt] ExprStmt
#-----| 0: [ReferenceDereferenceExpr] (reference dereference)
#-----| Type = [Class] Base
@@ -478,7 +480,7 @@ DynamicCast.cpp:
#-----| Type = [RValueReferenceType] Derived &&
# 5| [VirtualFunction] void Derived::f()
# 5| params:
-# 5| body: [Block] { ... }
+# 5| body: [BlockStmt] { ... }
# 5| 0: [ReturnStmt] return ...
# 8| [TopLevelFunction] void DynamicCast(Base*, Derived*)
# 8| params:
@@ -486,7 +488,7 @@ DynamicCast.cpp:
# 8| Type = [PointerType] Base *
# 8| 1: [Parameter] d
# 8| Type = [PointerType] Derived *
-# 8| body: [Block] { ... }
+# 8| body: [BlockStmt] { ... }
# 9| 0: [ExprStmt] ExprStmt
# 9| 0: [AssignExpr] ... = ...
# 9| Type = [PointerType] Derived *
@@ -508,7 +510,7 @@ DynamicCast.cpp:
# 12| Type = [LValueReferenceType] Base &
# 12| 1: [Parameter] d
# 12| Type = [LValueReferenceType] Derived &
-# 12| body: [Block] { ... }
+# 12| body: [BlockStmt] { ... }
# 13| 0: [ExprStmt] ExprStmt
# 13| 0: [ReferenceDereferenceExpr] (reference dereference)
# 13| Type = [Class] Derived
@@ -545,7 +547,7 @@ Parenthesis.c:
# 1| params:
# 1| 0: [Parameter] i
# 1| Type = [IntType] int
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -581,7 +583,7 @@ PointerDereference.c:
# 1| Type = [IntPointerType] int *
# 1| 1: [Parameter] j
# 1| Type = [IntType] int
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -603,7 +605,7 @@ ReferenceDereference.cpp:
# 4| Type = [LValueReferenceType] int &
# 4| 1: [Parameter] j
# 4| Type = [IntType] int
-# 4| body: [Block] { ... }
+# 4| body: [BlockStmt] { ... }
# 5| 0: [ExprStmt] ExprStmt
# 5| 0: [AssignExpr] ... = ...
# 5| Type = [IntType] int
@@ -623,7 +625,7 @@ ReferenceTo.cpp:
# 1| params:
# 1| 0: [Parameter] i
# 1| Type = [IntPointerType] int *
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [ReturnStmt] return ...
# 2| 0: [ReferenceToExpr] (reference to)
# 2| Type = [LValueReferenceType] int &
@@ -639,7 +641,7 @@ Sizeof.c:
# 1| params:
# 1| 0: [Parameter] array
# 1| Type = [ArrayType] int[]
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [DeclStmt] declaration
# 2| 0: [VariableDeclarationEntry] definition of i
# 2| Type = [IntType] int
@@ -676,7 +678,7 @@ Sizeof.c:
StatementExpr.c:
# 1| [TopLevelFunction] void StatementExpr()
# 1| params:
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [DeclStmt] declaration
# 2| 0: [VariableDeclarationEntry] definition of j
# 2| Type = [IntType] int
@@ -700,7 +702,7 @@ StaticMemberAccess.cpp:
# 5| Type = [IntType] int
# 5| 1: [Parameter] xref
# 5| Type = [LValueReferenceType] X &
-# 5| body: [Block] { ... }
+# 5| body: [BlockStmt] { ... }
# 7| 0: [ExprStmt] ExprStmt
# 7| 0: [AssignExpr] ... = ...
# 7| Type = [IntType] int
@@ -725,7 +727,7 @@ Subscript.c:
# 1| Type = [ArrayType] int[]
# 1| 1: [Parameter] j
# 1| Type = [IntType] int
-# 1| body: [Block] { ... }
+# 1| body: [BlockStmt] { ... }
# 2| 0: [ExprStmt] ExprStmt
# 2| 0: [AssignExpr] ... = ...
# 2| Type = [IntType] int
@@ -762,20 +764,20 @@ Throw.cpp:
#-----| 0: [Parameter] p#0
#-----| Type = [RValueReferenceType] F &&
# 2| initializations:
-# 2| body: [Block] { ... }
+# 2| body: [BlockStmt] { ... }
# 2| 0: [ReturnStmt] return ...
# 4| [Constructor] void F::F()
# 4| params:
# 4| initializations:
-# 4| body: [Block] { ... }
+# 4| body: [BlockStmt] { ... }
# 4| 0: [ReturnStmt] return ...
# 6| [TopLevelFunction] void Throw(int)
# 6| params:
# 6| 0: [Parameter] i
# 6| Type = [IntType] int
-# 6| body: [Block] { ... }
+# 6| body: [BlockStmt] { ... }
# 7| 0: [TryStmt] try { ... }
-# 7| 0: [Block] { ... }
+# 7| 0: [BlockStmt] { ... }
# 8| 0: [IfStmt] if (...) ...
# 8| 0: [CStyleCast] (bool)...
# 8| Conversion = [BoolConversion] conversion to bool
@@ -818,13 +820,13 @@ Typeid.cpp:
# 7| params:
# 13| [VirtualFunction] void Base::v()
# 13| params:
-# 13| body: [Block] { ... }
+# 13| body: [BlockStmt] { ... }
# 13| 0: [ReturnStmt] return ...
# 18| [TopLevelFunction] void TypeId(Base*)
# 18| params:
# 18| 0: [Parameter] bp
# 18| Type = [PointerType] Base *
-# 18| body: [Block] { ... }
+# 18| body: [BlockStmt] { ... }
# 19| 0: [DeclStmt] declaration
# 19| 0: [VariableDeclarationEntry] definition of name
# 19| Type = [PointerType] const char *
@@ -846,7 +848,7 @@ VacuousDestructorCall.cpp:
# 2| Type = [TemplateParameter] T
# 2| 1: [Parameter] y
# 2| Type = [PointerType] T *
-# 2| body: [Block] { ... }
+# 2| body: [BlockStmt] { ... }
# 3| 0: [ExprStmt] ExprStmt
# 3| 0: [ExprCall] call to expression
# 3| Type = [UnknownType] unknown
@@ -874,7 +876,7 @@ VacuousDestructorCall.cpp:
# 2| Type = [IntType] int
# 2| 1: [Parameter] y
# 2| Type = [IntPointerType] int *
-# 2| body: [Block] { ... }
+# 2| body: [BlockStmt] { ... }
# 3| 0: [ExprStmt] ExprStmt
# 3| 0: [VacuousDestructorCall] (vacuous destructor call)
# 3| Type = [VoidType] void
@@ -894,7 +896,7 @@ VacuousDestructorCall.cpp:
# 7| params:
# 7| 0: [Parameter] i
# 7| Type = [IntType] int
-# 7| body: [Block] { ... }
+# 7| body: [BlockStmt] { ... }
# 10| 0: [ExprStmt] ExprStmt
# 10| 0: [FunctionCall] call to CallDestructor
# 10| Type = [VoidType] void
@@ -914,7 +916,7 @@ Varargs.c:
# 8| params:
# 8| 0: [Parameter] text
# 8| Type = [PointerType] const char *
-# 8| body: [Block] { ... }
+# 8| body: [BlockStmt] { ... }
# 9| 0: [DeclStmt] declaration
# 9| 0: [VariableDeclarationEntry] definition of args
# 9| Type = [CTypedefType] va_list
@@ -947,7 +949,7 @@ macro_etc.c:
# 3| params:
# 3| 0: [Parameter] i
# 3| Type = [IntType] int
-# 3| body: [Block] { ... }
+# 3| body: [BlockStmt] { ... }
# 4| 0: [DeclStmt] declaration
# 4| 0: [TypeDeclarationEntry] definition of u
# 4| Type = [LocalUnion] u
@@ -997,7 +999,7 @@ macro_etc.c:
# 10| ValueCategory = prvalue
# 22| [TopLevelFunction] int foo()
# 22| params:
-# 22| body: [Block] { ... }
+# 22| body: [BlockStmt] { ... }
# 23| 0: [DeclStmt] declaration
# 23| 0: [VariableDeclarationEntry] definition of t
# 23| Type = [IntType] int
@@ -1059,7 +1061,7 @@ macro_etc.c:
# 27| 0: [VariableAccess] i
# 27| Type = [PlainCharType] char
# 27| ValueCategory = lvalue
-# 27| 3: [Block] { ... }
+# 27| 3: [BlockStmt] { ... }
# 27| 0: [ExprStmt] ExprStmt
# 27| 0: [AssignAddExpr] ... += ...
# 27| Type = [IntType] int
@@ -1111,7 +1113,7 @@ macro_etc.c:
# 28| 0: [VariableAccess] i
# 28| Type = [PlainCharType] char
# 28| ValueCategory = lvalue
-# 28| 3: [Block] { ... }
+# 28| 3: [BlockStmt] { ... }
# 28| 0: [ExprStmt] ExprStmt
# 28| 0: [AssignAddExpr] ... += ...
# 28| Type = [IntType] int
@@ -1210,7 +1212,7 @@ union_etc.cpp:
# 2| [Constructor] void S::S()
# 2| params:
# 2| initializations:
-# 2| body: [Block] { ... }
+# 2| body: [BlockStmt] { ... }
# 2| 0: [ReturnStmt] return ...
# 2| [CopyConstructor] void S::S(S const&)
# 2| params:
@@ -1240,7 +1242,7 @@ union_etc.cpp:
# 6| params:
# 6| 0: [Parameter] val
# 6| Type = [IntType] int
-# 6| body: [Block] { ... }
+# 6| body: [BlockStmt] { ... }
# 6| 0: [ExprStmt] ExprStmt
# 6| 0: [AssignExpr] ... = ...
# 6| Type = [IntType] int
@@ -1305,7 +1307,7 @@ union_etc.cpp:
#-----| Type = [RValueReferenceType] C &&
# 22| [TopLevelFunction] int foo()
# 22| params:
-# 22| body: [Block] { ... }
+# 22| body: [BlockStmt] { ... }
# 23| 0: [DeclStmt] declaration
# 23| 0: [VariableDeclarationEntry] definition of s
# 23| Type = [Struct] S
@@ -1423,7 +1425,7 @@ union_etc.cpp:
# 33| params:
# 33| 0: [Parameter] val
# 33| Type = [IntType] int
-# 33| body: [Block] { ... }
+# 33| body: [BlockStmt] { ... }
# 33| 0: [ExprStmt] ExprStmt
# 33| 0: [AssignExpr] ... = ...
# 33| Type = [IntType] int
@@ -1440,7 +1442,7 @@ union_etc.cpp:
# 33| 1: [ReturnStmt] return ...
# 36| [TopLevelFunction] int bar()
# 36| params:
-# 36| body: [Block] { ... }
+# 36| body: [BlockStmt] { ... }
# 37| 0: [DeclStmt] declaration
# 37| 0: [VariableDeclarationEntry] definition of s
# 37| Type = [PointerType] const T *
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.cpp
new file mode 100644
index 000000000000..d45b0062d5d4
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.cpp
@@ -0,0 +1,59 @@
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+typedef unsigned uint32_t;
+typedef signed long long int64_t;
+
+void test_assign_operator(uint8_t x) {
+ x &= 7; // [0 .. 7]
+}
+
+void test_non_negative_const(uint8_t x) {
+ uint8_t unsigned_const = 7;
+
+ // Non-negative range operand and non-negative constant. The operands are promoted
+ // to signed ints.
+ x & 0; // [0 .. 0]
+ x & 7; // [0 .. 7]
+ x & unsigned_const; // [0 .. 7]
+
+ // This tests what happens when both arguments are promoted to `unsigned int` instead
+ // of `int`, and when the constant is larger than the max bound
+ x & 0xFFFFFFFF; // [0 .. 255]
+}
+
+void test_non_const(uint8_t a, uint8_t b, uint32_t c, uint32_t d) {
+ if (b <= 100) {
+ // `a` and `b` are promoted to signed ints, meaning neither the range analysis library
+ // nor this extension handle it
+ a & b; // [-2147483648 .. 2147483647]
+ }
+ if (d <= 100) {
+ // Handled by the range analysis library
+ c & d; // [0 .. 100]
+ }
+}
+
+void test_negative_operand(uint8_t x, int8_t y) {
+ uint8_t unsigned_const = 7;
+ int8_t signed_const = -7;
+
+ // The right operand can be negative
+ x & -7; // [-2147483648 .. 2147483647]
+ x & signed_const; // [-2147483648 .. 2147483647]
+ x & y; // [-2147483648 .. 2147483647]
+
+ // The left operand can be negative
+ y & 7; // [-2147483648 .. 2147483647]
+ y & unsigned_const; // [-2147483648 .. 2147483647]
+ y & 0xFFFFFFFF; // [0 .. 4294967295]
+ (int64_t)y & 0xFFFFFFFF; // [-9223372036854776000 .. 9223372036854776000]
+ y & x; // [-2147483648 .. 2147483647]
+
+ // Both can be negative
+ y & -7; // [-2147483648 .. 2147483647]
+ y & signed_const; // [-2147483648 .. 2147483647]
+ signed_const & -7; // [-2147483648 .. 2147483647]
+ signed_const & y; // [-2147483648 .. 2147483647]
+ -7 & y; // [-2147483648 .. 2147483647]
+ -7 & signed_const; // [-2147483648 .. 2147483647]
+}
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.expected
new file mode 100644
index 000000000000..22ad956469e4
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.expected
@@ -0,0 +1,21 @@
+| bitwiseand.cpp:7:3:7:8 | ... &= ... | 0.0 | 7.0 |
+| bitwiseand.cpp:15:3:15:7 | ... & ... | 0.0 | 0.0 |
+| bitwiseand.cpp:16:3:16:7 | ... & ... | 0.0 | 7.0 |
+| bitwiseand.cpp:17:3:17:20 | ... & ... | 0.0 | 7.0 |
+| bitwiseand.cpp:21:3:21:16 | ... & ... | 0.0 | 255.0 |
+| bitwiseand.cpp:28:5:28:9 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:32:5:32:9 | ... & ... | 0.0 | 100.0 |
+| bitwiseand.cpp:41:3:41:8 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:42:3:42:18 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:43:3:43:7 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:46:3:46:7 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:47:3:47:20 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:48:3:48:16 | ... & ... | 0.0 | 4.294967295E9 |
+| bitwiseand.cpp:49:3:49:25 | ... & ... | -9.223372036854776E18 | 9.223372036854776E18 |
+| bitwiseand.cpp:50:3:50:7 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:53:3:53:8 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:54:3:54:18 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:55:3:55:19 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:56:3:56:18 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:57:3:57:8 | ... & ... | -2.147483648E9 | 2.147483647E9 |
+| bitwiseand.cpp:58:3:58:19 | ... & ... | -2.147483648E9 | 2.147483647E9 |
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.ql
new file mode 100644
index 000000000000..6521e8f0f610
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/bitwiseand/bitwiseand.ql
@@ -0,0 +1,8 @@
+import experimental.semmle.code.cpp.rangeanalysis.ExtendedRangeAnalysis
+
+from Operation expr, float lower, float upper
+where
+ (expr instanceof BitwiseAndExpr or expr instanceof AssignAndExpr) and
+ lower = lowerBound(expr) and
+ upper = upperBound(expr)
+select expr, lower, upper
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.cpp
new file mode 100644
index 000000000000..0fed35bc9aff
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.cpp
@@ -0,0 +1,9 @@
+
+
+void test_overridability_sub(int x) {
+ int zero = x - x;
+ zero; // 0
+
+ int nonzero = x - (unsigned char)x;
+ nonzero; // full range
+}
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.expected
new file mode 100644
index 000000000000..b43601c80886
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.expected
@@ -0,0 +1,6 @@
+| extended.cpp:4:14:4:14 | x | -2.147483648E9 | 2.147483647E9 |
+| extended.cpp:4:18:4:18 | x | -2.147483648E9 | 2.147483647E9 |
+| extended.cpp:5:3:5:6 | zero | 0.0 | 0.0 |
+| extended.cpp:7:17:7:17 | x | -2.147483648E9 | 2.147483647E9 |
+| extended.cpp:7:36:7:36 | x | -2.147483648E9 | 2.147483647E9 |
+| extended.cpp:8:3:8:9 | nonzero | -2.147483648E9 | 2.147483647E9 |
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.ql
new file mode 100644
index 000000000000..d6344e5d0629
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extended/extended.ql
@@ -0,0 +1,7 @@
+import experimental.semmle.code.cpp.rangeanalysis.ExtendedRangeAnalysis
+
+from VariableAccess expr, float lower, float upper
+where
+ lower = lowerBound(expr) and
+ upper = upperBound(expr)
+select expr, lower, upper
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c
new file mode 100644
index 000000000000..33cf54ee9ebf
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.c
@@ -0,0 +1,19 @@
+/// Adds its arguments (has custom modeling in QL)
+int custom_add_function(int a, int b);
+
+int test_extensibility_add(int x) {
+ if (x >= -10 && x <= 10) {
+ int result = custom_add_function(x, 100);
+ return result; // 90 .. 110
+ }
+}
+
+int test_overridability_sub(int x) {
+ int result = x - (unsigned char)x; // Returns 0 due to custom modeling for this test being deliberately wrong
+ return result; // 0
+}
+
+void test_parameter_override(int magic_name_at_most_10, int magic_name_at_most_20) {
+ magic_name_at_most_10;
+ magic_name_at_most_20;
+}
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected
new file mode 100644
index 000000000000..e9133e191044
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.expected
@@ -0,0 +1,9 @@
+| extensibility.c:5:7:5:7 | x | -2.147483648E9 | 2.147483647E9 |
+| extensibility.c:5:19:5:19 | x | -10.0 | 2.147483647E9 |
+| extensibility.c:6:38:6:38 | x | -10.0 | 10.0 |
+| extensibility.c:7:12:7:17 | result | 90.0 | 110.0 |
+| extensibility.c:12:16:12:16 | x | -2.147483648E9 | 2.147483647E9 |
+| extensibility.c:12:35:12:35 | x | -2.147483648E9 | 2.147483647E9 |
+| extensibility.c:13:10:13:15 | result | 0.0 | 0.0 |
+| extensibility.c:17:3:17:23 | magic_name_at_most_10 | -2.147483648E9 | 10.0 |
+| extensibility.c:18:3:18:23 | magic_name_at_most_20 | -2.147483648E9 | 20.0 |
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql
new file mode 100644
index 000000000000..4fbfed1706db
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/extensibility/extensibility.ql
@@ -0,0 +1,80 @@
+import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
+import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
+import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
+import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisDefinition
+
+class CustomAddFunctionCall extends SimpleRangeAnalysisExpr, FunctionCall {
+ CustomAddFunctionCall() { this.getTarget().hasGlobalName("custom_add_function") }
+
+ override float getLowerBounds() {
+ exists(float lower0, float lower1 |
+ lower0 = getFullyConvertedLowerBounds(this.getArgument(0)) and
+ lower1 = getFullyConvertedLowerBounds(this.getArgument(1)) and
+ // Note: this rounds toward 0, not -Inf as it should
+ result = lower0 + lower1
+ )
+ }
+
+ override float getUpperBounds() {
+ exists(float upper0, float upper1 |
+ upper0 = getFullyConvertedUpperBounds(this.getArgument(0)) and
+ upper1 = getFullyConvertedUpperBounds(this.getArgument(1)) and
+ // Note: this rounds toward 0, not Inf as it should
+ result = upper0 + upper1
+ )
+ }
+
+ override predicate dependsOnChild(Expr child) { child = this.getAnArgument() }
+}
+
+class SelfSub extends SimpleRangeAnalysisExpr, SubExpr {
+ SelfSub() {
+ getLeftOperand().(VariableAccess).getTarget() = getRightOperand().(VariableAccess).getTarget()
+ }
+
+ override float getLowerBounds() { result = 0 }
+
+ override float getUpperBounds() { result = 0 }
+
+ override predicate dependsOnChild(Expr child) { child = this.getAnOperand() }
+}
+
+/**
+ * A definition for test purposes of a parameter `p` that starts with a
+ * special prefix. This class is written to exploit how QL behaves when class
+ * fields are not functionally determined by `this`. When multiple parameters
+ * of the same function have the special prefix, there is still only one
+ * instance of this class.
+ */
+class MagicParameterName extends SimpleRangeAnalysisDefinition {
+ Parameter p;
+ float value;
+
+ MagicParameterName() {
+ this.definedByParameter(p) and
+ value = p.getName().regexpCapture("magic_name_at_most_(\\d+)", 1).toFloat()
+ }
+
+ override predicate hasRangeInformationFor(StackVariable v) { v = p }
+
+ override predicate dependsOnExpr(StackVariable v, Expr e) {
+ // No dependencies. This sample class yields constant values.
+ none()
+ }
+
+ override float getLowerBounds(StackVariable var) {
+ var = p and
+ result = typeLowerBound(p.getUnspecifiedType())
+ }
+
+ override float getUpperBounds(StackVariable var) {
+ var = p and
+ result = value
+ }
+}
+
+from VariableAccess expr, float lower, float upper
+where
+ lower = lowerBound(expr) and
+ upper = upperBound(expr)
+select expr, lower, upper
diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected
similarity index 100%
rename from cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected
rename to cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql
new file mode 100644
index 000000000000..1b77763682ad
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql
@@ -0,0 +1,19 @@
+import experimental.semmle.code.cpp.rangeanalysis.RangeAnalysis
+import semmle.code.cpp.ir.IR
+import semmle.code.cpp.controlflow.IRGuards
+import semmle.code.cpp.ir.ValueNumbering
+
+query predicate instructionBounds(
+ Instruction i, Bound b, int delta, boolean upper, Reason reason, Location reasonLoc
+) {
+ (
+ i.getAUse() instanceof ArgumentOperand
+ or
+ exists(ReturnValueInstruction retInstr | retInstr.getReturnValueOperand() = i.getAUse())
+ ) and
+ boundedInstruction(i, b, delta, upper, reason) and
+ not valueNumber(b.getInstruction()) = valueNumber(i) and
+ if reason instanceof CondReason
+ then reasonLoc = reason.(CondReason).getCond().getLocation()
+ else reasonLoc instanceof UnknownDefaultLocation
+}
diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/test.cpp
similarity index 100%
rename from cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.cpp
rename to cpp/ql/test/experimental/library-tests/rangeanalysis/rangeanalysis/test.cpp
diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected
similarity index 100%
rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected
rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.expected
diff --git a/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql
new file mode 100644
index 000000000000..fadd86f8a855
--- /dev/null
+++ b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/SignAnalysis.ql
@@ -0,0 +1,19 @@
+import experimental.semmle.code.cpp.rangeanalysis.SignAnalysis
+import semmle.code.cpp.ir.IR
+
+string getASignString(Instruction i) {
+ positiveInstruction(i) and
+ result = "positive"
+ or
+ negativeInstruction(i) and
+ result = "negative"
+ or
+ strictlyPositiveInstruction(i) and
+ result = "strictlyPositive"
+ or
+ strictlyNegativeInstruction(i) and
+ result = "strictlyNegative"
+}
+
+from Instruction i
+select i, strictconcat(string s | s = getASignString(i) | s, " ")
diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c
similarity index 100%
rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c
rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/binary_logical_operator.c
diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/bounded_bounds.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/bounded_bounds.c
similarity index 100%
rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/bounded_bounds.c
rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/bounded_bounds.c
diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/inline_assembly.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/inline_assembly.c
similarity index 100%
rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/inline_assembly.c
rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/inline_assembly.c
diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/minmax.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/minmax.c
similarity index 100%
rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/minmax.c
rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/minmax.c
diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.c b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.c
similarity index 100%
rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.c
rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.c
diff --git a/cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.cpp b/cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.cpp
similarity index 100%
rename from cpp/ql/test/library-tests/rangeanalysis/signanalysis/test.cpp
rename to cpp/ql/test/experimental/library-tests/rangeanalysis/signanalysis/test.cpp
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.expected
new file mode 100644
index 000000000000..c263d7e6dfd7
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.expected
@@ -0,0 +1,21 @@
+edges
+| test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp |
+| test.cpp:81:17:81:20 | call to func | test.cpp:82:24:82:28 | buff5 |
+| test.cpp:81:22:81:28 | medical | test.cpp:81:17:81:20 | call to func |
+nodes
+| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode |
+| test.cpp:74:24:74:30 | medical | semmle.label | medical |
+| test.cpp:77:16:77:22 | medical | semmle.label | medical |
+| test.cpp:78:24:78:27 | temp | semmle.label | temp |
+| test.cpp:81:17:81:20 | call to func | semmle.label | call to func |
+| test.cpp:81:22:81:28 | medical | semmle.label | medical |
+| test.cpp:82:24:82:28 | buff5 | semmle.label | buff5 |
+| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
+| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode |
+#select
+| test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:57:9:57:18 | theZipcode | this source. |
+| test.cpp:74:24:74:30 | medical | This write into the external location 'medical' may contain unencrypted data from $@ | test.cpp:74:24:74:30 | medical | this source. |
+| test.cpp:78:24:78:27 | temp | This write into the external location 'temp' may contain unencrypted data from $@ | test.cpp:77:16:77:22 | medical | this source. |
+| test.cpp:82:24:82:28 | buff5 | This write into the external location 'buff5' may contain unencrypted data from $@ | test.cpp:81:22:81:28 | medical | this source. |
+| test.cpp:96:37:96:46 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:96:37:96:46 | theZipcode | this source. |
+| test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:99:42:99:51 | theZipcode | this source. |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.qlref
new file mode 100644
index 000000000000..65c8c9c2dd4c
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-359/PrivateCleartextWrite.ql
\ No newline at end of file
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/test.cpp
new file mode 100644
index 000000000000..4d69ee5b2b72
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/test.cpp
@@ -0,0 +1,105 @@
+#define FILE int
+#define wchar char
+#define size_t int
+typedef int streamsize;
+
+size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
+int fputs(const char *s, FILE *stream);
+int fputc(int c, FILE *stream);
+int fprintf(FILE *stream, const char *format, ...);
+int sprintf(char *s, const char *format, ...);
+size_t strlen(const char *s);
+
+namespace std
+{
+ template
+ struct char_traits;
+
+ template >
+ class basic_ostream /*: virtual public basic_ios - not needed for this test */
+ {
+ public:
+ typedef charT char_type;
+ basic_ostream &write(const char_type *s, streamsize n);
+ };
+
+ template >
+ class basic_ofstream : public basic_ostream
+ {
+ public:
+ };
+
+ template
+ basic_ostream &operator<<(basic_ostream &, const charT *);
+
+ typedef basic_ostream ostream;
+ typedef basic_ofstream ofstream;
+}; // namespace std
+
+using namespace std;
+
+char *encrypt(char *buffer)
+{
+ return buffer;
+}
+char *func(char *buffer)
+{
+ return buffer;
+}
+
+// test for CleartextFileWrite
+void file()
+{
+ char *theZipcode = "cleartext zipcode!";
+ FILE *file;
+
+ // BAD: write zipcode to file in cleartext
+ fputs(theZipcode, file);
+
+ // GOOD: encrypt first
+ char *encrypted = encrypt(theZipcode);
+ fwrite(encrypted, sizeof(encrypted), 1, file);
+}
+
+// test for CleartextBufferWrite
+int main(int argc, char **argv)
+{
+ char *medical = "medical";
+ char *buff1;
+ char *buff2;
+ char *buff3;
+ char *buff4;
+
+ // BAD: write medical to buffer in cleartext
+ sprintf(buff1, "%s", medical);
+
+ // BAD: write medical to buffer in cleartext
+ char *temp = medical;
+ sprintf(buff2, "%s", temp);
+
+ // BAD: write medical to buffer in cleartext
+ char *buff5 = func(medical);
+ sprintf(buff3, "%s", buff5);
+
+ char *buff6 = encrypt(medical);
+ // GOOD: encrypt first
+ sprintf(buff4, "%s", buff6);
+}
+
+// test for CleartextFileWrite
+void stream()
+{
+ char *theZipcode = "cleartext zipcode!";
+ ofstream mystream;
+
+ // BAD: write zipcode to file in cleartext
+ mystream << "the zipcode is: " << theZipcode;
+
+ // BAD: write zipcode to file in cleartext
+ (mystream << "the zipcode is: ").write(theZipcode, strlen(theZipcode));
+
+ // GOOD: encrypt first
+ char *encrypted = encrypt(theZipcode);
+ mystream << "the zipcode is: " << encrypted;
+ (mystream << "the zipcode is: ").write(encrypted, strlen(encrypted));
+}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.cpp
new file mode 100644
index 000000000000..f9ffc5e8687e
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.cpp
@@ -0,0 +1,25 @@
+///// Library routines /////
+
+int scanf(const char *format, ...);
+int sscanf(const char *str, const char *format, ...);
+int fscanf(const char *str, const char *format, ...);
+
+///// Test code /////
+
+int main(int argc, char **argv)
+{
+
+ // BAD, do not use scanf without specifying a length first
+ char buf1[10];
+ scanf("%s", buf1);
+
+ // GOOD, length is specified
+ char buf2[10];
+ sscanf(buf2, "%9s");
+
+ // BAD, do not use scanf without specifying a length first
+ char file[10];
+ fscanf(file, "%s", buf2);
+
+ return 0;
+}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.expected
new file mode 100644
index 000000000000..701fd4320b0f
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.expected
@@ -0,0 +1,2 @@
+| MemoryUnsafeFunctionScan.cpp:14:5:14:9 | call to scanf | Dangerous use of one of the scanf functions |
+| MemoryUnsafeFunctionScan.cpp:22:5:22:10 | call to fscanf | Dangerous use of one of the scanf functions |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.qlref
new file mode 100644
index 000000000000..428d988a161d
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/semmle/tests/MemoryUnsafeFunctionScan.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.ql
\ No newline at end of file
diff --git a/cpp/ql/test/header-variant-tests/deduplication/functions.expected b/cpp/ql/test/header-variant-tests/deduplication/functions.expected
index a5ffe6852907..b55bfa8856fb 100644
--- a/cpp/ql/test/header-variant-tests/deduplication/functions.expected
+++ b/cpp/ql/test/header-variant-tests/deduplication/functions.expected
@@ -7,6 +7,8 @@
| foo.h:1:13:1:15 | foo | Function | 1 | |
| foo.h:1:13:1:15 | foo | MemberFunction | 1 | C |
| foo.h:1:13:1:15 | foo | MemberFunction | 1 | C |
+| main2.cpp:7:7:7:7 | C | MemberFunction | 0 | C |
+| main2.cpp:7:7:7:7 | C | MemberFunction | 0 | C |
| main2.cpp:7:7:7:7 | operator= | MemberFunction | 0 | C |
| main2.cpp:7:7:7:7 | operator= | MemberFunction | 0 | C |
| main2.cpp:7:7:7:7 | operator= | MemberFunction | 0 | C |
diff --git a/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.expected b/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.expected
index 9238c6182984..72beb5a1913e 100644
--- a/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.expected
+++ b/cpp/ql/test/library-tests/access/canAccessMember/canAccessMember.expected
@@ -8,89 +8,143 @@
| direct_friend::D::f | Can access D::f |
| direct_friend::D::f | Can access D::operator= |
| direct_friend::D::f | Can access D::x |
+| field_and_base::P::f | Can access B::B |
| field_and_base::P::f | Can access B::operator= |
+| field_and_base::P::f | Can access P::B |
+| field_and_base::P::f | Can access P::P |
| field_and_base::P::f | Can access P::f |
| field_and_base::P::f | Can access P::fieldB |
| field_and_base::P::f | Can access P::m |
| field_and_base::P::f | Can access P::m_static |
| field_and_base::P::f | Can access P::operator= |
+| friend_class::B::fun | Can access B::B |
| friend_class::B::fun | Can access B::a |
| friend_class::B::fun | Can access B::b |
| friend_class::B::fun | Can access B::fun |
| friend_class::B::fun | Can access B::operator= |
+| friend_class::B::fun | Can access C::C |
| friend_class::B::fun | Can access C::operator= |
+| friend_class::B::fun | Can access D1::D1 |
| friend_class::B::fun | Can access D1::operator= |
+| friend_class::B::fun | Can access D2::D2 |
| friend_class::B::fun | Can access D2::operator= |
+| friend_class::C::fun | Can access B::B |
| friend_class::C::fun | Can access B::operator= |
+| friend_class::C::fun | Can access C::B |
+| friend_class::C::fun | Can access C::C |
| friend_class::C::fun | Can access C::fun |
| friend_class::C::fun | Can access C::operator= |
| friend_class::C::fun | Can access C::x |
| friend_class::C::fun | Can access C::y |
+| friend_class::C::fun | Can access D1::D1 |
| friend_class::C::fun | Can access D1::operator= |
+| friend_class::C::fun | Can access D2::D2 |
| friend_class::C::fun | Can access D2::operator= |
+| friend_class::D1::fun | Can access B::B |
| friend_class::D1::fun | Can access B::a |
| friend_class::D1::fun | Can access B::b |
| friend_class::D1::fun | Can access B::fun |
| friend_class::D1::fun | Can access B::operator= |
+| friend_class::D1::fun | Can access C::C |
| friend_class::D1::fun | Can access C::operator= |
+| friend_class::D1::fun | Can access D1::C |
+| friend_class::D1::fun | Can access D1::D1 |
| friend_class::D1::fun | Can access D1::fun |
| friend_class::D1::fun | Can access D1::operator= |
+| friend_class::D1::fun | Can access D2::D2 |
| friend_class::D1::fun | Can access D2::operator= |
+| friend_class::D2::fun | Can access B::B |
| friend_class::D2::fun | Can access B::a |
| friend_class::D2::fun | Can access B::b |
| friend_class::D2::fun | Can access B::fun |
| friend_class::D2::fun | Can access B::operator= |
+| friend_class::D2::fun | Can access C::C |
| friend_class::D2::fun | Can access C::operator= |
+| friend_class::D2::fun | Can access D1::D1 |
| friend_class::D2::fun | Can access D1::operator= |
+| friend_class::D2::fun | Can access D2::B |
+| friend_class::D2::fun | Can access D2::D2 |
| friend_class::D2::fun | Can access D2::a |
| friend_class::D2::fun | Can access D2::b |
| friend_class::D2::fun | Can access D2::fun |
| friend_class::D2::fun | Can access D2::operator= |
| friend_fun::fun1 | Can access A::operator= |
+| friend_fun::fun1 | Can access B::B |
| friend_fun::fun1 | Can access B::operator= |
| friend_fun::fun2 | Can access A::operator= |
+| friend_fun::fun2 | Can access B::B |
| friend_fun::fun2 | Can access B::operator= |
| friend_fun::fun2 | Can access B::x |
| friend_fun::fun2 | Can access B::y |
+| mixed::B::fun | Can access A::A |
| mixed::B::fun | Can access A::operator= |
+| mixed::B::fun | Can access B::A |
+| mixed::B::fun | Can access B::B |
| mixed::B::fun | Can access B::fun |
| mixed::B::fun | Can access B::operator= |
+| mixed::B::fun | Can access C::C |
| mixed::B::fun | Can access C::operator= |
+| mixed::B::fun | Can access D::C |
+| mixed::B::fun | Can access D::D |
| mixed::B::fun | Can access D::operator= |
+| mixed::C::fun | Can access A::A |
| mixed::C::fun | Can access A::operator= |
+| mixed::C::fun | Can access B::B |
| mixed::C::fun | Can access B::operator= |
+| mixed::C::fun | Can access C::B |
+| mixed::C::fun | Can access C::C |
| mixed::C::fun | Can access C::fun |
| mixed::C::fun | Can access C::operator= |
+| mixed::C::fun | Can access D::B |
+| mixed::C::fun | Can access D::C |
+| mixed::C::fun | Can access D::D |
| mixed::C::fun | Can access D::fun |
| mixed::C::fun | Can access D::operator= |
+| mixed::D::fun | Can access A::A |
| mixed::D::fun | Can access A::operator= |
+| mixed::D::fun | Can access B::B |
| mixed::D::fun | Can access B::operator= |
+| mixed::D::fun | Can access C::B |
+| mixed::D::fun | Can access C::C |
| mixed::D::fun | Can access C::operator= |
+| mixed::D::fun | Can access D::B |
+| mixed::D::fun | Can access D::C |
+| mixed::D::fun | Can access D::D |
| mixed::D::fun | Can access D::fun |
| mixed::D::fun | Can access D::operator= |
| protected_derived::BP::f | Can access B::m |
| protected_derived::BP::f | Can access B::operator= |
+| protected_derived::BP::f | Can access BN::BN |
| protected_derived::BP::f | Can access BN::operator= |
+| protected_derived::BP::f | Can access BP::BP |
| protected_derived::BP::f | Can access BP::f |
| protected_derived::BP::f | Can access BP::m |
| protected_derived::BP::f | Can access BP::operator= |
+| protected_derived::BP::f | Can access BPNprot::BPNprot |
| protected_derived::BP::f | Can access BPNprot::operator= |
+| protected_derived::BP::f | Can access BPNpub::BP |
+| protected_derived::BP::f | Can access BPNpub::BPNpub |
| protected_derived::BP::f | Can access BPNpub::f |
| protected_derived::BP::f | Can access BPNpub::m |
| protected_derived::BP::f | Can access BPNpub::operator= |
+| protected_virtual::P::f | Can access B::B |
| protected_virtual::P::f | Can access B::operator= |
| protected_virtual::P::f | Can access Nprot::Nprot |
| protected_virtual::P::f | Can access Nprot::operator= |
+| protected_virtual::P::f | Can access Npub::B |
| protected_virtual::P::f | Can access Npub::Npub |
| protected_virtual::P::f | Can access Npub::m |
| protected_virtual::P::f | Can access Npub::operator= |
+| protected_virtual::P::f | Can access P::B |
| protected_virtual::P::f | Can access P::P |
| protected_virtual::P::f | Can access P::f |
| protected_virtual::P::f | Can access P::m |
| protected_virtual::P::f | Can access P::operator= |
| simple::Derived::castme | Can access Base::operator= |
+| simple::Derived::castme | Can access Derived::Derived |
| simple::Derived::castme | Can access Derived::castme |
| simple::Derived::castme | Can access Derived::operator= |
| simple::top | Can access Base::operator= |
+| simple::top | Can access Derived::Derived |
| simple::top | Can access Derived::castme |
| simple::top | Can access Derived::operator= |
diff --git a/cpp/ql/test/library-tests/complex_numbers/expr.ql b/cpp/ql/test/library-tests/complex_numbers/expr.ql
index 0f2e6f14d4e7..83c6dca9c64d 100644
--- a/cpp/ql/test/library-tests/complex_numbers/expr.ql
+++ b/cpp/ql/test/library-tests/complex_numbers/expr.ql
@@ -1,4 +1,4 @@
import cpp
from Expr e
-select e, e.getCanonicalQLClass()
+select e, e.getAPrimaryQlClass()
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp
index 9836f7fb20df..cd8b0daf6f30 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp
@@ -149,3 +149,63 @@ void test_conflated_fields2() {
taint_x(&p);
y_to_sink(&p);
}
+
+void sink(Point*);
+void sink(Point);
+
+void test_field_to_obj_taint_object(Point p) {
+ p.x = getenv("VAR")[0];
+ sink(p); // not tainted
+ sink(p.x); // tainted
+}
+
+void test_field_to_obj_taint_object_addrof(Point p) {
+ taint_x(&p);
+ sink(p); // tainted [field -> object]
+ sink(&p); // tainted [field -> object]
+ sink(p.x); // tainted
+}
+
+void test_field_to_obj_taint_pointer(Point* pp) {
+ pp->x = getenv("VAR")[0];
+ sink(pp); // tainted [field -> object]
+ sink(*pp); // not tainted
+}
+
+void call_sink_on_object(Point* pp) {
+ sink(pp); // tainted [field -> object]
+ sink(*pp); // tainted [field -> object]
+}
+
+void test_field_to_obj_taint_call_sink(Point* pp) {
+ pp->x = getenv("VAR")[0];
+ call_sink_on_object(pp);
+}
+
+void test_field_to_obj_taint_through_setter(Point* pp) {
+ taint_x(pp);
+ sink(pp); // tainted [field -> object]
+ sink(*pp); // not tainted
+}
+
+Point* getPoint();
+
+void test_field_to_obj_local_variable() {
+ Point* pp = getPoint();
+ pp->x = getenv("VAR")[0];
+ sink(pp); // not tainted
+ sink(*pp); // not tainted
+}
+
+void test_field_to_obj_taint_array(Point* pp, int i) {
+ pp[0].x = getenv("VAR")[0];
+ sink(pp[i]); // not tainted
+ sink(pp); // tainted [field -> object]
+ sink(*pp); // not tainted
+}
+
+void test_field_to_obj_test_pointer_arith(Point* pp) {
+ (pp + sizeof(*pp))->x = getenv("VAR")[0];
+ sink(pp); // tainted [field -> object]
+ sink(pp + sizeof(*pp)); // tainted [field -> object]
+}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/stl.cpp
similarity index 79%
rename from cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp
rename to cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/stl.cpp
index d92bb39d1584..3454e6ac9472 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.cpp
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/stl.cpp
@@ -1,4 +1,6 @@
+#include "shared.h"
+
typedef unsigned long size_t;
namespace std
@@ -57,8 +59,7 @@ namespace std
using stringstream = basic_stringstream;
}
-char *source();
-void sink(const char *s) {};
+char *source() { return getenv("USERDATA"); }
void sink(const std::string &s) {};
void sink(const std::stringstream &s) {};
@@ -70,9 +71,9 @@ void test_string()
sink(a); // tainted
sink(b);
- sink(c); // tainted
+ sink(c); // tainted [NOT DETECTED]
sink(b.c_str());
- sink(c.c_str()); // tainted
+ sink(c.c_str()); // tainted [NOT DETECTED]
}
void test_stringstream()
@@ -87,14 +88,14 @@ void test_stringstream()
ss5 << t;
sink(ss1);
- sink(ss2); // tainted [NOT DETECTED]
+ sink(ss2); // tainted
sink(ss3); // tainted [NOT DETECTED]
- sink(ss4); // tainted [NOT DETECTED]
+ sink(ss4); // tainted
sink(ss5); // tainted [NOT DETECTED]
sink(ss1.str());
- sink(ss2.str()); // tainted [NOT DETECTED]
+ sink(ss2.str()); // tainted
sink(ss3.str()); // tainted [NOT DETECTED]
- sink(ss4.str()); // tainted [NOT DETECTED]
+ sink(ss4.str()); // tainted
sink(ss5.str()); // tainted [NOT DETECTED]
}
@@ -122,12 +123,37 @@ void sink(const char *filename, const char *mode);
void test_strings2()
{
string path1 = user_input();
- sink(path1.c_str(), "r"); // tainted
+ sink(path1.c_str(), "r"); // tainted [NOT DETECTED]
string path2;
path2 = user_input();
sink(path2.c_str(), "r"); // tainted
string path3(user_input());
- sink(path3.c_str(), "r"); // tainted
+ sink(path3.c_str(), "r"); // tainted [NOT DETECTED]
+}
+
+void test_string3()
+{
+ const char *cs = source();
+
+ // convert char * -> std::string
+ std::string ss(cs);
+
+ sink(cs); // tainted
+ sink(ss); // tainted [NOT DETECTED]
+}
+
+void test_string4()
+{
+ const char *cs = source();
+
+ // convert char * -> std::string
+ std::string ss(cs);
+
+ // convert back std::string -> char *
+ cs = ss.c_str();
+
+ sink(cs); // tainted [NOT DETECTED]
+ sink(ss); // tainted [NOT DETECTED]
}
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
index 4a87761da6fa..83f50d7fe157 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
@@ -112,9 +112,28 @@
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:16 | call to getenv |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:26 | (int)... |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:11:140:26 | access to array |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:143:23:143:24 | pp |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:144:8:144:9 | pp |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:150:13:150:14 | & ... |
+| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:166:10:166:10 | x |
+| defaulttainttracking.cpp:140:11:140:16 | call to getenv | shared.h:6:15:6:23 | sinkparam |
+| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:14 | call to getenv |
+| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:24 | (int)... |
+| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:9:157:24 | access to array |
+| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:159:10:159:10 | x |
+| defaulttainttracking.cpp:157:9:157:14 | call to getenv | shared.h:6:15:6:23 | sinkparam |
+| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:16 | call to getenv |
+| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:26 | (int)... |
+| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:11:170:26 | access to array |
+| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:16 | call to getenv |
+| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:26 | (int)... |
+| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:11:181:26 | access to array |
+| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:16 | call to getenv |
+| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:26 | (int)... |
+| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:11:195:26 | access to array |
+| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:18 | call to getenv |
+| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:28 | (int)... |
+| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:13:201:28 | access to array |
+| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:32 | call to getenv |
+| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:42 | (int)... |
+| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:27:208:42 | access to array |
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:24:28:27 | call to atoi |
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:34 | call to getenv |
| dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:45 | (const char *)... |
@@ -153,6 +172,71 @@
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:15:13:20 | call to getenv |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:16:15:16:21 | global2 |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:15:23:20 | call to getenv |
+| stl.cpp:62:25:62:30 | call to getenv | shared.h:5:23:5:31 | sinkparam |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:21:29:21:29 | s |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:78:43:104 | p#0 |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:114:43:118 | p#1 |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:25:62:30 | call to getenv |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:64:36:64:36 | s |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:8:68:8 | a |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:68:12:68:17 | call to source |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:21 | call to source |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:23 | (const char *)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | (const char *)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:72:7:72:7 | a |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:21 | call to source |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:23 | (const char *)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:6 | call to operator<< |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:17 | (reference dereference) |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:14 | call to source |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:9:85:16 | (const char *)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:15 | call to operator<< |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:26 | (reference dereference) |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:23 | call to source |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:18:86:25 | (const char *)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:6 | call to operator<< |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference dereference) |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference to) |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:14 | call to source |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:18 | call to operator<< |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:26 | (reference dereference) |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (const stringstream)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (reference to) |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | ss2 |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (const stringstream)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (reference to) |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | ss4 |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | (const basic_stringstream, allocator>)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | ss2 |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | (const basic_stringstream, allocator>)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | ss4 |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:118:10:118:15 | call to source |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:26 | call to user_input |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:17:125:28 | (const char *)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:128:9:128:13 | path2 |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:19 | call to user_input |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | (const char *)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | (const basic_string, allocator>)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:24 | call to user_input |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:26 | (const char *)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:14:138:15 | cs |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:24 | call to source |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:138:19:138:26 | (const char *)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:18 | cs |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:143:7:143:8 | cs |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:14:149:15 | cs |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:24 | call to source |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:149:19:149:26 | (const char *)... |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:18 | cs |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string |
| test_diff.cpp:92:10:92:13 | argv | shared.h:5:23:5:31 | sinkparam |
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:13 | argv |
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:16 | (const char *)... |
diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
index 79b02cd9d608..414ee9623e59 100644
--- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
@@ -26,11 +26,53 @@
| defaulttainttracking.cpp:133:9:133:14 | call to getenv | defaulttainttracking.cpp:134:10:134:10 | x | IR only |
| defaulttainttracking.cpp:133:9:133:14 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only |
| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:140:7:140:7 | x | AST only |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:143:23:143:24 | pp | IR only |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:144:8:144:9 | pp | IR only |
-| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:150:13:150:14 | & ... | IR only |
+| defaulttainttracking.cpp:140:11:140:16 | call to getenv | defaulttainttracking.cpp:166:10:166:10 | x | IR only |
+| defaulttainttracking.cpp:140:11:140:16 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only |
+| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:157:5:157:5 | x | AST only |
+| defaulttainttracking.cpp:157:9:157:14 | call to getenv | defaulttainttracking.cpp:159:10:159:10 | x | IR only |
+| defaulttainttracking.cpp:157:9:157:14 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only |
+| defaulttainttracking.cpp:170:11:170:16 | call to getenv | defaulttainttracking.cpp:170:7:170:7 | x | AST only |
+| defaulttainttracking.cpp:181:11:181:16 | call to getenv | defaulttainttracking.cpp:181:7:181:7 | x | AST only |
+| defaulttainttracking.cpp:195:11:195:16 | call to getenv | defaulttainttracking.cpp:195:7:195:7 | x | AST only |
+| defaulttainttracking.cpp:201:13:201:18 | call to getenv | defaulttainttracking.cpp:201:9:201:9 | x | AST only |
+| defaulttainttracking.cpp:208:27:208:32 | call to getenv | defaulttainttracking.cpp:208:23:208:23 | x | AST only |
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:5:13:11 | global1 | AST only |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:5:23:11 | global2 | AST only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:43:78:43:104 | p#0 | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:62:7:62:12 | source | AST only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:64:36:64:36 | s | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:70:16:70:24 | call to basic_string | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:82:16:82:24 | call to basic_string | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:6 | call to operator<< | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:85:6:85:17 | (reference dereference) | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:15 | call to operator<< | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:86:15:86:26 | (reference dereference) | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:6 | call to operator<< | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference dereference) | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:6:87:19 | (reference to) | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:9:87:16 | (const char *)... | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:18 | call to operator<< | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:87:18:87:26 | (reference dereference) | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (const stringstream)... | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | (reference to) | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:91:7:91:9 | ss2 | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (const stringstream)... | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | (reference to) | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:93:7:93:9 | ss4 | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | (const basic_stringstream, allocator>)... | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:96:7:96:9 | ss2 | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | (const basic_stringstream, allocator>)... | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:98:7:98:9 | ss4 | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:117:7:117:16 | user_input | AST only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:125:16:125:28 | call to basic_string | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:128:9:128:13 | path2 | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:129:10:129:21 | call to basic_string | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | (const basic_string, allocator>)... | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:130:7:130:11 | path2 | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:132:15:132:27 | call to basic_string | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:141:17:141:19 | call to basic_string | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:152:17:152:19 | call to basic_string | IR only |
+| stl.cpp:62:25:62:30 | call to getenv | stl.cpp:157:7:157:8 | cs | AST only |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |
| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:36:24:36:24 | p | AST only |
| test_diff.cpp:111:10:111:13 | argv | shared.h:5:23:5:31 | sinkparam | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll b/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll
index 58a1dd2672b1..7ea87aeb583b 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll
@@ -10,7 +10,7 @@ import semmle.code.cpp.ir.IR
class TestBarrierGuard extends DataFlow::BarrierGuard {
TestBarrierGuard() { this.(CallInstruction).getStaticCallTarget().getName() = "guarded" }
- override predicate checks(Instruction checked, boolean isTrue) {
+ override predicate checksInstr(Instruction checked, boolean isTrue) {
checked = this.(CallInstruction).getPositionalArgument(0) and
isTrue = true
}
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/clang.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/clang.cpp
index 0ad17fb47afb..033bdfd1899e 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/clang.cpp
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/clang.cpp
@@ -48,6 +48,6 @@ void following_pointers(
int stackArray[2] = { source(), source() };
stackArray[0] = source();
- sink(stackArray); // no flow
+ sink(stackArray); // flow
}
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected
index d01f0daa6a24..901339c4f386 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected
@@ -1,6 +1,5 @@
uniqueEnclosingCallable
-uniqueTypeBound
-uniqueTypeRepr
+uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
@@ -15,7 +14,6 @@ postHasUniquePre
uniquePostUpdate
postIsInSameCallable
reverseRead
-storeIsPostUpdate
argHasPostUpdate
| lambdas.cpp:18:7:18:7 | a | ArgumentNode is missing PostUpdateNode. |
| lambdas.cpp:25:2:25:2 | b | ArgumentNode is missing PostUpdateNode. |
@@ -23,3 +21,69 @@ argHasPostUpdate
| lambdas.cpp:38:2:38:2 | d | ArgumentNode is missing PostUpdateNode. |
| lambdas.cpp:45:2:45:2 | e | ArgumentNode is missing PostUpdateNode. |
| test.cpp:67:29:67:35 | source1 | ArgumentNode is missing PostUpdateNode. |
+postWithInFlow
+| BarrierGuard.cpp:49:6:49:6 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| BarrierGuard.cpp:60:7:60:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:22:9:22:20 | sourceArray1 [inner post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:28:22:28:23 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:50:3:50:12 | stackArray [inner post update] | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:50:3:50:15 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:60:3:60:14 | globalBottom [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:61:3:61:14 | globalMiddle [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:78:24:78:37 | call to allocateBottom [inner post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:148:5:148:5 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| dispatch.cpp:168:8:168:8 | f [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:24:9:24:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:24:20:24:20 | y [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:26:9:26:9 | x [post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:26:19:26:24 | coords [inner post update] | PostUpdateNode should not be the target of local flow. |
+| example.c:28:23:28:25 | pos [inner post update] | PostUpdateNode should not be the target of local flow. |
+| globals.cpp:13:5:13:19 | flowTestGlobal1 [post update] | PostUpdateNode should not be the target of local flow. |
+| globals.cpp:23:5:23:19 | flowTestGlobal2 [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:23:3:23:14 | v [post update] | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:43:3:43:3 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:11:5:11:7 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:11:5:11:7 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:20:5:20:7 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:22:7:22:9 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:24:7:24:9 | lhs [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:29:5:29:7 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:31:7:31:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:39:7:39:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:44:5:44:7 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:46:7:46:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:48:7:48:9 | out [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:75:9:75:11 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:83:9:83:11 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:87:11:87:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:89:11:89:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:94:9:94:11 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:96:11:96:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:104:11:104:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:109:9:109:11 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:113:11:113:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:115:11:115:13 | val [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:91:3:91:9 | source1 [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:115:3:115:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:115:4:115:6 | out [inner post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:120:3:120:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:120:4:120:6 | out [inner post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:125:3:125:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:125:4:125:6 | out [inner post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:333:5:333:13 | globalVar [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:347:5:347:13 | globalVar [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:359:5:359:9 | field [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:373:5:373:9 | field [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:384:10:384:13 | ref arg & ... | PostUpdateNode should not be the target of local flow. |
+| test.cpp:384:11:384:13 | tmp [inner post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:391:10:391:13 | ref arg & ... | PostUpdateNode should not be the target of local flow. |
+| test.cpp:391:11:391:13 | tmp [inner post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:400:10:400:13 | ref arg & ... | PostUpdateNode should not be the target of local flow. |
+| test.cpp:400:11:400:13 | tmp [inner post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:407:10:407:13 | ref arg & ... | PostUpdateNode should not be the target of local flow. |
+| test.cpp:407:11:407:13 | tmp [inner post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:423:21:423:25 | local [inner post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:436:19:436:23 | local [inner post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:465:3:465:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:465:4:465:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
+| test.cpp:470:22:470:22 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
index 0bb9343dcaf8..038a138b2c0b 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
@@ -1,6 +1,5 @@
uniqueEnclosingCallable
-uniqueTypeBound
-uniqueTypeRepr
+uniqueType
uniqueNodeLocation
| BarrierGuard.cpp:2:11:2:13 | p#0 | Node should have one location but has 6. |
| acrossLinkTargets.cpp:2:11:2:13 | p#0 | Node should have one location but has 6. |
@@ -28,9 +27,59 @@ localCallNodes
postIsNotPre
postHasUniquePre
uniquePostUpdate
-| ref.cpp:83:5:83:17 | Chi | Node has multiple PostUpdateNodes. |
-| ref.cpp:109:5:109:22 | Chi | Node has multiple PostUpdateNodes. |
postIsInSameCallable
reverseRead
-storeIsPostUpdate
argHasPostUpdate
+postWithInFlow
+| BarrierGuard.cpp:49:3:49:17 | Chi | PostUpdateNode should not be the target of local flow. |
+| BarrierGuard.cpp:60:3:60:18 | Chi | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:28:3:28:34 | Chi | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:34:22:34:27 | Chi | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:34:32:34:37 | Chi | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:39:32:39:37 | Chi | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:39:42:39:47 | Chi | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:43:35:43:40 | Chi | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:43:51:43:51 | Chi | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:49:25:49:30 | Chi | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:49:35:49:40 | Chi | PostUpdateNode should not be the target of local flow. |
+| clang.cpp:50:3:50:26 | Chi | PostUpdateNode should not be the target of local flow. |
+| example.c:17:19:17:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| example.c:17:21:17:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| example.c:24:2:24:30 | Chi | PostUpdateNode should not be the target of local flow. |
+| example.c:24:13:24:30 | Chi | PostUpdateNode should not be the target of local flow. |
+| example.c:26:2:26:25 | Chi | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. |
+| file://:0:0:0:0 | Chi | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:13:12:13:12 | Chi | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:13:15:13:15 | Chi | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:28:10:31:2 | Chi | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:28:10:31:2 | Chi | PostUpdateNode should not be the target of local flow. |
+| lambdas.cpp:43:3:43:14 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:11:5:11:13 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:20:5:20:13 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:22:7:22:13 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:24:7:24:13 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:29:5:29:18 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:31:7:31:13 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:39:7:39:13 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:44:5:44:18 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:46:7:46:13 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:48:7:48:13 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:75:5:75:17 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:83:5:83:17 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:87:7:87:17 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:89:7:89:17 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:94:5:94:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:96:7:96:17 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:104:7:104:17 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:109:5:109:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:113:7:113:17 | Chi | PostUpdateNode should not be the target of local flow. |
+| ref.cpp:115:7:115:17 | Chi | PostUpdateNode should not be the target of local flow. |
+| test.cpp:91:3:91:18 | Chi | PostUpdateNode should not be the target of local flow. |
+| test.cpp:115:3:115:17 | Chi | PostUpdateNode should not be the target of local flow. |
+| test.cpp:120:3:120:10 | Chi | PostUpdateNode should not be the target of local flow. |
+| test.cpp:125:3:125:11 | Chi | PostUpdateNode should not be the target of local flow. |
+| test.cpp:359:5:359:20 | Chi | PostUpdateNode should not be the target of local flow. |
+| test.cpp:373:5:373:20 | Chi | PostUpdateNode should not be the target of local flow. |
+| test.cpp:465:3:465:15 | Chi | PostUpdateNode should not be the target of local flow. |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected
index 78d20cbf7ae0..75cdaee69ac4 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected
@@ -7,11 +7,15 @@
| example.c:17:19:17:22 | {...} | example.c:26:19:26:24 | coords |
| example.c:24:2:24:7 | coords [post update] | example.c:26:2:26:7 | coords |
| example.c:24:2:24:7 | coords [post update] | example.c:26:19:26:24 | coords |
+| example.c:24:2:24:30 | ... = ... | example.c:24:9:24:9 | x [post update] |
| example.c:24:13:24:18 | coords [post update] | example.c:24:2:24:7 | coords |
| example.c:24:13:24:18 | coords [post update] | example.c:26:2:26:7 | coords |
| example.c:24:13:24:18 | coords [post update] | example.c:26:19:26:24 | coords |
| example.c:24:13:24:30 | ... = ... | example.c:24:2:24:30 | ... = ... |
+| example.c:24:13:24:30 | ... = ... | example.c:24:20:24:20 | y [post update] |
+| example.c:24:20:24:20 | y | example.c:24:13:24:30 | ... = ... |
| example.c:24:24:24:30 | ... + ... | example.c:24:13:24:30 | ... = ... |
+| example.c:26:2:26:25 | ... = ... | example.c:26:9:26:9 | x [post update] |
| example.c:26:13:26:16 | call to getX | example.c:26:2:26:25 | ... = ... |
| example.c:26:18:26:24 | ref arg & ... | example.c:26:2:26:7 | coords |
| example.c:26:18:26:24 | ref arg & ... | example.c:26:19:26:24 | coords [inner post update] |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/ref.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/ref.cpp
index 8e1ac7657ea4..52849a557843 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/ref.cpp
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/ref.cpp
@@ -56,13 +56,13 @@ namespace withoutFields {
sink(x1); // flow [FALSE POSITIVE from uninitialized]
notAssign(x2, source());
- sink(x2); // no flow [FALSE POSITIVE from uninitialized]
+ sink(x2); // no flow [FALSE POSITIVE from uninitialized, FALSE POSITIVE by IR]
sourceToParamWrapper(x3);
sink(x3); // flow [FALSE POSITIVE from uninitialized]
notSource(x4);
- sink(x4); // no flow [FALSE POSITIVE from uninitialized]
+ sink(x4); // no flow [FALSE POSITIVE from uninitialized, FALSE POSITIVE by IR]
}
}
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
index bce01956dc73..25ab760cbcd9 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
@@ -428,7 +428,7 @@ void intPointerSourceCaller2() {
int local[1];
intPointerSource(local);
sink(local); // tainted
- sink(*local); // clean
+ sink(*local); // tainted
}
void intArraySourceCaller() {
@@ -441,7 +441,7 @@ void intArraySourceCaller2() {
int local[2];
intArraySource(local, 2);
sink(local); // tainted
- sink(*local); // clean
+ sink(*local); // tainted
}
///////////////////////////////////////////////////////////////////////////////
@@ -468,5 +468,5 @@ void intOutparamSource(int *p) {
void viaOutparam() {
int x = 0;
intOutparamSource(&x);
- sink(x); // tainted [FALSE NEGATIVE]
+ sink(x); // tainted
}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected
index 6ff83eb74b5f..fbe5a51298ed 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected
@@ -16,6 +16,7 @@
| clang.cpp:30:27:30:34 | call to getFirst | clang.cpp:28:27:28:32 | call to source |
| clang.cpp:37:10:37:11 | m2 | clang.cpp:34:32:34:37 | call to source |
| clang.cpp:45:17:45:18 | m2 | clang.cpp:43:35:43:40 | call to source |
+| clang.cpp:51:8:51:17 | stackArray | clang.cpp:50:19:50:24 | call to source |
| dispatch.cpp:11:38:11:38 | x | dispatch.cpp:37:19:37:24 | call to source |
| dispatch.cpp:11:38:11:38 | x | dispatch.cpp:45:18:45:23 | call to source |
| dispatch.cpp:35:16:35:25 | call to notSource1 | dispatch.cpp:9:37:9:42 | call to source |
@@ -79,12 +80,17 @@
| test.cpp:424:8:424:12 | local | test.cpp:423:20:423:25 | ref arg & ... |
| test.cpp:430:8:430:12 | local | test.cpp:428:7:428:11 | local |
| test.cpp:430:8:430:12 | local | test.cpp:429:20:429:24 | ref arg local |
+| test.cpp:431:8:431:13 | * ... | test.cpp:428:7:428:11 | local |
+| test.cpp:431:8:431:13 | * ... | test.cpp:429:20:429:24 | ref arg local |
| test.cpp:437:8:437:12 | local | test.cpp:435:7:435:11 | local |
| test.cpp:437:8:437:12 | local | test.cpp:436:18:436:23 | ref arg & ... |
| test.cpp:443:8:443:12 | local | test.cpp:441:7:441:11 | local |
| test.cpp:443:8:443:12 | local | test.cpp:442:18:442:22 | ref arg local |
+| test.cpp:444:8:444:13 | * ... | test.cpp:441:7:441:11 | local |
+| test.cpp:444:8:444:13 | * ... | test.cpp:442:18:442:22 | ref arg local |
| test.cpp:450:9:450:22 | (statement expression) | test.cpp:449:26:449:32 | source1 |
| test.cpp:461:8:461:12 | local | test.cpp:449:26:449:32 | source1 |
+| test.cpp:471:8:471:8 | x | test.cpp:465:8:465:13 | call to source |
| true_upon_entry.cpp:21:8:21:8 | x | true_upon_entry.cpp:17:11:17:16 | call to source |
| true_upon_entry.cpp:29:8:29:8 | x | true_upon_entry.cpp:27:9:27:14 | call to source |
| true_upon_entry.cpp:39:8:39:8 | x | true_upon_entry.cpp:33:11:33:16 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected
index d663862361cf..7d4bcf36adb2 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected
@@ -2,6 +2,7 @@
| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:62:14:62:14 | AST only |
| clang.cpp:12:9:12:20 | clang.cpp:22:8:22:20 | AST only |
| clang.cpp:39:42:39:47 | clang.cpp:41:18:41:19 | IR only |
+| clang.cpp:50:19:50:24 | clang.cpp:51:8:51:17 | AST only |
| dispatch.cpp:16:37:16:42 | dispatch.cpp:32:16:32:24 | IR only |
| dispatch.cpp:16:37:16:42 | dispatch.cpp:40:15:40:23 | IR only |
| dispatch.cpp:22:37:22:42 | dispatch.cpp:31:16:31:24 | IR only |
@@ -19,13 +20,12 @@
| globals.cpp:13:23:13:28 | globals.cpp:12:10:12:24 | IR only |
| globals.cpp:23:23:23:28 | globals.cpp:19:10:19:24 | IR only |
| lambdas.cpp:8:10:8:15 | lambdas.cpp:21:3:21:6 | AST only |
-| lambdas.cpp:43:7:43:12 | lambdas.cpp:46:7:46:7 | AST only |
-| ref.cpp:29:11:29:16 | ref.cpp:62:10:62:11 | AST only |
+| ref.cpp:44:11:44:16 | ref.cpp:65:10:65:11 | IR only |
| ref.cpp:53:9:53:10 | ref.cpp:56:10:56:11 | AST only |
| ref.cpp:53:13:53:14 | ref.cpp:59:10:59:11 | AST only |
| ref.cpp:53:17:53:18 | ref.cpp:62:10:62:11 | AST only |
| ref.cpp:53:21:53:22 | ref.cpp:65:10:65:11 | AST only |
-| ref.cpp:55:23:55:28 | ref.cpp:56:10:56:11 | AST only |
+| ref.cpp:58:19:58:24 | ref.cpp:59:10:59:11 | IR only |
| test.cpp:75:7:75:8 | test.cpp:76:8:76:9 | AST only |
| test.cpp:83:7:83:8 | test.cpp:84:8:84:18 | AST only |
| test.cpp:83:7:83:8 | test.cpp:86:8:86:9 | AST only |
@@ -41,11 +41,15 @@
| test.cpp:422:7:422:11 | test.cpp:424:8:424:12 | AST only |
| test.cpp:423:20:423:25 | test.cpp:424:8:424:12 | AST only |
| test.cpp:428:7:428:11 | test.cpp:430:8:430:12 | AST only |
+| test.cpp:428:7:428:11 | test.cpp:431:8:431:13 | AST only |
| test.cpp:429:20:429:24 | test.cpp:430:8:430:12 | AST only |
+| test.cpp:429:20:429:24 | test.cpp:431:8:431:13 | AST only |
| test.cpp:435:7:435:11 | test.cpp:437:8:437:12 | AST only |
| test.cpp:436:18:436:23 | test.cpp:437:8:437:12 | AST only |
| test.cpp:441:7:441:11 | test.cpp:443:8:443:12 | AST only |
+| test.cpp:441:7:441:11 | test.cpp:444:8:444:13 | AST only |
| test.cpp:442:18:442:22 | test.cpp:443:8:443:12 | AST only |
+| test.cpp:442:18:442:22 | test.cpp:444:8:444:13 | AST only |
| true_upon_entry.cpp:9:11:9:16 | true_upon_entry.cpp:13:8:13:8 | IR only |
| true_upon_entry.cpp:62:11:62:16 | true_upon_entry.cpp:66:8:66:8 | IR only |
| true_upon_entry.cpp:98:11:98:16 | true_upon_entry.cpp:105:8:105:8 | IR only |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected
index f7cedc063eee..945bb7136ce6 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected
@@ -44,6 +44,11 @@
| lambdas.cpp:29:3:29:6 | t | lambdas.cpp:8:10:8:15 | call to source |
| lambdas.cpp:35:8:35:8 | a | lambdas.cpp:8:10:8:15 | call to source |
| lambdas.cpp:41:8:41:8 | (reference dereference) | lambdas.cpp:8:10:8:15 | call to source |
+| lambdas.cpp:46:7:46:7 | w | lambdas.cpp:43:7:43:12 | call to source |
+| ref.cpp:56:10:56:11 | x1 | ref.cpp:55:23:55:28 | call to source |
+| ref.cpp:59:10:59:11 | x2 | ref.cpp:58:19:58:24 | call to source |
+| ref.cpp:62:10:62:11 | x3 | ref.cpp:29:11:29:16 | call to source |
+| ref.cpp:65:10:65:11 | x4 | ref.cpp:44:11:44:16 | call to source |
| ref.cpp:123:13:123:15 | val | ref.cpp:122:23:122:28 | call to source |
| ref.cpp:126:13:126:15 | val | ref.cpp:125:19:125:24 | call to source |
| ref.cpp:129:13:129:15 | val | ref.cpp:94:15:94:20 | call to source |
@@ -77,6 +82,7 @@
| test.cpp:394:10:394:12 | tmp | test.cpp:388:53:388:59 | source1 |
| test.cpp:450:9:450:22 | (statement expression) | test.cpp:449:26:449:32 | source1 |
| test.cpp:461:8:461:12 | local | test.cpp:449:26:449:32 | source1 |
+| test.cpp:471:8:471:8 | x | test.cpp:465:8:465:13 | call to source |
| true_upon_entry.cpp:13:8:13:8 | x | true_upon_entry.cpp:9:11:9:16 | call to source |
| true_upon_entry.cpp:21:8:21:8 | x | true_upon_entry.cpp:17:11:17:16 | call to source |
| true_upon_entry.cpp:29:8:29:8 | x | true_upon_entry.cpp:27:9:27:14 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/A.cpp b/cpp/ql/test/library-tests/dataflow/fields/A.cpp
index ba6736520a9b..d65e900af960 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/A.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/A.cpp
@@ -53,8 +53,8 @@ class A
{
B *b = new B();
b->set(new C1());
- sink(b->get()); // $ast $f-:ir
- sink((new B(new C()))->get()); // $ast $f-:ir
+ sink(b->get()); // $ast $ir=55:12
+ sink((new B(new C()))->get()); // $ast $ir
}
void f3()
@@ -104,7 +104,7 @@ class A
{
if (C1 *c1 = dynamic_cast(c))
{
- sink(c1->a); // $ast $ir
+ sink(c1->a); // $ast,ir
}
C *cc;
if (C2 *c2 = dynamic_cast(c))
@@ -129,7 +129,7 @@ class A
{
B *b = new B();
f7(b);
- sink(b->c); // $ast $f-:ir
+ sink(b->c); // $ast,ir
}
class D
@@ -149,7 +149,7 @@ class A
{
B *b = new B();
D *d = new D(b, r());
- sink(d->b); // $ast=143:25 $ast=150:12 $f-:ir
+ sink(d->b); // $ast,ir=143:25 $ast,ir=150:12
sink(d->b->c); // $ast $f-:ir
sink(b->c); // $ast,ir
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/C.cpp b/cpp/ql/test/library-tests/dataflow/fields/C.cpp
index 896b754ff310..892d298a81d0 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/C.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/C.cpp
@@ -26,9 +26,9 @@ class C
void func()
{
- sink(s1); // $ast $f-:ir
+ sink(s1); // $ast $ir
sink(s2); // $f-:ast $f-:ir
- sink(s3); // $ast $f-:ir
+ sink(s3); // $ast $ir
sink(s4); // $f-:ast $f-:ir
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll b/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll
index 3451061436cf..41ddf5a17a8b 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll
+++ b/cpp/ql/test/library-tests/dataflow/fields/IRConfiguration.qll
@@ -18,7 +18,7 @@ class Conf extends Configuration {
override predicate isSink(Node sink) {
exists(Call c |
c.getTarget().hasName("sink") and
- c.getAnArgument() = sink.asExpr()
+ c.getAnArgument() = sink.asConvertedExpr()
)
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/Nodes.qll b/cpp/ql/test/library-tests/dataflow/fields/Nodes.qll
new file mode 100644
index 000000000000..eb6f3247b82e
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/fields/Nodes.qll
@@ -0,0 +1,41 @@
+private import semmle.code.cpp.ir.dataflow.DataFlow as IR
+private import semmle.code.cpp.dataflow.DataFlow as AST
+private import cpp
+
+private newtype TNode =
+ TASTNode(AST::DataFlow::Node n) or
+ TIRNode(IR::DataFlow::Node n)
+
+class Node extends TNode {
+ string toString() { none() }
+
+ IR::DataFlow::Node asIR() { none() }
+
+ AST::DataFlow::Node asAST() { none() }
+
+ Location getLocation() { none() }
+}
+
+class ASTNode extends Node, TASTNode {
+ AST::DataFlow::Node n;
+
+ ASTNode() { this = TASTNode(n) }
+
+ override string toString() { result = n.toString() }
+
+ override AST::DataFlow::Node asAST() { result = n }
+
+ override Location getLocation() { result = n.getLocation() }
+}
+
+class IRNode extends Node, TIRNode {
+ IR::DataFlow::Node n;
+
+ IRNode() { this = TIRNode(n) }
+
+ override string toString() { result = n.toString() }
+
+ override IR::DataFlow::Node asIR() { result = n }
+
+ override Location getLocation() { result = n.getLocation() }
+}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp b/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp
index f096692419be..df33dbb288e2 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/aliasing.cpp
@@ -92,3 +92,116 @@ void nestedAssign() {
w.s.m1 = user_input();
sink(w.s.m1); // $ast,ir
}
+
+void addressOfField() {
+ S s;
+ s.m1 = user_input();
+
+ S s_copy = s;
+ int* px = &s_copy.m1;
+ sink(*px); // $f-:ast $ir
+}
+
+void taint_a_ptr(int* pa) {
+ *pa = user_input();
+}
+
+void test_field_conflation_array_content() {
+ S s;
+ taint_a_ptr(&s.m1);
+ sink(s.m2);
+}
+
+struct S_with_pointer {
+ int m1, m2;
+ int* data;
+};
+
+void pointer_deref(int* xs) {
+ taint_a_ptr(xs);
+ sink(xs[0]); // $f-:ast $ir
+}
+
+void pointer_deref_sub(int* xs) {
+ taint_a_ptr(xs - 2);
+ sink(*(xs - 2)); // $f-:ast $ir
+}
+
+void pointer_many_addrof_and_deref(int* xs) {
+ taint_a_ptr(xs);
+ sink(*&*&*xs); // $f-:ast $ir
+}
+
+void pointer_unary_plus(int* xs) {
+ taint_a_ptr(+xs);
+ sink(*+xs); // $f-:ast $ir
+}
+
+void pointer_member_index(S_with_pointer s) {
+ taint_a_ptr(s.data);
+ // `s.data` is points to all-aliased-memory
+ sink(s.data[0]); // $f-:ast,ir
+}
+
+void member_array_different_field(S_with_pointer* s) {
+ taint_a_ptr(&s[0].m1);
+ sink(s[0].m2);
+}
+
+struct S_with_array {
+ int m1, m2;
+ int data[10];
+};
+
+void pointer_member_deref() {
+ S_with_array s;
+ taint_a_ptr(s.data);
+ sink(*s.data); // $ir,ast
+}
+
+void array_member_deref() {
+ S_with_array s;
+ taint_a_ptr(s.data);
+ sink(s.data[0]); // $ir,ast
+}
+
+struct S2 {
+ S s;
+ int m3;
+};
+
+void deep_member_field_dot() {
+ S2 s2;
+ taint_a_ptr(&s2.s.m1);
+ sink(s2.s.m1); // $ir,ast
+}
+
+void deep_member_field_dot_different_fields() {
+ S2 s2;
+ taint_a_ptr(&s2.s.m1);
+ sink(s2.s.m2);
+}
+
+void deep_member_field_dot_2() {
+ S2 s2;
+ taint_a_ptr(&s2.s.m1);
+ S2 s2_2 = s2;
+ sink(s2_2.s.m1); // $ir,ast
+}
+
+void deep_member_field_dot_different_fields_2() {
+ S2 s2;
+ taint_a_ptr(&s2.s.m1);
+ S2 s2_2 = s2;
+ sink(s2_2.s.m2);
+}
+
+void deep_member_field_arrow(S2 *ps2) {
+ taint_a_ptr(&ps2->s.m1);
+ sink(ps2->s.m1); // $ir,ast
+}
+
+void deep_member_field_arrow_different_fields(S2 *ps2) {
+ taint_a_ptr(&ps2->s.m1);
+ sink(ps2->s.m2);
+}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/dataflow/fields/arrays.cpp b/cpp/ql/test/library-tests/dataflow/fields/arrays.cpp
new file mode 100644
index 000000000000..68290dc70770
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/fields/arrays.cpp
@@ -0,0 +1,51 @@
+void sink(void *o);
+void *user_input(void);
+
+void local_array() {
+ void *arr[10] = { 0 };
+ arr[0] = user_input();
+ sink(arr[0]); // $ast,ir
+ sink(arr[1]); // $f+:ast
+ sink(*arr); // $ast,ir
+ sink(*&arr[0]); // $ast,ir
+}
+
+void local_array_convoluted_assign() {
+ void *arr[10] = { 0 };
+ *&arr[0] = user_input();
+ sink(arr[0]); // $ast,ir
+ sink(arr[1]); // $f+:ast
+}
+
+struct inner {
+ void *data;
+ int unrelated;
+};
+
+struct middle {
+ inner arr[10];
+ inner *ptr;
+};
+
+struct outer {
+ middle nested;
+ middle *indirect;
+};
+
+void nested_array_1(outer o) {
+ o.nested.arr[1].data = user_input();
+ sink(o.nested.arr[1].data); // $ast,ir
+ sink(o.nested.arr[0].data); // $f+:ast
+}
+
+void nested_array_2(outer o) {
+ o.indirect->arr[1].data = user_input();
+ sink(o.indirect->arr[1].data); // $ast $f-:ir
+ sink(o.indirect->arr[0].data); // $f+:ast
+}
+
+void nested_array_3(outer o) {
+ o.indirect->ptr[1].data = user_input();
+ sink(o.indirect->ptr[1].data); // $f-:ast,ir
+ sink(o.indirect->ptr[0].data);
+}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp b/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp
index 6a0d61f799a3..84c3b039cb90 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp
@@ -48,19 +48,19 @@ struct S {
void test_setDirectly() {
S s;
s.setDirectly(user_input());
- sink(s.getDirectly()); // $ast $f-:ir
+ sink(s.getDirectly()); // $ast $ir
}
void test_setIndirectly() {
S s;
s.setIndirectly(user_input());
- sink(s.getIndirectly()); // $ast $f-:ir
+ sink(s.getIndirectly()); // $ast $ir
}
void test_setThroughNonMember() {
S s;
s.setThroughNonMember(user_input());
- sink(s.getThroughNonMember()); // $ast $f-:ir
+ sink(s.getThroughNonMember()); // $ast $ir
}
void test_nonMemberSetA() {
@@ -109,11 +109,11 @@ void test_outer_with_ptr(Outer *pouter) {
sink(outer.inner_nested.a); // $ast,ir
sink(outer.inner_ptr->a); // $ast $f-:ir
- sink(outer.a); // $f-:ast $f-:ir
+ sink(outer.a); // $ast,ir
sink(pouter->inner_nested.a); // $ast,ir
sink(pouter->inner_ptr->a); // $ast $f-:ir
- sink(pouter->a); // $f-:ast $f-:ir
+ sink(pouter->a); // $ast,ir
}
void test_outer_with_ref(Outer *pouter) {
@@ -129,9 +129,9 @@ void test_outer_with_ref(Outer *pouter) {
sink(outer.inner_nested.a); // $ast,ir
sink(outer.inner_ptr->a); // $ast $f-:ir
- sink(outer.a); // $ast $f-:ir
+ sink(outer.a); // $ast,ir
sink(pouter->inner_nested.a); // $ast,ir
sink(pouter->inner_ptr->a); // $ast $f-:ir
- sink(pouter->a); // $ast $f-:ir
+ sink(pouter->a); // $ast,ir
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp
index bc7ac3f341f8..cb55bdd28637 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/complex.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/complex.cpp
@@ -39,17 +39,8 @@ 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 $f-:ir
- sink(b.inner.f.b()); // $f+:ast=62:19 $ast=63:19 $f+:ast=64:19 $ast=65:19 $f-:ir
+ sink(b.inner.f.a()); // $ast=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/constructors.cpp b/cpp/ql/test/library-tests/dataflow/fields/constructors.cpp
index 5179ea363957..4816180954ea 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/constructors.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/constructors.cpp
@@ -25,8 +25,8 @@ class Foo
void bar(Foo &f)
{
- sink(f.a()); //$ast=34:11 $ast=36:11 $f-:ir
- sink(f.b()); //$ast=35:14 $ast=36:25 $f-:ir
+ sink(f.a()); //$ast=34:11 $ast=36:11 $ir=34:11 $ir=36:11
+ sink(f.b()); //$ast=35:14 $ast=36:25 $ir=35:14 $ir=36:25
}
void foo()
diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected
index 04a03e5fb254..49b12c14350b 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected
@@ -1,16 +1,7 @@
uniqueEnclosingCallable
| C.cpp:37:24:37:33 | 0 | Node should have one enclosing callable but has 0. |
| C.cpp:37:24:37:33 | new | Node should have one enclosing callable but has 0. |
-uniqueTypeBound
-| complex.cpp:22:11:22:17 | constructor init of field f [post-this] | Node should have one type bound but has 0. |
-| complex.cpp:22:11:22:17 | constructor init of field f [pre-this] | Node should have one type bound but has 0. |
-| complex.cpp:25:7:25:7 | constructor init of field inner [post-this] | Node should have one type bound but has 0. |
-| complex.cpp:25:7:25:7 | constructor init of field inner [pre-this] | Node should have one type bound but has 0. |
-uniqueTypeRepr
-| complex.cpp:22:11:22:17 | constructor init of field f [post-this] | Node should have one type representation but has 0. |
-| complex.cpp:22:11:22:17 | constructor init of field f [pre-this] | Node should have one type representation but has 0. |
-| complex.cpp:25:7:25:7 | constructor init of field inner [post-this] | Node should have one type representation but has 0. |
-| complex.cpp:25:7:25:7 | constructor init of field inner [pre-this] | Node should have one type representation but has 0. |
+uniqueType
uniqueNodeLocation
missingLocation
uniqueNodeToString
@@ -25,7 +16,6 @@ postHasUniquePre
uniquePostUpdate
postIsInSameCallable
reverseRead
-storeIsPostUpdate
argHasPostUpdate
| A.cpp:41:15:41:21 | new | ArgumentNode is missing PostUpdateNode. |
| A.cpp:55:12:55:19 | new | ArgumentNode is missing PostUpdateNode. |
@@ -40,6 +30,120 @@ argHasPostUpdate
| D.cpp:43:24:43:40 | new | ArgumentNode is missing PostUpdateNode. |
| D.cpp:50:24:50:40 | new | ArgumentNode is missing PostUpdateNode. |
| D.cpp:57:25:57:41 | new | ArgumentNode is missing PostUpdateNode. |
+| arrays.cpp:7:8:7:13 | access to array | ArgumentNode is missing PostUpdateNode. |
+| arrays.cpp:8:8:8:13 | access to array | ArgumentNode is missing PostUpdateNode. |
+| arrays.cpp:9:8:9:11 | * ... | ArgumentNode is missing PostUpdateNode. |
+| arrays.cpp:10:8:10:15 | * ... | ArgumentNode is missing PostUpdateNode. |
+| arrays.cpp:16:8:16:13 | access to array | ArgumentNode is missing PostUpdateNode. |
+| arrays.cpp:17:8:17:13 | access to array | ArgumentNode is missing PostUpdateNode. |
| by_reference.cpp:51:8:51:8 | s | ArgumentNode is missing PostUpdateNode. |
| by_reference.cpp:57:8:57:8 | s | ArgumentNode is missing PostUpdateNode. |
| by_reference.cpp:63:8:63:8 | s | ArgumentNode is missing PostUpdateNode. |
+postWithInFlow
+| A.cpp:25:13:25:13 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:27:28:27:28 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:42:11:42:12 | cc [inner post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:43:11:43:12 | ct [inner post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:100:9:100:9 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:142:10:142:10 | c [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:143:13:143:13 | b [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:183:7:183:10 | head [post update] | PostUpdateNode should not be the target of local flow. |
+| A.cpp:184:13:184:16 | next [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:35:13:35:17 | elem1 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:36:13:36:17 | elem2 [post update] | PostUpdateNode should not be the target of local flow. |
+| B.cpp:46:13:46:16 | box1 [post update] | PostUpdateNode should not be the target of local flow. |
+| C.cpp:24:11:24:12 | s3 [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:9:21:9:24 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:11:29:11:32 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:16:21:16:23 | box [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:18:29:18:31 | box [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:30:13:30:16 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:44:19:44:22 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:5:57:12 | boxfield [post update] | PostUpdateNode should not be the target of local flow. |
+| D.cpp:58:20:58:23 | elem [post update] | PostUpdateNode should not be the target of local flow. |
+| E.cpp:33:19:33:19 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:9:6:9:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:13:5:13:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:17:5:17:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:25:18:25:19 | s1 [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:37:8:37:9 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:42:6:42:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:49:9:49:10 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:54:6:54:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:60:6:60:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:72:5:72:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:79:6:79:7 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:86:5:86:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:92:7:92:8 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:98:5:98:6 | m1 [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:106:3:106:5 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:106:4:106:5 | pa [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:111:18:111:19 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:126:15:126:16 | xs [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:136:16:136:17 | xs [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:147:16:147:16 | s [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:147:21:147:22 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:175:21:175:22 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:181:21:181:22 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:187:21:187:22 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:194:21:194:22 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:200:23:200:24 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:205:23:205:24 | m1 [inner post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:6:3:6:5 | arr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:6:3:6:8 | access to array [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:15:3:15:10 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:15:5:15:7 | arr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:36:12:36:14 | arr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:36:19:36:22 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:37:17:37:19 | arr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:38:17:38:19 | arr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:42:15:42:17 | arr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:42:22:42:25 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:43:20:43:22 | arr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:44:20:44:22 | arr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:48:15:48:17 | ptr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:48:22:48:25 | data [post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:49:20:49:22 | ptr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:50:20:50:22 | ptr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:12:8:12:8 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:16:11:16:11 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:68:18:68:18 | s [inner post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:84:10:84:10 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:88:9:88:9 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:92:3:92:5 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:92:4:92:5 | pa [inner post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:96:3:96:4 | pa [post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:102:28:102:39 | inner_nested [inner post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:104:22:104:22 | a [inner post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:106:30:106:41 | inner_nested [inner post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:108:24:108:24 | a [inner post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:123:28:123:36 | inner_ptr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:127:30:127:38 | inner_ptr [inner post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:11:22:11:23 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:12:22:12:23 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:20:24:20:25 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:21:24:21:25 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:9:36:9:36 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:12:56:12:56 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:13:57:13:57 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:22:23:22:23 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:37:26:37:33 | call to getInner [inner post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:42:13:42:20 | call to getInner [inner post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:42:25:42:25 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:47:7:47:11 | outer [inner post update] | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:47:27:47:27 | a [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:49:13:49:15 | bar [inner post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:49:20:49:22 | baz [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:53:13:53:15 | bar [inner post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:53:35:53:43 | bufferLen [post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:54:20:54:22 | bar [inner post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:60:16:60:18 | ref arg dst | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:61:25:61:27 | bar [inner post update] | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:65:25:65:27 | bar [inner post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:20:24:20:25 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:21:24:21:25 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:65:7:65:7 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:83:12:83:13 | f1 [post update] | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:92:7:92:7 | i [post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:24:11:24:12 | ab [inner post update] | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:36:17:36:24 | nestedAB [inner post update] | PostUpdateNode should not be the target of local flow. |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected
index ba7e3bc01257..ce9ed1850808 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected
@@ -1,14 +1,10 @@
uniqueEnclosingCallable
-uniqueTypeBound
-uniqueTypeRepr
+uniqueType
uniqueNodeLocation
-| D.cpp:1:17:1:17 | o | Node should have one location but has 3. |
-| by_reference.cpp:1:17:1:17 | o | Node should have one location but has 3. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
| file://:0:0:0:0 | p#0 | Node should have one location but has 0. |
-| qualifiers.cpp:1:17:1:17 | o | Node should have one location but has 3. |
missingLocation
| Nodes without location: 4 |
uniqueNodeToString
@@ -21,8 +17,134 @@ localCallNodes
postIsNotPre
postHasUniquePre
| simple.cpp:65:5:65:22 | Store | PostUpdateNode should have one pre-update node but has 0. |
+| simple.cpp:92:5:92:22 | Store | PostUpdateNode should have one pre-update node but has 0. |
uniquePostUpdate
postIsInSameCallable
reverseRead
-storeIsPostUpdate
argHasPostUpdate
+postWithInFlow
+| A.cpp:25:7:25:17 | Chi | PostUpdateNode should not be the target of local flow. |
+| A.cpp:27:22:27:32 | Chi | PostUpdateNode should not be the target of local flow. |
+| A.cpp:98:12:98:18 | Chi | PostUpdateNode should not be the target of local flow. |
+| A.cpp:100:5:100:13 | Chi | PostUpdateNode should not be the target of local flow. |
+| A.cpp:142:7:142:20 | Chi | PostUpdateNode should not be the target of local flow. |
+| A.cpp:143:7:143:31 | Chi | PostUpdateNode should not be the target of local flow. |
+| A.cpp:183:7:183:20 | Chi | PostUpdateNode should not be the target of local flow. |
+| A.cpp:184:7:184:23 | Chi | PostUpdateNode should not be the target of local flow. |
+| B.cpp:6:15:6:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| B.cpp:15:15:15:27 | Chi | PostUpdateNode should not be the target of local flow. |
+| B.cpp:35:7:35:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| B.cpp:36:7:36:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| B.cpp:46:7:46:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| C.cpp:22:12:22:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| C.cpp:22:12:22:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| C.cpp:24:5:24:25 | Chi | PostUpdateNode should not be the target of local flow. |
+| C.cpp:24:16:24:25 | Chi | PostUpdateNode should not be the target of local flow. |
+| D.cpp:9:21:9:28 | Chi | PostUpdateNode should not be the target of local flow. |
+| D.cpp:11:29:11:36 | Chi | PostUpdateNode should not be the target of local flow. |
+| D.cpp:16:21:16:27 | Chi | PostUpdateNode should not be the target of local flow. |
+| D.cpp:18:29:18:35 | Chi | PostUpdateNode should not be the target of local flow. |
+| D.cpp:28:15:28:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| D.cpp:35:15:35:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| D.cpp:42:15:42:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| D.cpp:49:15:49:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| D.cpp:56:15:56:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| D.cpp:57:5:57:42 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:9:3:9:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:13:3:13:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:17:3:17:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:21:12:21:12 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:21:15:21:15 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:22:12:22:12 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:22:15:22:15 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:23:12:23:12 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:23:15:23:15 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:35:12:35:12 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:35:15:35:15 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:37:3:37:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:40:12:40:12 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:40:15:40:15 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:42:3:42:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:47:12:47:12 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:47:15:47:15 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:49:3:49:25 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:52:12:52:12 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:52:15:52:15 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:54:3:54:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:59:12:59:12 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:59:15:59:15 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:60:3:60:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:70:19:70:19 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:70:22:70:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:72:3:72:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:77:19:77:19 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:77:22:77:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:79:3:79:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:84:19:84:19 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:84:22:84:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:86:3:86:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:91:19:91:19 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:91:22:91:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:92:3:92:23 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:98:3:98:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:106:3:106:20 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:111:15:111:19 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:147:15:147:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:175:15:175:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:181:15:181:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:187:15:187:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:194:15:194:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:200:15:200:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| aliasing.cpp:205:15:205:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:5:18:5:23 | Chi | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:5:21:5:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:6:3:6:23 | Chi | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:14:18:14:23 | Chi | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:14:21:14:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:15:3:15:25 | Chi | PostUpdateNode should not be the target of local flow. |
+| arrays.cpp:36:3:36:37 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:12:5:12:16 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:16:5:16:19 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:84:3:84:25 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:88:3:88:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:92:3:92:20 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:96:3:96:19 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:102:21:102:39 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:104:15:104:22 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:106:21:106:41 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:108:15:108:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:122:21:122:38 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:124:15:124:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:126:21:126:40 | Chi | PostUpdateNode should not be the target of local flow. |
+| by_reference.cpp:128:15:128:23 | Chi | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:11:22:11:27 | Chi | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:12:22:12:27 | Chi | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:14:26:14:26 | Chi | PostUpdateNode should not be the target of local flow. |
+| complex.cpp:14:33:14:33 | Chi | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:20:24:20:29 | Chi | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:21:24:21:29 | Chi | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:23:28:23:28 | Chi | PostUpdateNode should not be the target of local flow. |
+| constructors.cpp:23:35:23:35 | Chi | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:9:30:9:44 | Chi | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:12:49:12:64 | Chi | PostUpdateNode should not be the target of local flow. |
+| qualifiers.cpp:13:51:13:65 | Chi | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:39:12:39:95 | Chi | PostUpdateNode should not be the target of local flow. |
+| realistic.cpp:49:9:49:64 | Chi | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:20:24:20:29 | Chi | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:21:24:21:29 | Chi | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:23:28:23:28 | Chi | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:23:35:23:35 | Chi | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:65:5:65:22 | Store | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:83:9:83:28 | Chi | PostUpdateNode should not be the target of local flow. |
+| simple.cpp:92:5:92:22 | Store | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:20:20:20:29 | Chi | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:20:34:20:34 | Chi | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:27:7:27:16 | Chi | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:27:21:27:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:28:5:28:7 | Chi | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:36:10:36:24 | Chi | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:40:20:40:29 | Chi | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:40:34:40:34 | Chi | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:42:7:42:16 | Chi | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:42:21:42:21 | Chi | PostUpdateNode should not be the target of local flow. |
+| struct_init.c:43:5:43:7 | Chi | PostUpdateNode should not be the target of local flow. |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected
new file mode 100644
index 000000000000..242c09f3a3bd
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.expected
@@ -0,0 +1,45 @@
+| A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | & ... | AST only |
+| A.cpp:47:12:47:18 | new | A.cpp:49:13:49:13 | c | AST only |
+| A.cpp:64:21:64:28 | new | A.cpp:66:14:66:14 | c | AST only |
+| A.cpp:73:25:73:32 | new | A.cpp:75:14:75:14 | c | AST only |
+| A.cpp:98:12:98:18 | new | A.cpp:120:16:120:16 | a | AST only |
+| A.cpp:142:14:142:20 | new | A.cpp:153:16:153:16 | c | AST only |
+| A.cpp:159:12:159:18 | new | A.cpp:165:26:165:29 | head | AST only |
+| A.cpp:159:12:159:18 | new | A.cpp:169:15:169:18 | head | AST only |
+| B.cpp:6:15:6:24 | new | B.cpp:9:20:9:24 | elem1 | AST only |
+| B.cpp:15:15:15:27 | new | B.cpp:19:20:19:24 | elem2 | AST only |
+| D.cpp:28:15:28:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
+| D.cpp:35:15:35:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
+| D.cpp:42:15:42:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
+| D.cpp:49:15:49:24 | new | D.cpp:22:25:22:31 | call to getElem | AST only |
+| D.cpp:56:15:56:24 | new | D.cpp:64:25:64:28 | elem | AST only |
+| E.cpp:28:21:28:23 | ref arg raw | E.cpp:31:10:31:12 | raw | AST only |
+| E.cpp:29:24:29:29 | ref arg buffer | E.cpp:32:13:32:18 | buffer | AST only |
+| E.cpp:30:28:30:33 | ref arg buffer | E.cpp:21:18:21:23 | buffer | AST only |
+| aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 | IR only |
+| aliasing.cpp:42:11:42:20 | call to user_input | aliasing.cpp:43:13:43:14 | m1 | IR only |
+| aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | IR only |
+| aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | IR only |
+| aliasing.cpp:98:10:98:19 | call to user_input | aliasing.cpp:102:8:102:10 | * ... | IR only |
+| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:122:8:122:12 | access to array | IR only |
+| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:127:8:127:16 | * ... | IR only |
+| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:132:8:132:14 | * ... | IR only |
+| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:137:8:137:11 | * ... | IR only |
+| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:8:8:8:13 | access to array | AST only |
+| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:17:8:17:13 | access to array | AST only |
+| arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:38:24:38:27 | data | AST only |
+| arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:43:27:43:30 | data | AST only |
+| arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:44:27:44:30 | data | AST only |
+| by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:111:25:111:25 | a | AST only |
+| 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 |
+| 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 |
+| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:38:23:38:23 | a | AST only |
+| qualifiers.cpp:42:29:42:38 | call to user_input | qualifiers.cpp:43:23:43:23 | a | AST only |
+| qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:48:23:48:23 | a | AST only |
+| realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:61:47:61:55 | bufferLen | AST only |
+| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:33:25:33:25 | a | AST only |
+| struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/flow-diff.ql b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.ql
new file mode 100644
index 000000000000..47bee0db4923
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/fields/flow-diff.ql
@@ -0,0 +1,31 @@
+/**
+ * @kind problem
+ */
+
+import cpp
+import Nodes
+import IRConfiguration as IRConf
+import ASTConfiguration as ASTConf
+private import semmle.code.cpp.ir.dataflow.DataFlow as IR
+private import semmle.code.cpp.dataflow.DataFlow as AST
+
+from Node source, Node sink, IRConf::Conf irConf, ASTConf::Conf astConf, string msg
+where
+ irConf.hasFlow(source.asIR(), sink.asIR()) and
+ not exists(AST::DataFlow::Node astSource, AST::DataFlow::Node astSink |
+ astSource.asExpr() = source.asIR().asExpr() and
+ astSink.asExpr() = sink.asIR().asExpr()
+ |
+ astConf.hasFlow(astSource, astSink)
+ ) and
+ msg = "IR only"
+ or
+ astConf.hasFlow(source.asAST(), sink.asAST()) and
+ not exists(IR::DataFlow::Node irSource, IR::DataFlow::Node irSink |
+ irSource.asExpr() = source.asAST().asExpr() and
+ irSink.asExpr() = sink.asAST().asExpr()
+ |
+ irConf.hasFlow(irSource, irSink)
+ ) and
+ msg = "AST only"
+select source, sink, msg
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 69c088fb260d..61770c0aca35 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
@@ -1,18 +1,40 @@
edges
+| A.cpp:55:5:55:5 | set output argument [c] | A.cpp:56:13:56:15 | call to get |
+| A.cpp:55:12:55:19 | (C *)... | A.cpp:55:5:55:5 | set output argument [c] |
+| A.cpp:55:12:55:19 | new | A.cpp:55:5:55:5 | set output argument [c] |
+| A.cpp:57:11:57:24 | B output argument [c] | A.cpp:57:28:57:30 | call to get |
+| A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | B output argument [c] |
| A.cpp:98:12:98:18 | new | A.cpp:100:5:100:13 | Store |
-| A.cpp:100:5:100:13 | Chi [a] | A.cpp:101:8:101:9 | Argument 0 indirection [a] |
+| A.cpp:100:5:100:13 | Chi [a] | A.cpp:103:14:103:14 | *c [a] |
| A.cpp:100:5:100:13 | Store | A.cpp:100:5:100:13 | Chi [a] |
-| A.cpp:101:8:101:9 | Argument 0 indirection [a] | A.cpp:103:14:103:14 | *c [a] |
| A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a |
-| A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a |
-| A.cpp:107:16:107:16 | a | A.cpp:107:12:107:16 | (void *)... |
+| A.cpp:126:5:126:5 | Chi [c] | A.cpp:131:8:131:8 | f7 output argument [c] |
+| A.cpp:126:5:126:5 | set output argument [c] | A.cpp:126:5:126:5 | Chi [c] |
+| A.cpp:126:12:126:18 | new | A.cpp:126:5:126:5 | set output argument [c] |
+| A.cpp:131:8:131:8 | Chi [c] | A.cpp:132:13:132:13 | c |
+| A.cpp:131:8:131:8 | f7 output argument [c] | A.cpp:131:8:131:8 | Chi [c] |
| A.cpp:142:7:142:20 | Chi [c] | A.cpp:151:18:151:18 | D output argument [c] |
| A.cpp:142:7:142:20 | Store | A.cpp:142:7:142:20 | Chi [c] |
| A.cpp:142:14:142:20 | new | A.cpp:142:7:142:20 | Store |
-| A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c |
+| A.cpp:143:7:143:31 | Chi [b] | A.cpp:151:12:151:24 | D output argument [b] |
+| A.cpp:143:7:143:31 | Store | A.cpp:143:7:143:31 | Chi [b] |
+| A.cpp:143:25:143:31 | new | A.cpp:143:7:143:31 | Store |
+| A.cpp:150:12:150:18 | new | A.cpp:151:12:151:24 | D output argument [b] |
+| A.cpp:151:12:151:24 | Chi [b] | A.cpp:152:13:152:13 | b |
+| A.cpp:151:12:151:24 | D output argument [b] | A.cpp:151:12:151:24 | Chi [b] |
| A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c |
| A.cpp:151:18:151:18 | D output argument [c] | A.cpp:151:18:151:18 | Chi [c] |
-| A.cpp:154:13:154:13 | c | A.cpp:154:10:154:13 | (void *)... |
+| C.cpp:18:12:18:18 | C output argument [s1] | C.cpp:27:8:27:11 | *#this [s1] |
+| C.cpp:18:12:18:18 | C output argument [s3] | C.cpp:27:8:27:11 | *#this [s3] |
+| C.cpp:22:12:22:21 | Chi [s1] | C.cpp:24:5:24:25 | Chi [s1] |
+| C.cpp:22:12:22:21 | Store | C.cpp:22:12:22:21 | Chi [s1] |
+| C.cpp:22:12:22:21 | new | C.cpp:22:12:22:21 | Store |
+| C.cpp:24:5:24:25 | Chi [s1] | C.cpp:18:12:18:18 | C output argument [s1] |
+| C.cpp:24:5:24:25 | Chi [s3] | C.cpp:18:12:18:18 | C output argument [s3] |
+| C.cpp:24:5:24:25 | Store | C.cpp:24:5:24:25 | Chi [s3] |
+| C.cpp:24:16:24:25 | new | C.cpp:24:5:24:25 | Store |
+| C.cpp:27:8:27:11 | *#this [s1] | C.cpp:29:10:29:11 | s1 |
+| C.cpp:27:8:27:11 | *#this [s3] | C.cpp:31:10:31:11 | s3 |
| aliasing.cpp:9:3:9:22 | Chi [m1] | aliasing.cpp:25:17:25:19 | pointerSetter output argument [m1] |
| aliasing.cpp:9:3:9:22 | Store | aliasing.cpp:9:3:9:22 | Chi [m1] |
| aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:3:9:22 | Store |
@@ -32,9 +54,56 @@ edges
| aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 |
| aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 |
| aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 |
-| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] |
+| aliasing.cpp:98:3:98:21 | Chi [m1] | aliasing.cpp:100:14:100:14 | Store [m1] |
+| aliasing.cpp:98:3:98:21 | Store | aliasing.cpp:98:3:98:21 | Chi [m1] |
+| aliasing.cpp:98:10:98:19 | call to user_input | aliasing.cpp:98:3:98:21 | Store |
+| aliasing.cpp:100:14:100:14 | Store [m1] | aliasing.cpp:102:8:102:10 | * ... |
+| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [array content] |
+| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:126:15:126:20 | taint_a_ptr output argument [array content] |
+| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [array content] |
+| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [array content] |
+| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:158:15:158:20 | taint_a_ptr output argument [array content] |
+| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:164:15:164:20 | taint_a_ptr output argument [array content] |
+| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [array content] |
+| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [array content] |
+| aliasing.cpp:106:3:106:20 | Chi [array content] | aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [array content] |
+| aliasing.cpp:106:3:106:20 | Store | aliasing.cpp:106:3:106:20 | Chi [array content] |
+| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:106:3:106:20 | Store |
+| aliasing.cpp:121:15:121:16 | Chi [array content] | aliasing.cpp:122:8:122:12 | access to array |
+| aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [array content] | aliasing.cpp:121:15:121:16 | Chi [array content] |
+| aliasing.cpp:126:15:126:20 | Chi [array content] | aliasing.cpp:127:8:127:16 | * ... |
+| aliasing.cpp:126:15:126:20 | taint_a_ptr output argument [array content] | aliasing.cpp:126:15:126:20 | Chi [array content] |
+| aliasing.cpp:131:15:131:16 | Chi [array content] | aliasing.cpp:132:8:132:14 | * ... |
+| aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [array content] | aliasing.cpp:131:15:131:16 | Chi [array content] |
+| aliasing.cpp:136:15:136:17 | Chi [array content] | aliasing.cpp:137:8:137:11 | * ... |
+| aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [array content] | aliasing.cpp:136:15:136:17 | Chi [array content] |
+| aliasing.cpp:158:15:158:20 | Chi [array content] | aliasing.cpp:159:8:159:14 | * ... |
+| aliasing.cpp:158:15:158:20 | taint_a_ptr output argument [array content] | aliasing.cpp:158:15:158:20 | Chi [array content] |
+| aliasing.cpp:164:15:164:20 | Chi [array content] | aliasing.cpp:165:8:165:16 | access to array |
+| aliasing.cpp:164:15:164:20 | taint_a_ptr output argument [array content] | aliasing.cpp:164:15:164:20 | Chi [array content] |
+| aliasing.cpp:175:15:175:22 | Chi | aliasing.cpp:175:15:175:22 | Chi [m1] |
+| aliasing.cpp:175:15:175:22 | Chi [m1] | aliasing.cpp:176:13:176:14 | m1 |
+| aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [array content] | aliasing.cpp:175:15:175:22 | Chi |
+| aliasing.cpp:187:15:187:22 | Chi | aliasing.cpp:187:15:187:22 | Chi [m1] |
+| aliasing.cpp:187:15:187:22 | Chi [m1] | aliasing.cpp:188:13:188:14 | Store [m1] |
+| aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [array content] | aliasing.cpp:187:15:187:22 | Chi |
+| aliasing.cpp:188:13:188:14 | Store [m1] | aliasing.cpp:189:15:189:16 | m1 |
+| aliasing.cpp:200:15:200:24 | Chi | aliasing.cpp:200:15:200:24 | Chi [m1] |
+| aliasing.cpp:200:15:200:24 | Chi [m1] | aliasing.cpp:201:15:201:16 | m1 |
+| aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [array content] | aliasing.cpp:200:15:200:24 | Chi |
+| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array |
+| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... |
+| 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:36:26:36:35 | call to user_input | arrays.cpp:37:24:37:27 | data |
+| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | by_reference.cpp:51:10:51:20 | call to getDirectly |
+| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:3:50:3 | setDirectly output argument [a] |
+| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | by_reference.cpp:57:10:57:22 | call to getIndirectly |
+| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] |
+| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
+| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] |
+| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] |
-| by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
| by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] |
| by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] |
| by_reference.cpp:84:3:84:25 | Store | by_reference.cpp:84:3:84:25 | Chi [a] |
@@ -43,49 +112,137 @@ edges
| by_reference.cpp:88:3:88:24 | Chi [a] | by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] |
| by_reference.cpp:88:3:88:24 | Store | by_reference.cpp:88:3:88:24 | Chi [a] |
| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:88:3:88:24 | Store |
+| by_reference.cpp:92:3:92:20 | Chi [array content] | by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [array content] |
+| by_reference.cpp:92:3:92:20 | Chi [array content] | by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [array content] |
+| by_reference.cpp:92:3:92:20 | Store | by_reference.cpp:92:3:92:20 | Chi [array content] |
+| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:92:3:92:20 | Store |
+| by_reference.cpp:96:3:96:19 | Chi [array content] | by_reference.cpp:124:15:124:21 | taint_a_ref output argument [array content] |
+| by_reference.cpp:96:3:96:19 | Chi [array content] | by_reference.cpp:128:15:128:23 | taint_a_ref output argument [array content] |
+| by_reference.cpp:96:3:96:19 | Store | by_reference.cpp:96:3:96:19 | Chi [array content] |
+| by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:96:3:96:19 | Store |
| by_reference.cpp:102:21:102:39 | Chi [a] | by_reference.cpp:110:27:110:27 | a |
| by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | by_reference.cpp:102:21:102:39 | Chi [a] |
+| by_reference.cpp:104:15:104:22 | Chi | by_reference.cpp:104:15:104:22 | Chi [a] |
+| by_reference.cpp:104:15:104:22 | Chi [a] | by_reference.cpp:112:14:112:14 | a |
+| by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [array content] | by_reference.cpp:104:15:104:22 | Chi |
| by_reference.cpp:106:21:106:41 | Chi [a] | by_reference.cpp:114:29:114:29 | a |
| by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | by_reference.cpp:106:21:106:41 | Chi [a] |
+| by_reference.cpp:108:15:108:24 | Chi | by_reference.cpp:108:15:108:24 | Chi [a] |
+| by_reference.cpp:108:15:108:24 | Chi [a] | by_reference.cpp:116:16:116:16 | a |
+| by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [array content] | by_reference.cpp:108:15:108:24 | Chi |
| by_reference.cpp:122:21:122:38 | Chi [a] | by_reference.cpp:130:27:130:27 | a |
| by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | by_reference.cpp:122:21:122:38 | Chi [a] |
+| by_reference.cpp:124:15:124:21 | Chi | by_reference.cpp:124:15:124:21 | Chi [a] |
+| by_reference.cpp:124:15:124:21 | Chi [a] | by_reference.cpp:132:14:132:14 | a |
+| by_reference.cpp:124:15:124:21 | taint_a_ref output argument [array content] | by_reference.cpp:124:15:124:21 | Chi |
| by_reference.cpp:126:21:126:40 | Chi [a] | by_reference.cpp:134:29:134:29 | a |
| by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | by_reference.cpp:126:21:126:40 | Chi [a] |
+| 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: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 |
+| constructors.cpp:28:10:28:10 | a output argument [b_] | constructors.cpp:29:12:29:12 | call to b |
+| constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:34:11:34:26 | Foo output argument [a_] |
+| constructors.cpp:34:11:34:26 | Foo output argument [a_] | constructors.cpp:26:15:26:15 | *f [a_] |
+| constructors.cpp:35:11:35:26 | Foo output argument [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
+| constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:35:11:35:26 | Foo output argument [b_] |
+| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [a_] |
+| constructors.cpp:36:11:36:37 | Foo output argument [a_] | constructors.cpp:26:15:26:15 | *f [a_] |
+| constructors.cpp:36:11:36:37 | Foo output argument [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
+| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [b_] |
+| simple.cpp:26:15:26:15 | *f [a_] | simple.cpp:28:12:28:12 | call to a |
+| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:28:10:28:10 | a output argument [b_] |
+| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:29:12:29:12 | call to b |
+| simple.cpp:28:10:28:10 | a output argument [b_] | simple.cpp:29:12:29:12 | call to b |
+| simple.cpp:39:5:39:5 | setA output argument [a_] | simple.cpp:26:15:26:15 | *f [a_] |
+| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:39:5:39:5 | setA output argument [a_] |
+| simple.cpp:40:5:40:5 | setB output argument [b_] | simple.cpp:26:15:26:15 | *f [b_] |
+| simple.cpp:40:12:40:21 | call to user_input | simple.cpp:40:5:40:5 | setB output argument [b_] |
+| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:26:15:26:15 | *f [a_] |
+| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:42:5:42:5 | setB output argument [a_] |
+| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:41:5:41:5 | setA output argument [a_] |
+| simple.cpp:42:5:42:5 | setB output argument [a_] | simple.cpp:26:15:26:15 | *f [a_] |
+| simple.cpp:42:5:42:5 | setB output argument [b_] | simple.cpp:26:15:26:15 | *f [b_] |
+| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:42:5:42:5 | setB output argument [b_] |
| simple.cpp:65:5:65:22 | Store [i] | simple.cpp:66:12:66:12 | Store [i] |
| simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | Store [i] |
| simple.cpp:66:12:66:12 | Store [i] | simple.cpp:67:13:67:13 | i |
-| simple.cpp:83:9:83:28 | Chi [f1] | simple.cpp:84:14:84:20 | Argument -1 indirection [f1] |
+| simple.cpp:83:9:83:28 | Chi [f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
| simple.cpp:83:9:83:28 | Store | simple.cpp:83:9:83:28 | Chi [f1] |
| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | Store |
-| simple.cpp:84:14:84:20 | Argument -1 indirection [f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
+| simple.cpp:92:5:92:22 | Store [i] | simple.cpp:93:20:93:20 | Store [i] |
+| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | Store [i] |
+| simple.cpp:93:20:93:20 | Store [i] | simple.cpp:94:13:94:13 | i |
| struct_init.c:14:24:14:25 | *ab [a] | struct_init.c:15:12:15:12 | a |
-| struct_init.c:20:20:20:29 | Chi [a] | struct_init.c:24:10:24:12 | Argument 0 indirection [a] |
+| struct_init.c:20:20:20:29 | Chi [a] | struct_init.c:14:24:14:25 | *ab [a] |
| struct_init.c:20:20:20:29 | Store | struct_init.c:20:20:20:29 | Chi [a] |
| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:20:20:29 | Store |
| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a |
-| struct_init.c:24:10:24:12 | Argument 0 indirection [a] | struct_init.c:14:24:14:25 | *ab [a] |
-| struct_init.c:27:7:27:16 | Chi [a] | struct_init.c:36:10:36:24 | Argument 0 indirection [a] |
+| struct_init.c:27:7:27:16 | Chi [a] | struct_init.c:14:24:14:25 | *ab [a] |
| struct_init.c:27:7:27:16 | Store | struct_init.c:27:7:27:16 | Chi [a] |
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:7:27:16 | Store |
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:31:23:31:23 | a |
-| struct_init.c:36:10:36:24 | Argument 0 indirection [a] | struct_init.c:14:24:14:25 | *ab [a] |
nodes
+| A.cpp:55:5:55:5 | set output argument [c] | semmle.label | set output argument [c] |
+| A.cpp:55:12:55:19 | (C *)... | semmle.label | (C *)... |
+| A.cpp:55:12:55:19 | new | semmle.label | new |
+| A.cpp:56:13:56:15 | call to get | semmle.label | call to get |
+| A.cpp:57:11:57:24 | B output argument [c] | semmle.label | B output argument [c] |
+| A.cpp:57:17:57:23 | new | semmle.label | new |
+| A.cpp:57:28:57:30 | call to get | semmle.label | call to get |
| A.cpp:98:12:98:18 | new | semmle.label | new |
| A.cpp:100:5:100:13 | Chi [a] | semmle.label | Chi [a] |
| A.cpp:100:5:100:13 | Store | semmle.label | Store |
-| A.cpp:101:8:101:9 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] |
| A.cpp:103:14:103:14 | *c [a] | semmle.label | *c [a] |
-| A.cpp:107:12:107:16 | (void *)... | semmle.label | (void *)... |
-| A.cpp:107:16:107:16 | a | semmle.label | a |
| A.cpp:107:16:107:16 | a | semmle.label | a |
+| A.cpp:126:5:126:5 | Chi [c] | semmle.label | Chi [c] |
+| A.cpp:126:5:126:5 | set output argument [c] | semmle.label | set output argument [c] |
+| A.cpp:126:12:126:18 | new | semmle.label | new |
+| A.cpp:131:8:131:8 | Chi [c] | semmle.label | Chi [c] |
+| A.cpp:131:8:131:8 | f7 output argument [c] | semmle.label | f7 output argument [c] |
+| A.cpp:132:13:132:13 | c | semmle.label | c |
| A.cpp:142:7:142:20 | Chi [c] | semmle.label | Chi [c] |
| A.cpp:142:7:142:20 | Store | semmle.label | Store |
| A.cpp:142:14:142:20 | new | semmle.label | new |
+| A.cpp:143:7:143:31 | Chi [b] | semmle.label | Chi [b] |
+| A.cpp:143:7:143:31 | Store | semmle.label | Store |
+| A.cpp:143:25:143:31 | new | semmle.label | new |
+| A.cpp:150:12:150:18 | new | semmle.label | new |
+| A.cpp:151:12:151:24 | Chi [b] | semmle.label | Chi [b] |
+| A.cpp:151:12:151:24 | D output argument [b] | semmle.label | D output argument [b] |
| A.cpp:151:18:151:18 | Chi [c] | semmle.label | Chi [c] |
| A.cpp:151:18:151:18 | D output argument [c] | semmle.label | D output argument [c] |
-| A.cpp:154:10:154:13 | (void *)... | semmle.label | (void *)... |
-| A.cpp:154:13:154:13 | c | semmle.label | c |
+| A.cpp:152:13:152:13 | b | semmle.label | b |
| A.cpp:154:13:154:13 | c | semmle.label | c |
+| C.cpp:18:12:18:18 | C output argument [s1] | semmle.label | C output argument [s1] |
+| C.cpp:18:12:18:18 | C output argument [s3] | semmle.label | C output argument [s3] |
+| C.cpp:22:12:22:21 | Chi [s1] | semmle.label | Chi [s1] |
+| C.cpp:22:12:22:21 | Store | semmle.label | Store |
+| C.cpp:22:12:22:21 | new | semmle.label | new |
+| C.cpp:24:5:24:25 | Chi [s1] | semmle.label | Chi [s1] |
+| C.cpp:24:5:24:25 | Chi [s3] | semmle.label | Chi [s3] |
+| C.cpp:24:5:24:25 | Store | semmle.label | Store |
+| C.cpp:24:16:24:25 | new | semmle.label | new |
+| C.cpp:27:8:27:11 | *#this [s1] | semmle.label | *#this [s1] |
+| C.cpp:27:8:27:11 | *#this [s3] | semmle.label | *#this [s3] |
+| C.cpp:29:10:29:11 | s1 | semmle.label | s1 |
+| C.cpp:31:10:31:11 | s3 | semmle.label | s3 |
| aliasing.cpp:9:3:9:22 | Chi [m1] | semmle.label | Chi [m1] |
| aliasing.cpp:9:3:9:22 | Store | semmle.label | Store |
| aliasing.cpp:9:11:9:20 | call to user_input | semmle.label | call to user_input |
@@ -113,28 +270,146 @@ nodes
| aliasing.cpp:87:12:87:13 | m1 | semmle.label | m1 |
| aliasing.cpp:92:12:92:21 | call to user_input | semmle.label | call to user_input |
| aliasing.cpp:93:12:93:13 | m1 | semmle.label | m1 |
+| aliasing.cpp:98:3:98:21 | Chi [m1] | semmle.label | Chi [m1] |
+| aliasing.cpp:98:3:98:21 | Store | semmle.label | Store |
+| aliasing.cpp:98:10:98:19 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:100:14:100:14 | Store [m1] | semmle.label | Store [m1] |
+| aliasing.cpp:102:8:102:10 | * ... | semmle.label | * ... |
+| aliasing.cpp:106:3:106:20 | Chi [array content] | semmle.label | Chi [array content] |
+| aliasing.cpp:106:3:106:20 | Store | semmle.label | Store |
+| aliasing.cpp:106:9:106:18 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:121:15:121:16 | Chi [array content] | semmle.label | Chi [array content] |
+| aliasing.cpp:121:15:121:16 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:122:8:122:12 | access to array | semmle.label | access to array |
+| aliasing.cpp:126:15:126:20 | Chi [array content] | semmle.label | Chi [array content] |
+| aliasing.cpp:126:15:126:20 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:127:8:127:16 | * ... | semmle.label | * ... |
+| aliasing.cpp:131:15:131:16 | Chi [array content] | semmle.label | Chi [array content] |
+| aliasing.cpp:131:15:131:16 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:132:8:132:14 | * ... | semmle.label | * ... |
+| aliasing.cpp:136:15:136:17 | Chi [array content] | semmle.label | Chi [array content] |
+| aliasing.cpp:136:15:136:17 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:137:8:137:11 | * ... | semmle.label | * ... |
+| aliasing.cpp:158:15:158:20 | Chi [array content] | semmle.label | Chi [array content] |
+| aliasing.cpp:158:15:158:20 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:159:8:159:14 | * ... | semmle.label | * ... |
+| aliasing.cpp:164:15:164:20 | Chi [array content] | semmle.label | Chi [array content] |
+| aliasing.cpp:164:15:164:20 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:165:8:165:16 | access to array | semmle.label | access to array |
+| aliasing.cpp:175:15:175:22 | Chi | semmle.label | Chi |
+| aliasing.cpp:175:15:175:22 | Chi [m1] | semmle.label | Chi [m1] |
+| aliasing.cpp:175:15:175:22 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:176:13:176:14 | m1 | semmle.label | m1 |
+| aliasing.cpp:187:15:187:22 | Chi | semmle.label | Chi |
+| aliasing.cpp:187:15:187:22 | Chi [m1] | semmle.label | Chi [m1] |
+| aliasing.cpp:187:15:187:22 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:188:13:188:14 | Store [m1] | semmle.label | Store [m1] |
+| aliasing.cpp:189:15:189:16 | m1 | semmle.label | m1 |
+| aliasing.cpp:200:15:200:24 | Chi | semmle.label | Chi |
+| aliasing.cpp:200:15:200:24 | Chi [m1] | semmle.label | Chi [m1] |
+| aliasing.cpp:200:15:200:24 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
+| aliasing.cpp:201:15:201:16 | m1 | semmle.label | m1 |
+| arrays.cpp:6:12:6:21 | call to user_input | semmle.label | call to user_input |
+| arrays.cpp:7:8:7:13 | access to array | semmle.label | access to array |
+| arrays.cpp:9:8:9:11 | * ... | semmle.label | * ... |
+| arrays.cpp:10:8:10:15 | * ... | semmle.label | * ... |
+| 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:36:26:36:35 | call to user_input | semmle.label | call to user_input |
+| arrays.cpp:37:24:37:27 | data | semmle.label | data |
+| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | semmle.label | setDirectly output argument [a] |
+| by_reference.cpp:50:17:50:26 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:51:10:51:20 | call to getDirectly | semmle.label | call to getDirectly |
+| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | semmle.label | setIndirectly output argument [a] |
+| by_reference.cpp:56:19:56:28 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:57:10:57:22 | call to getIndirectly | semmle.label | call to getIndirectly |
+| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | semmle.label | setThroughNonMember output argument [a] |
+| by_reference.cpp:62:25:62:34 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | semmle.label | call to getThroughNonMember |
| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | semmle.label | nonMemberSetA output argument [a] |
| by_reference.cpp:68:21:68:30 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | semmle.label | call to nonMemberGetA |
-| by_reference.cpp:69:22:69:23 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] |
| by_reference.cpp:84:3:84:25 | Chi [a] | semmle.label | Chi [a] |
| by_reference.cpp:84:3:84:25 | Store | semmle.label | Store |
| by_reference.cpp:84:14:84:23 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:88:3:88:24 | Chi [a] | semmle.label | Chi [a] |
| by_reference.cpp:88:3:88:24 | Store | semmle.label | Store |
| by_reference.cpp:88:13:88:22 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:92:3:92:20 | Chi [array content] | semmle.label | Chi [array content] |
+| by_reference.cpp:92:3:92:20 | Store | semmle.label | Store |
+| by_reference.cpp:92:9:92:18 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:96:3:96:19 | Chi [array content] | semmle.label | Chi [array content] |
+| by_reference.cpp:96:3:96:19 | Store | semmle.label | Store |
+| by_reference.cpp:96:8:96:17 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:102:21:102:39 | Chi [a] | semmle.label | Chi [a] |
| by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] | semmle.label | taint_inner_a_ptr output argument [a] |
+| by_reference.cpp:104:15:104:22 | Chi | semmle.label | Chi |
+| by_reference.cpp:104:15:104:22 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:104:15:104:22 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
| by_reference.cpp:106:21:106:41 | Chi [a] | semmle.label | Chi [a] |
| by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] | semmle.label | taint_inner_a_ptr output argument [a] |
+| by_reference.cpp:108:15:108:24 | Chi | semmle.label | Chi |
+| by_reference.cpp:108:15:108:24 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:108:15:108:24 | taint_a_ptr output argument [array content] | semmle.label | taint_a_ptr output argument [array content] |
| by_reference.cpp:110:27:110:27 | a | semmle.label | a |
+| by_reference.cpp:112:14:112:14 | a | semmle.label | a |
| by_reference.cpp:114:29:114:29 | a | semmle.label | a |
+| by_reference.cpp:116:16:116:16 | a | semmle.label | a |
| by_reference.cpp:122:21:122:38 | Chi [a] | semmle.label | Chi [a] |
| by_reference.cpp:122:21:122:38 | taint_inner_a_ref output argument [a] | semmle.label | taint_inner_a_ref output argument [a] |
+| by_reference.cpp:124:15:124:21 | Chi | semmle.label | Chi |
+| by_reference.cpp:124:15:124:21 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:124:15:124:21 | taint_a_ref output argument [array content] | semmle.label | taint_a_ref output argument [array content] |
| by_reference.cpp:126:21:126:40 | Chi [a] | semmle.label | Chi [a] |
| by_reference.cpp:126:21:126:40 | taint_inner_a_ref output argument [a] | semmle.label | taint_inner_a_ref output argument [a] |
+| by_reference.cpp:128:15:128:23 | Chi | semmle.label | Chi |
+| by_reference.cpp:128:15:128:23 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:128:15:128:23 | taint_a_ref output argument [array content] | semmle.label | taint_a_ref output argument [array content] |
| by_reference.cpp:130:27:130:27 | a | semmle.label | a |
+| by_reference.cpp:132:14:132:14 | a | semmle.label | a |
| by_reference.cpp:134:29:134:29 | a | semmle.label | a |
+| 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: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_] |
+| constructors.cpp:28:12:28:12 | call to a | semmle.label | call to a |
+| constructors.cpp:29:12:29:12 | call to b | semmle.label | call to b |
+| constructors.cpp:34:11:34:20 | call to user_input | semmle.label | call to user_input |
+| constructors.cpp:34:11:34:26 | Foo output argument [a_] | semmle.label | Foo output argument [a_] |
+| constructors.cpp:35:11:35:26 | Foo output argument [b_] | semmle.label | Foo output argument [b_] |
+| constructors.cpp:35:14:35:23 | call to user_input | semmle.label | call to user_input |
+| constructors.cpp:36:11:36:20 | call to user_input | semmle.label | call to user_input |
+| constructors.cpp:36:11:36:37 | Foo output argument [a_] | semmle.label | Foo output argument [a_] |
+| constructors.cpp:36:11:36:37 | Foo output argument [b_] | semmle.label | Foo output argument [b_] |
+| constructors.cpp:36:25:36:34 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] |
+| simple.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] |
+| simple.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] |
+| simple.cpp:28:12:28:12 | call to a | semmle.label | call to a |
+| simple.cpp:29:12:29:12 | call to b | semmle.label | call to b |
+| simple.cpp:39:5:39:5 | setA output argument [a_] | semmle.label | setA output argument [a_] |
+| simple.cpp:39:12:39:21 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:40:5:40:5 | setB output argument [b_] | semmle.label | setB output argument [b_] |
+| simple.cpp:40:12:40:21 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:41:5:41:5 | setA output argument [a_] | semmle.label | setA output argument [a_] |
+| simple.cpp:41:12:41:21 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:42:5:42:5 | setB output argument [a_] | semmle.label | setB output argument [a_] |
+| simple.cpp:42:5:42:5 | setB output argument [b_] | semmle.label | setB output argument [b_] |
+| simple.cpp:42:12:42:21 | call to user_input | semmle.label | call to user_input |
| simple.cpp:65:5:65:22 | Store [i] | semmle.label | Store [i] |
| simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input |
| simple.cpp:66:12:66:12 | Store [i] | semmle.label | Store [i] |
@@ -142,25 +417,32 @@ nodes
| simple.cpp:83:9:83:28 | Chi [f1] | semmle.label | Chi [f1] |
| simple.cpp:83:9:83:28 | Store | semmle.label | Store |
| simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input |
-| simple.cpp:84:14:84:20 | Argument -1 indirection [f1] | semmle.label | Argument -1 indirection [f1] |
| simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 |
+| simple.cpp:92:5:92:22 | Store [i] | semmle.label | Store [i] |
+| simple.cpp:92:11:92:20 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:93:20:93:20 | Store [i] | semmle.label | Store [i] |
+| simple.cpp:94:13:94:13 | i | semmle.label | i |
| struct_init.c:14:24:14:25 | *ab [a] | semmle.label | *ab [a] |
| struct_init.c:15:12:15:12 | a | semmle.label | a |
| struct_init.c:20:20:20:29 | Chi [a] | semmle.label | Chi [a] |
| struct_init.c:20:20:20:29 | Store | semmle.label | Store |
| struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input |
| struct_init.c:22:11:22:11 | a | semmle.label | a |
-| struct_init.c:24:10:24:12 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] |
| struct_init.c:27:7:27:16 | Chi [a] | semmle.label | Chi [a] |
| struct_init.c:27:7:27:16 | Store | semmle.label | Store |
| struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input |
| struct_init.c:31:23:31:23 | a | semmle.label | a |
-| struct_init.c:36:10:36:24 | Argument 0 indirection [a] | semmle.label | Argument 0 indirection [a] |
#select
-| A.cpp:107:12:107:16 | (void *)... | A.cpp:98:12:98:18 | new | A.cpp:107:12:107:16 | (void *)... | (void *)... flows from $@ | A.cpp:98:12:98:18 | new | new |
+| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... |
+| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | new | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new | new |
+| A.cpp:57:28:57:30 | call to get | A.cpp:57:17:57:23 | new | A.cpp:57:28:57:30 | call to get | call to get flows from $@ | A.cpp:57:17:57:23 | new | new |
| A.cpp:107:16:107:16 | a | A.cpp:98:12:98:18 | new | A.cpp:107:16:107:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new | new |
-| A.cpp:154:10:154:13 | (void *)... | A.cpp:142:14:142:20 | new | A.cpp:154:10:154:13 | (void *)... | (void *)... flows from $@ | A.cpp:142:14:142:20 | new | new |
+| A.cpp:132:13:132:13 | c | A.cpp:126:12:126:18 | new | A.cpp:132:13:132:13 | c | c flows from $@ | A.cpp:126:12:126:18 | new | new |
+| A.cpp:152:13:152:13 | b | A.cpp:143:25:143:31 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:143:25:143:31 | new | new |
+| A.cpp:152:13:152:13 | b | A.cpp:150:12:150:18 | new | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:150:12:150:18 | new | new |
| A.cpp:154:13:154:13 | c | A.cpp:142:14:142:20 | new | A.cpp:154:13:154:13 | c | c flows from $@ | A.cpp:142:14:142:20 | new | new |
+| C.cpp:29:10:29:11 | s1 | C.cpp:22:12:22:21 | new | C.cpp:29:10:29:11 | s1 | s1 flows from $@ | C.cpp:22:12:22:21 | new | new |
+| C.cpp:31:10:31:11 | s3 | C.cpp:24:16:24:25 | new | C.cpp:31:10:31:11 | s3 | s3 flows from $@ | C.cpp:24:16:24:25 | new | new |
| aliasing.cpp:29:11:29:12 | m1 | aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:29:11:29:12 | m1 | m1 flows from $@ | aliasing.cpp:9:11:9:20 | call to user_input | call to user_input |
| aliasing.cpp:30:11:30:12 | m1 | aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:30:11:30:12 | m1 | m1 flows from $@ | aliasing.cpp:13:10:13:19 | call to user_input | call to user_input |
| aliasing.cpp:38:11:38:12 | m1 | aliasing.cpp:37:13:37:22 | call to user_input | aliasing.cpp:38:11:38:12 | m1 | m1 flows from $@ | aliasing.cpp:37:13:37:22 | call to user_input | call to user_input |
@@ -169,13 +451,48 @@ nodes
| aliasing.cpp:80:12:80:13 | m1 | aliasing.cpp:79:11:79:20 | call to user_input | aliasing.cpp:80:12:80:13 | m1 | m1 flows from $@ | aliasing.cpp:79:11:79:20 | call to user_input | call to user_input |
| aliasing.cpp:87:12:87:13 | m1 | aliasing.cpp:86:10:86:19 | call to user_input | aliasing.cpp:87:12:87:13 | m1 | m1 flows from $@ | aliasing.cpp:86:10:86:19 | call to user_input | call to user_input |
| aliasing.cpp:93:12:93:13 | m1 | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | m1 flows from $@ | aliasing.cpp:92:12:92:21 | call to user_input | call to user_input |
+| aliasing.cpp:102:8:102:10 | * ... | aliasing.cpp:98:10:98:19 | call to user_input | aliasing.cpp:102:8:102:10 | * ... | * ... flows from $@ | aliasing.cpp:98:10:98:19 | call to user_input | call to user_input |
+| aliasing.cpp:122:8:122:12 | access to array | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:122:8:122:12 | access to array | access to array flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:127:8:127:16 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:127:8:127:16 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:132:8:132:14 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:132:8:132:14 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:137:8:137:11 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:137:8:137:11 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:159:8:159:14 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:159:8:159:14 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:165:8:165:16 | access to array | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:165:8:165:16 | access to array | access to array flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:176:13:176:14 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:176:13:176:14 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:189:15:189:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:189:15:189:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:201:15:201:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:201:15:201:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| arrays.cpp:7:8:7:13 | access to array | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array | access to array flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
+| arrays.cpp:9:8:9:11 | * ... | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... | * ... flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
+| arrays.cpp:10:8:10:15 | * ... | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... | * ... flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | 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:16:8:16:13 | access to array | access to array flows from $@ | arrays.cpp:15:14:15:23 | call to user_input | call to user_input |
+| arrays.cpp:37:24:37:27 | data | arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:37:24:37:27 | data | data flows from $@ | arrays.cpp:36:26:36:35 | call to user_input | call to user_input |
+| by_reference.cpp:51:10:51:20 | call to getDirectly | by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:51:10:51:20 | call to getDirectly | call to getDirectly flows from $@ | by_reference.cpp:50:17:50:26 | call to user_input | call to user_input |
+| by_reference.cpp:57:10:57:22 | call to getIndirectly | by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:57:10:57:22 | call to getIndirectly | call to getIndirectly flows from $@ | by_reference.cpp:56:19:56:28 | call to user_input | call to user_input |
+| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:63:10:63:28 | call to getThroughNonMember | call to getThroughNonMember flows from $@ | by_reference.cpp:62:25:62:34 | call to user_input | call to user_input |
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | call to nonMemberGetA flows from $@ | by_reference.cpp:68:21:68:30 | call to user_input | call to user_input |
| by_reference.cpp:110:27:110:27 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:110:27:110:27 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
+| by_reference.cpp:112:14:112:14 | a | by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:112:14:112:14 | a | a flows from $@ | by_reference.cpp:92:9:92:18 | call to user_input | call to user_input |
| by_reference.cpp:114:29:114:29 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:114:29:114:29 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
+| by_reference.cpp:116:16:116:16 | a | by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:116:16:116:16 | a | a flows from $@ | by_reference.cpp:92:9:92:18 | call to user_input | call to user_input |
| by_reference.cpp:130:27:130:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:130:27:130:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input |
+| by_reference.cpp: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: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 |
+| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:36:25:36:34 | call to user_input | call to user_input |
+| simple.cpp:28:12:28:12 | call to a | simple.cpp:39:12:39:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:39:12:39:21 | call to user_input | call to user_input |
+| simple.cpp:28:12:28:12 | call to a | simple.cpp:41:12:41:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:41:12:41:21 | call to user_input | call to user_input |
+| simple.cpp:29:12:29:12 | call to b | simple.cpp:40:12:40:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:40:12:40:21 | call to user_input | call to user_input |
+| simple.cpp:29:12:29:12 | call to b | simple.cpp:42:12:42:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:42:12:42:21 | call to user_input | call to user_input |
| simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input |
| simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input |
+| simple.cpp:94:13:94:13 | i | simple.cpp:92:11:92:20 | call to user_input | simple.cpp:94:13:94:13 | i | i flows from $@ | simple.cpp:92:11:92:20 | call to user_input | call to user_input |
| struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input |
| struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input |
| struct_init.c:22:11:22:11 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:22:11:22:11 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected
index 889f789da8d7..51b43e6be1c6 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
@@ -157,6 +157,70 @@
| aliasing.cpp:86:5:86:6 | m1 | AST only |
| aliasing.cpp:92:3:92:3 | w | AST only |
| aliasing.cpp:92:7:92:8 | m1 | AST only |
+| aliasing.cpp:98:5:98:6 | m1 | AST only |
+| aliasing.cpp:106:3:106:5 | * ... | AST only |
+| aliasing.cpp:111:15:111:19 | & ... | AST only |
+| aliasing.cpp:121:15:121:16 | xs | AST only |
+| aliasing.cpp:126:15:126:20 | ... - ... | AST only |
+| aliasing.cpp:131:15:131:16 | xs | AST only |
+| aliasing.cpp:136:15:136:17 | + ... | AST only |
+| aliasing.cpp:141:15:141:15 | s | AST only |
+| aliasing.cpp:141:17:141:20 | data | AST only |
+| aliasing.cpp:147:15:147:22 | & ... | AST only |
+| aliasing.cpp:158:15:158:15 | s | AST only |
+| aliasing.cpp:158:17:158:20 | data | AST only |
+| aliasing.cpp:164:15:164:15 | s | AST only |
+| aliasing.cpp:164:17:164:20 | data | AST only |
+| aliasing.cpp:175:15:175:22 | & ... | AST only |
+| aliasing.cpp:175:16:175:17 | s2 | AST only |
+| aliasing.cpp:181:15:181:22 | & ... | AST only |
+| aliasing.cpp:181:16:181:17 | s2 | AST only |
+| aliasing.cpp:187:15:187:22 | & ... | AST only |
+| aliasing.cpp:187:16:187:17 | s2 | AST only |
+| aliasing.cpp:194:15:194:22 | & ... | AST only |
+| aliasing.cpp:194:16:194:17 | s2 | AST only |
+| aliasing.cpp:200:15:200:24 | & ... | AST only |
+| aliasing.cpp:200:16:200:18 | ps2 | AST only |
+| aliasing.cpp:205:15:205:24 | & ... | AST only |
+| aliasing.cpp:205:16:205:18 | ps2 | AST only |
+| arrays.cpp:6:3:6:8 | access to array | AST only |
+| arrays.cpp:6:3:6:23 | arr | IR only |
+| arrays.cpp:15:3:15:10 | * ... | AST only |
+| arrays.cpp:36:3:36:3 | o | AST only |
+| arrays.cpp:36:5:36:10 | nested | AST only |
+| arrays.cpp:36:19:36:22 | data | AST only |
+| arrays.cpp:37:8:37:8 | o | AST only |
+| arrays.cpp:37:8:37:22 | access to array | AST only |
+| arrays.cpp:37:10:37:15 | nested | AST only |
+| arrays.cpp:37:24:37:27 | data | AST only |
+| arrays.cpp:38:8:38:8 | o | AST only |
+| arrays.cpp:38:8:38:22 | access to array | AST only |
+| arrays.cpp:38:10:38:15 | nested | AST only |
+| arrays.cpp:38:24:38:27 | data | AST only |
+| arrays.cpp:42:3:42:3 | o | AST only |
+| arrays.cpp:42:3:42:20 | access to array | AST only |
+| arrays.cpp:42:5:42:12 | indirect | AST only |
+| arrays.cpp:42:22:42:25 | data | AST only |
+| arrays.cpp:43:8:43:8 | o | AST only |
+| arrays.cpp:43:8:43:25 | access to array | AST only |
+| arrays.cpp:43:10:43:17 | indirect | AST only |
+| arrays.cpp:43:27:43:30 | data | AST only |
+| arrays.cpp:44:8:44:8 | o | AST only |
+| arrays.cpp:44:8:44:25 | access to array | AST only |
+| arrays.cpp:44:10:44:17 | indirect | AST only |
+| arrays.cpp:44:27:44:30 | data | AST only |
+| arrays.cpp:48:3:48:3 | o | AST only |
+| arrays.cpp:48:3:48:20 | access to array | AST only |
+| arrays.cpp:48:5:48:12 | indirect | AST only |
+| arrays.cpp:48:22:48:25 | data | AST only |
+| arrays.cpp:49:8:49:8 | o | AST only |
+| arrays.cpp:49:8:49:25 | access to array | AST only |
+| arrays.cpp:49:10:49:17 | indirect | AST only |
+| arrays.cpp:49:27:49:30 | data | AST only |
+| arrays.cpp:50:8:50:8 | o | AST only |
+| arrays.cpp:50:8:50:25 | access to array | AST only |
+| arrays.cpp:50:10:50:17 | indirect | AST only |
+| arrays.cpp:50:27:50:30 | data | AST only |
| by_reference.cpp:12:8:12:8 | a | AST only |
| by_reference.cpp:16:11:16:11 | a | AST only |
| by_reference.cpp:20:5:20:8 | this | AST only |
@@ -177,18 +241,16 @@
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | AST only |
| by_reference.cpp:84:10:84:10 | a | AST only |
| by_reference.cpp:88:9:88:9 | a | AST only |
+| by_reference.cpp:92:3:92:5 | * ... | AST only |
+| by_reference.cpp:96:3:96:4 | pa | AST only |
| by_reference.cpp:102:21:102:39 | & ... | AST only |
-| by_reference.cpp:102:22:102:26 | outer | AST only |
| by_reference.cpp:103:21:103:25 | outer | AST only |
| by_reference.cpp:103:27:103:35 | inner_ptr | AST only |
| by_reference.cpp:104:15:104:22 | & ... | AST only |
-| by_reference.cpp:104:16:104:20 | outer | AST only |
| by_reference.cpp:106:21:106:41 | & ... | AST only |
-| by_reference.cpp:106:22:106:27 | pouter | AST only |
| by_reference.cpp:107:21:107:26 | pouter | AST only |
| by_reference.cpp:107:29:107:37 | inner_ptr | AST only |
| by_reference.cpp:108:15:108:24 | & ... | AST only |
-| by_reference.cpp:108:16:108:21 | pouter | AST only |
| by_reference.cpp:110:8:110:12 | outer | AST only |
| by_reference.cpp:110:14:110:25 | inner_nested | AST only |
| by_reference.cpp:110:27:110:27 | a | AST only |
@@ -205,17 +267,13 @@
| by_reference.cpp:115:27:115:27 | a | AST only |
| by_reference.cpp:116:8:116:13 | pouter | AST only |
| by_reference.cpp:116:16:116:16 | a | AST only |
-| by_reference.cpp:122:21:122:25 | outer | AST only |
| by_reference.cpp:122:27:122:38 | inner_nested | AST only |
| by_reference.cpp:123:21:123:36 | * ... | AST only |
| by_reference.cpp:123:22:123:26 | outer | AST only |
-| by_reference.cpp:124:15:124:19 | outer | AST only |
| by_reference.cpp:124:21:124:21 | a | AST only |
-| by_reference.cpp:126:21:126:26 | pouter | AST only |
| by_reference.cpp:126:29:126:40 | inner_nested | AST only |
| by_reference.cpp:127:21:127:38 | * ... | AST only |
| by_reference.cpp:127:22:127:27 | pouter | AST only |
-| by_reference.cpp:128:15:128:20 | pouter | AST only |
| by_reference.cpp:128:23:128:23 | a | AST only |
| by_reference.cpp:130:8:130:12 | outer | AST only |
| by_reference.cpp:130:14:130:25 | inner_nested | AST only |
@@ -235,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 |
@@ -304,6 +362,32 @@
| qualifiers.cpp:48:10:48:14 | outer | AST only |
| qualifiers.cpp:48:16:48:20 | inner | AST only |
| qualifiers.cpp:48:23:48:23 | a | AST only |
+| realistic.cpp:26:5:26:10 | offset | AST only |
+| realistic.cpp:42:20:42:20 | o | AST only |
+| realistic.cpp:49:9:49:11 | foo | AST only |
+| realistic.cpp:49:20:49:22 | baz | AST only |
+| realistic.cpp:53:9:53:11 | foo | AST only |
+| realistic.cpp:53:9:53:18 | access to array | AST only |
+| realistic.cpp:53:20:53:22 | baz | AST only |
+| realistic.cpp:53:25:53:33 | userInput | AST only |
+| realistic.cpp:53:35:53:43 | bufferLen | AST only |
+| realistic.cpp:54:16:54:18 | foo | AST only |
+| realistic.cpp:54:16:54:25 | access to array | AST only |
+| realistic.cpp:54:27:54:29 | baz | AST only |
+| realistic.cpp:54:32:54:40 | userInput | AST only |
+| realistic.cpp:54:42:54:47 | buffer | AST only |
+| realistic.cpp:60:16:60:18 | dst | AST only |
+| realistic.cpp:61:21:61:23 | foo | AST only |
+| realistic.cpp:61:21:61:30 | access to array | AST only |
+| realistic.cpp:61:32:61:34 | baz | AST only |
+| realistic.cpp:61:37:61:45 | userInput | AST only |
+| realistic.cpp:61:47:61:55 | bufferLen | AST only |
+| realistic.cpp:65:21:65:23 | foo | AST only |
+| realistic.cpp:65:21:65:30 | access to array | AST only |
+| realistic.cpp:65:32:65:34 | baz | AST only |
+| realistic.cpp:65:37:65:45 | userInput | AST only |
+| realistic.cpp:65:47:65:52 | buffer | AST only |
+| realistic.cpp:66:21:66:23 | dst | AST only |
| simple.cpp:20:24:20:25 | a_ | AST only |
| simple.cpp:21:24:21:25 | b_ | AST only |
| simple.cpp:28:10:28:10 | f | AST only |
@@ -320,6 +404,7 @@
| simple.cpp:83:9:83:10 | this | AST only |
| simple.cpp:83:12:83:13 | f1 | AST only |
| simple.cpp:84:14:84:20 | this | AST only |
+| simple.cpp:92:7:92:7 | i | AST only |
| struct_init.c:15:8:15:9 | ab | AST only |
| struct_init.c:15:12:15:12 | a | AST only |
| struct_init.c:16:8:16:9 | ab | AST only |
@@ -342,6 +427,5 @@
| struct_init.c:34:14:34:22 | pointerAB | AST only |
| struct_init.c:34:25:34:25 | b | AST only |
| struct_init.c:36:10:36:24 | & ... | AST only |
-| struct_init.c:36:11:36:15 | outer | AST only |
| struct_init.c:46:10:46:14 | outer | AST only |
| struct_init.c:46:16:46:24 | pointerAB | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql
index 8f6296290e9c..d8b6b4e0e69a 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql
+++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql
@@ -5,43 +5,18 @@
import cpp
import semmle.code.cpp.ir.dataflow.DataFlow::DataFlow as IR
import semmle.code.cpp.dataflow.DataFlow::DataFlow as AST
+import Nodes
-newtype TNode =
- TASTNode(AST::Node n) or
- TIRNode(IR::Node n)
-
-class Node extends TNode {
- string toString() { none() }
-
- IR::Node asIR() { none() }
-
- AST::Node asAST() { none() }
-
- Location getLocation() { none() }
-}
-
-class ASTNode extends Node, TASTNode {
- AST::Node n;
-
- ASTNode() { this = TASTNode(n) }
+class ASTPartialDefNode extends ASTNode {
+ ASTPartialDefNode() { exists(n.asPartialDefinition()) }
override string toString() { result = n.asPartialDefinition().toString() }
-
- override AST::Node asAST() { result = n }
-
- override Location getLocation() { result = n.getLocation() }
}
-class IRNode extends Node, TIRNode {
- IR::Node n;
-
- IRNode() { this = TIRNode(n) }
+class IRPartialDefNode extends IRNode {
+ IRPartialDefNode() { exists(n.asPartialDefinition()) }
override string toString() { result = n.asPartialDefinition().toString() }
-
- override IR::Node asIR() { result = n }
-
- override Location getLocation() { result = n.getLocation() }
}
from Node node, AST::Node astNode, IR::Node irNode, string msg
diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected
index 050f4bc47d55..a66dd2869551 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected
@@ -26,10 +26,29 @@
| aliasing.cpp:79:3:79:3 | s |
| aliasing.cpp:86:3:86:3 | s |
| aliasing.cpp:92:5:92:5 | s |
+| aliasing.cpp:98:3:98:3 | s |
+| aliasing.cpp:111:16:111:16 | s |
+| aliasing.cpp:147:16:147:19 | access to array |
+| aliasing.cpp:175:19:175:19 | s |
+| aliasing.cpp:181:19:181:19 | s |
+| aliasing.cpp:187:19:187:19 | s |
+| aliasing.cpp:194:19:194:19 | s |
+| aliasing.cpp:200:21:200:21 | s |
+| aliasing.cpp:205:21:205:21 | s |
+| arrays.cpp:6:3:6:5 | arr |
+| arrays.cpp:36:3:36:17 | access to array |
| by_reference.cpp:12:5:12:5 | s |
| by_reference.cpp:16:5:16:8 | this |
| by_reference.cpp:84:3:84:7 | inner |
| by_reference.cpp:88:3:88:7 | inner |
+| by_reference.cpp:102:22:102:26 | outer |
+| by_reference.cpp:104:16:104:20 | outer |
+| by_reference.cpp:106:22:106:27 | pouter |
+| by_reference.cpp:108:16:108:21 | pouter |
+| by_reference.cpp:122:21:122:25 | outer |
+| by_reference.cpp:124:15:124:19 | outer |
+| by_reference.cpp:126:21:126:26 | pouter |
+| by_reference.cpp:128:15:128:20 | pouter |
| complex.cpp:11:22:11:23 | this |
| complex.cpp:12:22:12:23 | this |
| constructors.cpp:20:24:20:25 | this |
@@ -37,7 +56,10 @@
| qualifiers.cpp:9:30:9:33 | this |
| qualifiers.cpp:12:49:12:53 | inner |
| qualifiers.cpp:13:51:13:55 | inner |
+| realistic.cpp:49:9:49:18 | access to array |
| simple.cpp:20:24:20:25 | this |
| simple.cpp:21:24:21:25 | this |
| simple.cpp:65:5:65:5 | a |
| simple.cpp:83:9:83:10 | f2 |
+| simple.cpp:92:5:92:5 | a |
+| struct_init.c:36:11:36:15 | outer |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected
index 3f5a2e497d85..6c58d51ff742 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected
@@ -185,6 +185,79 @@
| aliasing.cpp:92:3:92:3 | w |
| aliasing.cpp:92:5:92:5 | s |
| aliasing.cpp:92:7:92:8 | m1 |
+| aliasing.cpp:98:3:98:3 | s |
+| aliasing.cpp:98:5:98:6 | m1 |
+| aliasing.cpp:106:3:106:5 | * ... |
+| aliasing.cpp:111:15:111:19 | & ... |
+| aliasing.cpp:111:16:111:16 | s |
+| aliasing.cpp:121:15:121:16 | xs |
+| aliasing.cpp:126:15:126:20 | ... - ... |
+| aliasing.cpp:131:15:131:16 | xs |
+| aliasing.cpp:136:15:136:17 | + ... |
+| aliasing.cpp:141:15:141:15 | s |
+| aliasing.cpp:141:17:141:20 | data |
+| aliasing.cpp:147:15:147:22 | & ... |
+| aliasing.cpp:147:16:147:19 | access to array |
+| aliasing.cpp:158:15:158:15 | s |
+| aliasing.cpp:158:17:158:20 | data |
+| aliasing.cpp:164:15:164:15 | s |
+| aliasing.cpp:164:17:164:20 | data |
+| aliasing.cpp:175:15:175:22 | & ... |
+| aliasing.cpp:175:16:175:17 | s2 |
+| aliasing.cpp:175:19:175:19 | s |
+| aliasing.cpp:181:15:181:22 | & ... |
+| aliasing.cpp:181:16:181:17 | s2 |
+| aliasing.cpp:181:19:181:19 | s |
+| aliasing.cpp:187:15:187:22 | & ... |
+| aliasing.cpp:187:16:187:17 | s2 |
+| aliasing.cpp:187:19:187:19 | s |
+| aliasing.cpp:194:15:194:22 | & ... |
+| aliasing.cpp:194:16:194:17 | s2 |
+| aliasing.cpp:194:19:194:19 | s |
+| aliasing.cpp:200:15:200:24 | & ... |
+| aliasing.cpp:200:16:200:18 | ps2 |
+| aliasing.cpp:200:21:200:21 | s |
+| aliasing.cpp:205:15:205:24 | & ... |
+| aliasing.cpp:205:16:205:18 | ps2 |
+| aliasing.cpp:205:21:205:21 | s |
+| arrays.cpp:6:3:6:8 | access to array |
+| arrays.cpp:15:3:15:10 | * ... |
+| arrays.cpp:36:3:36:3 | o |
+| arrays.cpp:36:3:36:17 | access to array |
+| arrays.cpp:36:5:36:10 | nested |
+| arrays.cpp:36:19:36:22 | data |
+| arrays.cpp:37:8:37:8 | o |
+| arrays.cpp:37:8:37:22 | access to array |
+| arrays.cpp:37:10:37:15 | nested |
+| arrays.cpp:37:24:37:27 | data |
+| arrays.cpp:38:8:38:8 | o |
+| arrays.cpp:38:8:38:22 | access to array |
+| arrays.cpp:38:10:38:15 | nested |
+| arrays.cpp:38:24:38:27 | data |
+| arrays.cpp:42:3:42:3 | o |
+| arrays.cpp:42:3:42:20 | access to array |
+| arrays.cpp:42:5:42:12 | indirect |
+| arrays.cpp:42:22:42:25 | data |
+| arrays.cpp:43:8:43:8 | o |
+| arrays.cpp:43:8:43:25 | access to array |
+| arrays.cpp:43:10:43:17 | indirect |
+| arrays.cpp:43:27:43:30 | data |
+| arrays.cpp:44:8:44:8 | o |
+| arrays.cpp:44:8:44:25 | access to array |
+| arrays.cpp:44:10:44:17 | indirect |
+| arrays.cpp:44:27:44:30 | data |
+| arrays.cpp:48:3:48:3 | o |
+| arrays.cpp:48:3:48:20 | access to array |
+| arrays.cpp:48:5:48:12 | indirect |
+| arrays.cpp:48:22:48:25 | data |
+| arrays.cpp:49:8:49:8 | o |
+| arrays.cpp:49:8:49:25 | access to array |
+| arrays.cpp:49:10:49:17 | indirect |
+| arrays.cpp:49:27:49:30 | data |
+| arrays.cpp:50:8:50:8 | o |
+| arrays.cpp:50:8:50:25 | access to array |
+| arrays.cpp:50:10:50:17 | indirect |
+| arrays.cpp:50:27:50:30 | data |
| by_reference.cpp:12:5:12:5 | s |
| by_reference.cpp:12:8:12:8 | a |
| by_reference.cpp:16:5:16:8 | this |
@@ -209,6 +282,8 @@
| by_reference.cpp:84:10:84:10 | a |
| by_reference.cpp:88:3:88:7 | inner |
| by_reference.cpp:88:9:88:9 | a |
+| by_reference.cpp:92:3:92:5 | * ... |
+| by_reference.cpp:96:3:96:4 | pa |
| by_reference.cpp:102:21:102:39 | & ... |
| by_reference.cpp:102:22:102:26 | outer |
| by_reference.cpp:103:21:103:25 | outer |
@@ -269,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_ |
@@ -343,6 +418,33 @@
| qualifiers.cpp:48:10:48:14 | outer |
| qualifiers.cpp:48:16:48:20 | inner |
| qualifiers.cpp:48:23:48:23 | a |
+| realistic.cpp:26:5:26:10 | offset |
+| realistic.cpp:42:20:42:20 | o |
+| realistic.cpp:49:9:49:11 | foo |
+| realistic.cpp:49:9:49:18 | access to array |
+| realistic.cpp:49:20:49:22 | baz |
+| realistic.cpp:53:9:53:11 | foo |
+| realistic.cpp:53:9:53:18 | access to array |
+| realistic.cpp:53:20:53:22 | baz |
+| realistic.cpp:53:25:53:33 | userInput |
+| realistic.cpp:53:35:53:43 | bufferLen |
+| realistic.cpp:54:16:54:18 | foo |
+| realistic.cpp:54:16:54:25 | access to array |
+| realistic.cpp:54:27:54:29 | baz |
+| realistic.cpp:54:32:54:40 | userInput |
+| realistic.cpp:54:42:54:47 | buffer |
+| realistic.cpp:60:16:60:18 | dst |
+| realistic.cpp:61:21:61:23 | foo |
+| realistic.cpp:61:21:61:30 | access to array |
+| realistic.cpp:61:32:61:34 | baz |
+| realistic.cpp:61:37:61:45 | userInput |
+| realistic.cpp:61:47:61:55 | bufferLen |
+| realistic.cpp:65:21:65:23 | foo |
+| realistic.cpp:65:21:65:30 | access to array |
+| realistic.cpp:65:32:65:34 | baz |
+| realistic.cpp:65:37:65:45 | userInput |
+| realistic.cpp:65:47:65:52 | buffer |
+| realistic.cpp:66:21:66:23 | dst |
| simple.cpp:20:24:20:25 | a_ |
| simple.cpp:20:24:20:25 | this |
| simple.cpp:21:24:21:25 | b_ |
@@ -363,6 +465,8 @@
| simple.cpp:83:9:83:10 | this |
| simple.cpp:83:12:83:13 | f1 |
| simple.cpp:84:14:84:20 | this |
+| simple.cpp:92:5:92:5 | a |
+| simple.cpp:92:7:92:7 | i |
| struct_init.c:15:8:15:9 | ab |
| struct_init.c:15:12:15:12 | a |
| struct_init.c:16:8:16:9 | ab |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected
index d505ff5d87e9..b932e0395f49 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] |
@@ -155,6 +155,74 @@ edges
| aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:92:3:92:23 | ... = ... |
| aliasing.cpp:93:8:93:8 | w [s, m1] | aliasing.cpp:93:10:93:10 | s [m1] |
| aliasing.cpp:93:10:93:10 | s [m1] | aliasing.cpp:93:12:93:13 | m1 |
+| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:158:17:158:20 | ref arg data |
+| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:164:17:164:20 | ref arg data |
+| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:175:15:175:22 | ref arg & ... |
+| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:187:15:187:22 | ref arg & ... |
+| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:200:15:200:24 | ref arg & ... |
+| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:106:4:106:5 | pa [inner post update] |
+| aliasing.cpp:158:15:158:15 | s [post update] [data] | aliasing.cpp:159:9:159:9 | s [data] |
+| aliasing.cpp:158:17:158:20 | ref arg data | aliasing.cpp:158:15:158:15 | s [post update] [data] |
+| aliasing.cpp:159:9:159:9 | s [data] | aliasing.cpp:159:11:159:14 | data |
+| aliasing.cpp:159:11:159:14 | data | aliasing.cpp:159:8:159:14 | * ... |
+| aliasing.cpp:164:15:164:15 | s [post update] [data] | aliasing.cpp:165:8:165:8 | s [data] |
+| aliasing.cpp:164:17:164:20 | ref arg data | aliasing.cpp:164:15:164:15 | s [post update] [data] |
+| aliasing.cpp:165:8:165:8 | s [data] | aliasing.cpp:165:10:165:13 | data |
+| aliasing.cpp:165:10:165:13 | data | aliasing.cpp:165:8:165:16 | access to array |
+| aliasing.cpp:175:15:175:22 | ref arg & ... | aliasing.cpp:175:21:175:22 | m1 [inner post update] |
+| aliasing.cpp:175:16:175:17 | s2 [post update] [s, m1] | aliasing.cpp:176:8:176:9 | s2 [s, m1] |
+| aliasing.cpp:175:19:175:19 | s [post update] [m1] | aliasing.cpp:175:16:175:17 | s2 [post update] [s, m1] |
+| aliasing.cpp:175:21:175:22 | m1 [inner post update] | aliasing.cpp:175:19:175:19 | s [post update] [m1] |
+| aliasing.cpp:176:8:176:9 | s2 [s, m1] | aliasing.cpp:176:11:176:11 | s [m1] |
+| aliasing.cpp:176:11:176:11 | s [m1] | aliasing.cpp:176:13:176:14 | m1 |
+| aliasing.cpp:187:15:187:22 | ref arg & ... | aliasing.cpp:187:21:187:22 | m1 [inner post update] |
+| aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] | aliasing.cpp:189:8:189:11 | s2_2 [s, m1] |
+| aliasing.cpp:187:19:187:19 | s [post update] [m1] | aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] |
+| aliasing.cpp:187:21:187:22 | m1 [inner post update] | aliasing.cpp:187:19:187:19 | s [post update] [m1] |
+| aliasing.cpp:189:8:189:11 | s2_2 [s, m1] | aliasing.cpp:189:13:189:13 | s [m1] |
+| aliasing.cpp:189:13:189:13 | s [m1] | aliasing.cpp:189:15:189:16 | m1 |
+| aliasing.cpp:200:15:200:24 | ref arg & ... | aliasing.cpp:200:23:200:24 | m1 [inner post update] |
+| aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] | aliasing.cpp:201:8:201:10 | ps2 [s, m1] |
+| aliasing.cpp:200:21:200:21 | s [post update] [m1] | aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] |
+| aliasing.cpp:200:23:200:24 | m1 [inner post update] | aliasing.cpp:200:21:200:21 | s [post update] [m1] |
+| aliasing.cpp:201:8:201:10 | ps2 [s, m1] | aliasing.cpp:201:13:201:13 | s [m1] |
+| aliasing.cpp:201:13:201:13 | s [m1] | aliasing.cpp:201:15:201:16 | m1 |
+| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array |
+| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:8:8:8:13 | access to array |
+| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... |
+| 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, 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, 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, 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, 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, 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, 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, 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, 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] |
| by_reference.cpp:50:3:50:3 | ref arg s [a] | by_reference.cpp:51:8:51:8 | s [a] |
| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:3:50:3 | ref arg s [a] |
| by_reference.cpp:51:8:51:8 | s [a] | by_reference.cpp:51:10:51:20 | call to getDirectly |
@@ -184,6 +252,9 @@ edges
| by_reference.cpp:88:3:88:7 | inner [post update] [a] | by_reference.cpp:127:21:127:38 | ref arg * ... [a] |
| by_reference.cpp:88:3:88:24 | ... = ... | by_reference.cpp:88:3:88:7 | inner [post update] [a] |
| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:88:3:88:24 | ... = ... |
+| by_reference.cpp:92:4:92:5 | pa [inner post update] | by_reference.cpp:104:15:104:22 | ref arg & ... |
+| by_reference.cpp:92:4:92:5 | pa [inner post update] | by_reference.cpp:108:15:108:24 | ref arg & ... |
+| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:92:4:92:5 | pa [inner post update] |
| by_reference.cpp:95:25:95:26 | pa | by_reference.cpp:124:21:124:21 | ref arg a |
| by_reference.cpp:95:25:95:26 | pa | by_reference.cpp:128:23:128:23 | ref arg a |
| by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:95:25:95:26 | pa |
@@ -192,19 +263,27 @@ edges
| by_reference.cpp:102:28:102:39 | inner_nested [inner post update] [a] | by_reference.cpp:102:22:102:26 | outer [post update] [inner_nested, a] |
| by_reference.cpp:103:21:103:25 | outer [post update] [inner_ptr, a] | by_reference.cpp:111:8:111:12 | outer [inner_ptr, a] |
| by_reference.cpp:103:27:103:35 | ref arg inner_ptr [a] | by_reference.cpp:103:21:103:25 | outer [post update] [inner_ptr, a] |
+| by_reference.cpp:104:15:104:22 | ref arg & ... | by_reference.cpp:104:22:104:22 | a [inner post update] |
+| by_reference.cpp:104:16:104:20 | outer [post update] [a] | by_reference.cpp:112:8:112:12 | outer [a] |
+| by_reference.cpp:104:22:104:22 | a [inner post update] | by_reference.cpp:104:16:104:20 | outer [post update] [a] |
| by_reference.cpp:106:21:106:41 | ref arg & ... [a] | by_reference.cpp:106:30:106:41 | inner_nested [inner post update] [a] |
| by_reference.cpp:106:22:106:27 | pouter [post update] [inner_nested, a] | by_reference.cpp:114:8:114:13 | pouter [inner_nested, a] |
| by_reference.cpp:106:30:106:41 | inner_nested [inner post update] [a] | by_reference.cpp:106:22:106:27 | pouter [post update] [inner_nested, a] |
| by_reference.cpp:107:21:107:26 | pouter [post update] [inner_ptr, a] | by_reference.cpp:115:8:115:13 | pouter [inner_ptr, a] |
| by_reference.cpp:107:29:107:37 | ref arg inner_ptr [a] | by_reference.cpp:107:21:107:26 | pouter [post update] [inner_ptr, a] |
+| by_reference.cpp:108:15:108:24 | ref arg & ... | by_reference.cpp:108:24:108:24 | a [inner post update] |
+| by_reference.cpp:108:16:108:21 | pouter [post update] [a] | by_reference.cpp:116:8:116:13 | pouter [a] |
+| by_reference.cpp:108:24:108:24 | a [inner post update] | by_reference.cpp:108:16:108:21 | pouter [post update] [a] |
| by_reference.cpp:110:8:110:12 | outer [inner_nested, a] | by_reference.cpp:110:14:110:25 | inner_nested [a] |
| by_reference.cpp:110:14:110:25 | inner_nested [a] | by_reference.cpp:110:27:110:27 | a |
| by_reference.cpp:111:8:111:12 | outer [inner_ptr, a] | by_reference.cpp:111:14:111:22 | inner_ptr [a] |
| by_reference.cpp:111:14:111:22 | inner_ptr [a] | by_reference.cpp:111:25:111:25 | a |
+| by_reference.cpp:112:8:112:12 | outer [a] | by_reference.cpp:112:14:112:14 | a |
| by_reference.cpp:114:8:114:13 | pouter [inner_nested, a] | by_reference.cpp:114:16:114:27 | inner_nested [a] |
| by_reference.cpp:114:16:114:27 | inner_nested [a] | by_reference.cpp:114:29:114:29 | a |
| by_reference.cpp:115:8:115:13 | pouter [inner_ptr, a] | by_reference.cpp:115:16:115:24 | inner_ptr [a] |
| by_reference.cpp:115:16:115:24 | inner_ptr [a] | by_reference.cpp:115:27:115:27 | a |
+| by_reference.cpp:116:8:116:13 | pouter [a] | by_reference.cpp:116:16:116:16 | a |
| by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] | by_reference.cpp:130:8:130:12 | outer [inner_nested, a] |
| by_reference.cpp:122:27:122:38 | ref arg inner_nested [a] | by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] |
| by_reference.cpp:123:21:123:36 | ref arg * ... [a] | by_reference.cpp:123:28:123:36 | inner_ptr [inner post update] [a] |
@@ -229,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 |
@@ -307,6 +387,18 @@ 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, 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, 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, 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_] |
| simple.cpp:26:15:26:15 | f [b_] | simple.cpp:29:10:29:10 | f [b_] |
| simple.cpp:28:10:28:10 | f [a_] | simple.cpp:28:12:28:12 | call to a |
@@ -332,6 +424,10 @@ edges
| simple.cpp:83:9:83:28 | ... = ... | simple.cpp:83:9:83:10 | f2 [post update] [f1] |
| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | ... = ... |
| simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
+| simple.cpp:92:5:92:5 | a [post update] [i] | simple.cpp:94:10:94:11 | a2 [i] |
+| simple.cpp:92:5:92:22 | ... = ... | simple.cpp:92:5:92:5 | a [post update] [i] |
+| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | ... = ... |
+| simple.cpp:94:10:94:11 | a2 [i] | simple.cpp:94:13:94:13 | i |
| struct_init.c:14:24:14:25 | ab [a] | struct_init.c:15:8:15:9 | ab [a] |
| struct_init.c:15:8:15:9 | ab [a] | struct_init.c:15:12:15:12 | a |
| struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:22:8:22:9 | ab [a] |
@@ -422,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] |
@@ -491,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] |
@@ -539,6 +635,79 @@ nodes
| aliasing.cpp:93:8:93:8 | w [s, m1] | semmle.label | w [s, m1] |
| aliasing.cpp:93:10:93:10 | s [m1] | semmle.label | s [m1] |
| aliasing.cpp:93:12:93:13 | m1 | semmle.label | m1 |
+| aliasing.cpp:106:4:106:5 | pa [inner post update] | semmle.label | pa [inner post update] |
+| aliasing.cpp:106:9:106:18 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:158:15:158:15 | s [post update] [data] | semmle.label | s [post update] [data] |
+| aliasing.cpp:158:17:158:20 | ref arg data | semmle.label | ref arg data |
+| aliasing.cpp:159:8:159:14 | * ... | semmle.label | * ... |
+| aliasing.cpp:159:9:159:9 | s [data] | semmle.label | s [data] |
+| aliasing.cpp:159:11:159:14 | data | semmle.label | data |
+| aliasing.cpp:164:15:164:15 | s [post update] [data] | semmle.label | s [post update] [data] |
+| aliasing.cpp:164:17:164:20 | ref arg data | semmle.label | ref arg data |
+| aliasing.cpp:165:8:165:8 | s [data] | semmle.label | s [data] |
+| aliasing.cpp:165:8:165:16 | access to array | semmle.label | access to array |
+| aliasing.cpp:165:10:165:13 | data | semmle.label | data |
+| aliasing.cpp:175:15:175:22 | ref arg & ... | semmle.label | ref arg & ... |
+| aliasing.cpp:175:16:175:17 | s2 [post update] [s, m1] | semmle.label | s2 [post update] [s, m1] |
+| aliasing.cpp:175:19:175:19 | s [post update] [m1] | semmle.label | s [post update] [m1] |
+| aliasing.cpp:175:21:175:22 | m1 [inner post update] | semmle.label | m1 [inner post update] |
+| aliasing.cpp:176:8:176:9 | s2 [s, m1] | semmle.label | s2 [s, m1] |
+| aliasing.cpp:176:11:176:11 | s [m1] | semmle.label | s [m1] |
+| aliasing.cpp:176:13:176:14 | m1 | semmle.label | m1 |
+| aliasing.cpp:187:15:187:22 | ref arg & ... | semmle.label | ref arg & ... |
+| aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] | semmle.label | s2 [post update] [s, m1] |
+| aliasing.cpp:187:19:187:19 | s [post update] [m1] | semmle.label | s [post update] [m1] |
+| aliasing.cpp:187:21:187:22 | m1 [inner post update] | semmle.label | m1 [inner post update] |
+| aliasing.cpp:189:8:189:11 | s2_2 [s, m1] | semmle.label | s2_2 [s, m1] |
+| aliasing.cpp:189:13:189:13 | s [m1] | semmle.label | s [m1] |
+| aliasing.cpp:189:15:189:16 | m1 | semmle.label | m1 |
+| aliasing.cpp:200:15:200:24 | ref arg & ... | semmle.label | ref arg & ... |
+| aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] | semmle.label | ps2 [post update] [s, m1] |
+| aliasing.cpp:200:21:200:21 | s [post update] [m1] | semmle.label | s [post update] [m1] |
+| aliasing.cpp:200:23:200:24 | m1 [inner post update] | semmle.label | m1 [inner post update] |
+| aliasing.cpp:201:8:201:10 | ps2 [s, m1] | semmle.label | ps2 [s, m1] |
+| aliasing.cpp:201:13:201:13 | s [m1] | semmle.label | s [m1] |
+| aliasing.cpp:201:15:201:16 | m1 | semmle.label | m1 |
+| arrays.cpp:6:12:6:21 | call to user_input | semmle.label | call to user_input |
+| arrays.cpp:7:8:7:13 | access to array | semmle.label | access to array |
+| arrays.cpp:8:8:8:13 | access to array | semmle.label | access to array |
+| arrays.cpp:9:8:9:11 | * ... | semmle.label | * ... |
+| arrays.cpp:10:8:10:15 | * ... | semmle.label | * ... |
+| 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, 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, 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, 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, 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, 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, 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] |
+| arrays.cpp:44:27:44:30 | data | semmle.label | data |
| by_reference.cpp:50:3:50:3 | ref arg s [a] | semmle.label | ref arg s [a] |
| by_reference.cpp:50:17:50:26 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:51:8:51:8 | s [a] | semmle.label | s [a] |
@@ -562,6 +731,8 @@ nodes
| by_reference.cpp:88:3:88:7 | inner [post update] [a] | semmle.label | inner [post update] [a] |
| by_reference.cpp:88:3:88:24 | ... = ... | semmle.label | ... = ... |
| by_reference.cpp:88:13:88:22 | call to user_input | semmle.label | call to user_input |
+| by_reference.cpp:92:4:92:5 | pa [inner post update] | semmle.label | pa [inner post update] |
+| by_reference.cpp:92:9:92:18 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:95:25:95:26 | pa | semmle.label | pa |
| by_reference.cpp:96:8:96:17 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:102:21:102:39 | ref arg & ... [a] | semmle.label | ref arg & ... [a] |
@@ -569,23 +740,33 @@ nodes
| by_reference.cpp:102:28:102:39 | inner_nested [inner post update] [a] | semmle.label | inner_nested [inner post update] [a] |
| by_reference.cpp:103:21:103:25 | outer [post update] [inner_ptr, a] | semmle.label | outer [post update] [inner_ptr, a] |
| by_reference.cpp:103:27:103:35 | ref arg inner_ptr [a] | semmle.label | ref arg inner_ptr [a] |
+| by_reference.cpp:104:15:104:22 | ref arg & ... | semmle.label | ref arg & ... |
+| by_reference.cpp:104:16:104:20 | outer [post update] [a] | semmle.label | outer [post update] [a] |
+| by_reference.cpp:104:22:104:22 | a [inner post update] | semmle.label | a [inner post update] |
| by_reference.cpp:106:21:106:41 | ref arg & ... [a] | semmle.label | ref arg & ... [a] |
| by_reference.cpp:106:22:106:27 | pouter [post update] [inner_nested, a] | semmle.label | pouter [post update] [inner_nested, a] |
| by_reference.cpp:106:30:106:41 | inner_nested [inner post update] [a] | semmle.label | inner_nested [inner post update] [a] |
| by_reference.cpp:107:21:107:26 | pouter [post update] [inner_ptr, a] | semmle.label | pouter [post update] [inner_ptr, a] |
| by_reference.cpp:107:29:107:37 | ref arg inner_ptr [a] | semmle.label | ref arg inner_ptr [a] |
+| by_reference.cpp:108:15:108:24 | ref arg & ... | semmle.label | ref arg & ... |
+| by_reference.cpp:108:16:108:21 | pouter [post update] [a] | semmle.label | pouter [post update] [a] |
+| by_reference.cpp:108:24:108:24 | a [inner post update] | semmle.label | a [inner post update] |
| by_reference.cpp:110:8:110:12 | outer [inner_nested, a] | semmle.label | outer [inner_nested, a] |
| by_reference.cpp:110:14:110:25 | inner_nested [a] | semmle.label | inner_nested [a] |
| by_reference.cpp:110:27:110:27 | a | semmle.label | a |
| by_reference.cpp:111:8:111:12 | outer [inner_ptr, a] | semmle.label | outer [inner_ptr, a] |
| by_reference.cpp:111:14:111:22 | inner_ptr [a] | semmle.label | inner_ptr [a] |
| by_reference.cpp:111:25:111:25 | a | semmle.label | a |
+| by_reference.cpp:112:8:112:12 | outer [a] | semmle.label | outer [a] |
+| by_reference.cpp:112:14:112:14 | a | semmle.label | a |
| by_reference.cpp:114:8:114:13 | pouter [inner_nested, a] | semmle.label | pouter [inner_nested, a] |
| by_reference.cpp:114:16:114:27 | inner_nested [a] | semmle.label | inner_nested [a] |
| by_reference.cpp:114:29:114:29 | a | semmle.label | a |
| by_reference.cpp:115:8:115:13 | pouter [inner_ptr, a] | semmle.label | pouter [inner_ptr, a] |
| by_reference.cpp:115:16:115:24 | inner_ptr [a] | semmle.label | inner_ptr [a] |
| by_reference.cpp:115:27:115:27 | a | semmle.label | a |
+| by_reference.cpp:116:8:116:13 | pouter [a] | semmle.label | pouter [a] |
+| by_reference.cpp:116:16:116:16 | a | semmle.label | a |
| by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] | semmle.label | outer [post update] [inner_nested, a] |
| by_reference.cpp:122:27:122:38 | ref arg inner_nested [a] | semmle.label | ref arg inner_nested [a] |
| by_reference.cpp:123:21:123:36 | ref arg * ... [a] | semmle.label | ref arg * ... [a] |
@@ -616,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_] |
@@ -703,6 +886,19 @@ 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, 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, 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, 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 |
| simple.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] |
| simple.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] |
| simple.cpp:28:10:28:10 | f [a_] | semmle.label | f [a_] |
@@ -732,6 +928,11 @@ nodes
| simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input |
| simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 |
| simple.cpp:84:14:84:20 | this [f2, f1] | semmle.label | this [f2, f1] |
+| simple.cpp:92:5:92:5 | a [post update] [i] | semmle.label | a [post update] [i] |
+| simple.cpp:92:5:92:22 | ... = ... | semmle.label | ... = ... |
+| simple.cpp:92:11:92:20 | call to user_input | semmle.label | call to user_input |
+| simple.cpp:94:10:94:11 | a2 [i] | semmle.label | a2 [i] |
+| simple.cpp:94:13:94:13 | i | semmle.label | i |
| struct_init.c:14:24:14:25 | ab [a] | semmle.label | ab [a] |
| struct_init.c:15:8:15:9 | ab [a] | semmle.label | ab [a] |
| struct_init.c:15:12:15:12 | a | semmle.label | a |
@@ -792,28 +993,41 @@ nodes
| aliasing.cpp:30:11:30:12 | m1 | aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:30:11:30:12 | m1 | m1 flows from $@ | aliasing.cpp:13:10:13:19 | call to user_input | call to user_input |
| aliasing.cpp:62:14:62:15 | m1 | aliasing.cpp:60:11:60:20 | call to user_input | aliasing.cpp:62:14:62:15 | m1 | m1 flows from $@ | aliasing.cpp:60:11:60:20 | call to user_input | call to user_input |
| aliasing.cpp:93:12:93:13 | m1 | aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:93:12:93:13 | m1 | m1 flows from $@ | aliasing.cpp:92:12:92:21 | call to user_input | call to user_input |
+| aliasing.cpp:159:8:159:14 | * ... | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:159:8:159:14 | * ... | * ... flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:165:8:165:16 | access to array | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:165:8:165:16 | access to array | access to array flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:176:13:176:14 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:176:13:176:14 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:189:15:189:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:189:15:189:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| aliasing.cpp:201:15:201:16 | m1 | aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:201:15:201:16 | m1 | m1 flows from $@ | aliasing.cpp:106:9:106:18 | call to user_input | call to user_input |
+| arrays.cpp:7:8:7:13 | access to array | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array | access to array flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
+| arrays.cpp:8:8:8:13 | access to array | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:8:8:8:13 | access to array | access to array flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
+| arrays.cpp:9:8:9:11 | * ... | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... | * ... flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | call to user_input |
+| arrays.cpp:10:8:10:15 | * ... | arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... | * ... flows from $@ | arrays.cpp:6:12:6:21 | call to user_input | 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:16:8:16:13 | access to array | access to array flows from $@ | arrays.cpp:15:14:15:23 | call to user_input | call to user_input |
+| arrays.cpp:17:8:17:13 | access to array | arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:17:8:17:13 | access to array | access to array flows from $@ | arrays.cpp:15:14:15:23 | call to user_input | call to user_input |
+| arrays.cpp:37:24:37:27 | data | arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:37:24:37:27 | data | data flows from $@ | arrays.cpp:36:26:36:35 | call to user_input | call to user_input |
+| arrays.cpp:38:24:38:27 | data | arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:38:24:38:27 | data | data flows from $@ | arrays.cpp:36:26:36:35 | call to user_input | call to user_input |
+| arrays.cpp:43:27:43:30 | data | arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:43:27:43:30 | data | data flows from $@ | arrays.cpp:42:29:42:38 | call to user_input | call to user_input |
+| arrays.cpp:44:27:44:30 | data | arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:44:27:44:30 | data | data flows from $@ | arrays.cpp:42:29:42:38 | call to user_input | call to user_input |
| by_reference.cpp:51:10:51:20 | call to getDirectly | by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:51:10:51:20 | call to getDirectly | call to getDirectly flows from $@ | by_reference.cpp:50:17:50:26 | call to user_input | call to user_input |
| by_reference.cpp:57:10:57:22 | call to getIndirectly | by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:57:10:57:22 | call to getIndirectly | call to getIndirectly flows from $@ | by_reference.cpp:56:19:56:28 | call to user_input | call to user_input |
| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:63:10:63:28 | call to getThroughNonMember | call to getThroughNonMember flows from $@ | by_reference.cpp:62:25:62:34 | call to user_input | call to user_input |
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:69:8:69:20 | call to nonMemberGetA | call to nonMemberGetA flows from $@ | by_reference.cpp:68:21:68:30 | call to user_input | call to user_input |
| by_reference.cpp:110:27:110:27 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:110:27:110:27 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
| by_reference.cpp:111:25:111:25 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:111:25:111:25 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
+| by_reference.cpp:112:14:112:14 | a | by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:112:14:112:14 | a | a flows from $@ | by_reference.cpp:92:9:92:18 | call to user_input | call to user_input |
| by_reference.cpp:114:29:114:29 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:114:29:114:29 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
| by_reference.cpp:115:27:115:27 | a | by_reference.cpp:84:14:84:23 | call to user_input | by_reference.cpp:115:27:115:27 | a | a flows from $@ | by_reference.cpp:84:14:84:23 | call to user_input | call to user_input |
+| by_reference.cpp:116:16:116:16 | a | by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:116:16:116:16 | a | a flows from $@ | by_reference.cpp:92:9:92:18 | call to user_input | call to user_input |
| by_reference.cpp:130:27:130:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:130:27:130:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input |
| by_reference.cpp:131:25:131:25 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:131:25:131:25 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input |
| 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: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 |
@@ -824,12 +1038,14 @@ nodes
| qualifiers.cpp:38:23:38:23 | a | qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:38:23:38:23 | a | a flows from $@ | qualifiers.cpp:37:38:37:47 | call to user_input | call to user_input |
| qualifiers.cpp:43:23:43:23 | a | qualifiers.cpp:42:29:42:38 | call to user_input | qualifiers.cpp:43:23:43:23 | a | a flows from $@ | qualifiers.cpp:42:29:42:38 | call to user_input | call to user_input |
| qualifiers.cpp:48:23:48:23 | a | qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:48:23:48:23 | a | a flows from $@ | qualifiers.cpp:47:31:47:40 | call to user_input | call to user_input |
+| realistic.cpp:61:47:61:55 | bufferLen | realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:61:47:61:55 | bufferLen | bufferLen flows from $@ | realistic.cpp:53:55:53:64 | call to user_input | call to user_input |
| simple.cpp:28:12:28:12 | call to a | simple.cpp:39:12:39:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:39:12:39:21 | call to user_input | call to user_input |
| simple.cpp:28:12:28:12 | call to a | simple.cpp:41:12:41:21 | call to user_input | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:41:12:41:21 | call to user_input | call to user_input |
| simple.cpp:29:12:29:12 | call to b | simple.cpp:40:12:40:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:40:12:40:21 | call to user_input | call to user_input |
| simple.cpp:29:12:29:12 | call to b | simple.cpp:42:12:42:21 | call to user_input | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:42:12:42:21 | call to user_input | call to user_input |
| simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input |
| simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input |
+| simple.cpp:94:13:94:13 | i | simple.cpp:92:11:92:20 | call to user_input | simple.cpp:94:13:94:13 | i | i flows from $@ | simple.cpp:92:11:92:20 | call to user_input | call to user_input |
| struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input |
| struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input |
| struct_init.c:15:12:15:12 | a | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:40:20:40:29 | call to user_input | call to user_input |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/realistic.cpp b/cpp/ql/test/library-tests/dataflow/fields/realistic.cpp
new file mode 100644
index 000000000000..f121dc58b4d7
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/fields/realistic.cpp
@@ -0,0 +1,70 @@
+typedef unsigned char u8;
+typedef unsigned long size_t;
+struct UserInput {
+ size_t bufferLen;
+ u8 buffer[256];
+};
+struct Baz {
+ int foo;
+ struct UserInput userInput;
+};
+struct Bar {
+ u8* foo;
+ struct Baz * baz;
+};
+struct Foo {
+ struct Bar bar[128];
+};
+void printf(const char *fmt, ...) {
+ return;
+}
+void * malloc(size_t size) {
+ static unsigned char buffer[0x1000];
+ static unsigned int offset;
+ if (size + offset >= sizeof(buffer)) return nullptr;
+ void* m = (void*)&buffer[offset];
+ offset += size;
+ return m;
+}
+void * memcpy ( void * destination, const void * source, size_t num ) {
+ u8* d = (u8*)destination;
+ u8* s = (u8*)source;
+ u8* e = d + num;
+ while(d != e) {
+ *d++ = *s++;
+ }
+ return destination;
+}
+void *user_input(void) {
+ return (void*)"\x0a\x00\x00\x00\x00\x00\x00\x00The quick brown fox jumps over the lazy dog";
+}
+void sink(void *o) {
+ printf("%p\n", o);
+}
+#define MAX_BAZ 3
+int main(int argc, char** argv) {
+ char dst[256];
+ struct Foo foo;
+ for (int i = 0; i < MAX_BAZ; i++) {
+ foo.bar[i].baz = (struct Baz*)malloc(sizeof(struct Baz));
+ }
+ int i = 0;
+ while(i < MAX_BAZ) {
+ foo.bar[i].baz->userInput.bufferLen = (size_t)user_input();
+ memcpy(foo.bar[i].baz->userInput.buffer, user_input(), sizeof(foo.bar[i].baz->userInput.buffer));
+ if(foo.bar[i].baz->userInput.bufferLen > sizeof(foo.bar[i].baz->userInput.buffer))
+ {
+ printf("The user-supplied input 0x%lx is larger than the buffer 0x%lx!\n", foo.bar[i].baz->userInput.bufferLen, sizeof(foo.bar[i].baz->userInput.buffer));
+ return -1;
+ }
+ memcpy(dst, foo.bar[i].baz->userInput.buffer, foo.bar[i].baz->userInput.bufferLen);
+ sink((void*)foo.bar[i].baz->userInput.bufferLen); // $ast $f-:ir
+ // There is no flow to the following two `sink` calls because the
+ // source is the _pointer_ returned by `user_input` rather than the
+ // _data_ to which it points.
+ sink((void*)foo.bar[i].baz->userInput.buffer); // $f-:ast,ir
+ sink((void*)dst); // $f-:ast,ir
+ i++;
+ }
+ return 0;
+}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp
index 4a3a15a0b176..3f7c91f77471 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp
@@ -25,8 +25,8 @@ class Foo
void bar(Foo &f)
{
- sink(f.a()); //$ast=39:12 $ast=41:12 $f-:ir
- sink(f.b()); //$ast=40:12 $ast=42:12 $f-:ir
+ sink(f.a()); //$ast=39:12 $ast=41:12 $ir=39:12 $ir=41:12
+ sink(f.b()); //$ast=40:12 $ast=42:12 $ir=40:12 $ir=42:12
}
void foo()
@@ -85,4 +85,13 @@ struct C2
}
};
+typedef A A_typedef;
+
+void single_field_test_typedef(A_typedef a)
+{
+ a.i = user_input();
+ A_typedef a2 = a;
+ sink(a2.i); //$ast,ir
+}
+
} // namespace Simple
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/IRTaintTestCommon.qll b/cpp/ql/test/library-tests/dataflow/taint-tests/IRTaintTestCommon.qll
index aa24c2629c7c..715d1883525a 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/IRTaintTestCommon.qll
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/IRTaintTestCommon.qll
@@ -1,4 +1,5 @@
import cpp
+import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.TaintTracking
/** Common data flow configuration to be used by tests. */
@@ -6,7 +7,7 @@ class TestAllocationConfig extends TaintTracking::Configuration {
TestAllocationConfig() { this = "TestAllocationConfig" }
override predicate isSource(DataFlow::Node source) {
- source.asExpr().(FunctionCall).getTarget().getName() = "source"
+ source.(DataFlow::ExprNode).getConvertedExpr().(FunctionCall).getTarget().getName() = "source"
or
source.asParameter().getName().matches("source%")
or
@@ -17,7 +18,16 @@ class TestAllocationConfig extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) {
exists(FunctionCall call |
call.getTarget().getName() = "sink" and
- sink.asExpr() = call.getAnArgument()
+ sink.(DataFlow::ExprNode).getConvertedExpr() = call.getAnArgument()
+ or
+ call.getTarget().getName() = "sink" and
+ sink.asExpr() = call.getAnArgument() and
+ sink.(DataFlow::ExprNode).getConvertedExpr() instanceof ReferenceDereferenceExpr
+ )
+ or
+ exists(ReadSideEffectInstruction read |
+ read.getSideEffectOperand() = sink.asOperand() and
+ read.getPrimaryInstruction().(CallInstruction).getStaticCallTarget().hasName("sink")
)
}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp
new file mode 100644
index 000000000000..b86d2bf02346
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/arrayassignment.cpp
@@ -0,0 +1,147 @@
+
+int source();
+void sink(int);
+void sink(class MyInt);
+void sink(class MyArray);
+
+void test_pointer_deref_assignment()
+{
+ int x = 0;
+ int *p_x = &x;
+ int *p2_x = &x;
+ int &r_x = x;
+
+ *p_x = source();
+
+ sink(x); // tainted [DETECTED BY IR ONLY]
+ sink(*p_x); // tainted
+ sink(*p2_x); // tainted [DETECTED BY IR ONLY]
+ sink(r_x); // tainted [DETECTED BY IR ONLY]
+}
+
+void test_reference_deref_assignment()
+{
+ int x = 0;
+ int *p_x = &x;
+ int &r_x = x;
+ int &r2_x = x;
+
+ r_x = source();
+
+ sink(x); // tainted [DETECTED BY IR ONLY]
+ sink(*p_x); // tainted [DETECTED BY IR ONLY]
+ sink(r_x); // tainted
+ sink(r2_x); // tainted [DETECTED BY IR ONLY]
+}
+
+class MyInt
+{
+public:
+ MyInt() : i(0) {}
+
+ int &get() { return i; }
+
+ MyInt &operator=(const int &other);
+ MyInt &operator=(const MyInt &other);
+
+ int i;
+};
+
+void test_myint_member_assignment()
+{
+ MyInt mi;
+
+ mi.i = source();
+
+ sink(mi); // tainted [DETECTED BY IR ONLY]
+ sink(mi.get()); // tainted
+}
+
+void test_myint_method_assignment()
+{
+ MyInt mi;
+
+ mi.get() = source();
+
+ sink(mi); // tainted [DETECTED BY IR ONLY]
+ sink(mi.get()); // tainted
+}
+
+void test_myint_overloaded_assignment()
+{
+ MyInt mi, mi2;
+
+ mi = source();
+ mi2 = mi;
+
+ sink(mi); // tainted [NOT DETECTED]
+ sink(mi.get()); // tainted [NOT DETECTED]
+ sink(mi2); // tainted [NOT DETECTED]
+ sink(mi2.get()); // tainted [NOT DETECTED]
+}
+
+class MyArray
+{
+public:
+ MyArray() : values({0}) {}
+
+ int &get(int i) { return values[i]; }
+
+ int &operator[](int i);
+
+ int values[10];
+};
+
+void test_myarray_member_assignment()
+{
+ MyArray ma;
+
+ ma.values[0] = source();
+
+ sink(ma.values[0]); // tainted
+}
+
+void test_myarray_method_assignment()
+{
+ MyArray ma;
+
+ ma.get(0) = source();
+
+ sink(ma.get(0)); // tainted [NOT DETECTED]
+}
+
+void test_myarray_overloaded_assignment()
+{
+ MyArray ma, ma2;
+
+ ma[0] = source();
+ ma2 = ma;
+
+ sink(ma[0]); // tainted [NOT DETECTED]
+ sink(ma2[0]); // tainted [NOT DETECTED]
+}
+
+void sink(int *);
+
+void test_array_reference_assignment()
+{
+ int arr1[10] = {0};
+ int arr2[10] = {0};
+ int arr3[10] = {0};
+ int &ref1 = arr1[5];
+ int *ptr2, *ptr3;
+
+ ref1 = source();
+ sink(ref1); // tainted
+ sink(arr1[5]); // tainted [DETECTED BY IR ONLY]
+
+ ptr2 = &(arr2[5]);
+ *ptr2 = source();
+ sink(*ptr2); // tainted
+ sink(arr2[5]); // tainted [DETECTED BY IR ONLY]
+
+ ptr3 = arr3;
+ ptr3[5] = source();
+ sink(ptr3[5]); // tainted
+ sink(arr3[5]); // tainted [DETECTED BY IR ONLY]
+}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp
new file mode 100644
index 000000000000..d5745bcb7133
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass.cpp
@@ -0,0 +1,69 @@
+
+int source();
+void sink(...) {};
+
+class MyCopyableClass {
+public:
+ MyCopyableClass() {} // Constructor
+ MyCopyableClass(int _v) : v(_v) {} // ConversionConstructor
+ MyCopyableClass(const MyCopyableClass &other) : v(other.v) {} // CopyConstructor
+ MyCopyableClass &operator=(const MyCopyableClass &other) { // CopyAssignmentOperator
+ v = other.v;
+ return *this;
+ }
+
+ int v;
+};
+
+void test_copyableclass()
+{
+ {
+ MyCopyableClass s1(1);
+ MyCopyableClass s2 = 1;
+ MyCopyableClass s3(s1);
+ MyCopyableClass s4;
+ s4 = 1;
+
+ sink(s1);
+ sink(s2);
+ sink(s3);
+ sink(s4);
+ }
+
+ {
+ MyCopyableClass s1(source());
+ MyCopyableClass s2 = source();
+ MyCopyableClass s3(s1);
+ MyCopyableClass s4;
+ s4 = source();
+
+ sink(s1); // tainted
+ sink(s2); // tainted
+ sink(s3); // tainted
+ sink(s4); // tainted
+ }
+
+ {
+ MyCopyableClass s1;
+ MyCopyableClass s2 = s1;
+ MyCopyableClass s3(s1);
+ MyCopyableClass s4;
+ s4 = s1;
+
+ sink(s1);
+ sink(s2);
+ sink(s3);
+ sink(s4);
+ }
+
+ {
+ MyCopyableClass s1 = MyCopyableClass(source());
+ MyCopyableClass s2;
+ MyCopyableClass s3;
+ s2 = MyCopyableClass(source());
+
+ sink(s1); // tainted
+ sink(s2); // tainted
+ sink(s3 = source()); // tainted
+ }
+}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp
new file mode 100644
index 000000000000..67f6a45fbe7c
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp
@@ -0,0 +1,69 @@
+
+int source();
+void sink(...) {};
+
+class MyCopyableClassDeclOnly {
+public:
+ MyCopyableClassDeclOnly(); // Constructor
+ MyCopyableClassDeclOnly(int _v); // ConversionConstructor
+ MyCopyableClassDeclOnly(const MyCopyableClassDeclOnly &other); // CopyConstructor
+ MyCopyableClassDeclOnly &operator=(const MyCopyableClassDeclOnly &other); // CopyAssignmentOperator
+
+
+
+
+ int v;
+};
+
+void test_copyableclass()
+{
+ {
+ MyCopyableClassDeclOnly s1(1);
+ MyCopyableClassDeclOnly s2 = 1;
+ MyCopyableClassDeclOnly s3(s1);
+ MyCopyableClassDeclOnly s4;
+ s4 = 1;
+
+ sink(s1);
+ sink(s2);
+ sink(s3);
+ sink(s4);
+ }
+
+ {
+ MyCopyableClassDeclOnly s1(source());
+ MyCopyableClassDeclOnly s2 = source();
+ MyCopyableClassDeclOnly s3(s1);
+ MyCopyableClassDeclOnly s4;
+ s4 = source();
+
+ sink(s1); // tainted
+ sink(s2); // tainted
+ sink(s3); // tainted
+ sink(s4); // tainted
+ }
+
+ {
+ MyCopyableClassDeclOnly s1;
+ MyCopyableClassDeclOnly s2 = s1;
+ MyCopyableClassDeclOnly s3(s1);
+ MyCopyableClassDeclOnly s4;
+ s4 = s1;
+
+ sink(s1);
+ sink(s2);
+ sink(s3);
+ sink(s4);
+ }
+
+ {
+ MyCopyableClassDeclOnly s1 = MyCopyableClassDeclOnly(source());
+ MyCopyableClassDeclOnly s2;
+ MyCopyableClassDeclOnly s3;
+ s2 = MyCopyableClassDeclOnly(source());
+
+ sink(s1); // tainted
+ sink(s2); // tainted
+ sink(s3 = source()); // tainted
+ }
+}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp
index 90bbb1ca0cd3..fbe78cf02e10 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp
@@ -112,7 +112,7 @@ void test1()
{
char buffer[256] = {0};
sink(mysprintf(buffer, 256, "%s", string::source()));
- sink(buffer); // tainted [NOT DETECTED - implement UserDefinedFormattingFunction.getOutputParameterIndex()]
+ sink(buffer); // tainted
}
{
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
index 48d96a5c2c22..cc5f33b50c3c 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected
@@ -1,3 +1,260 @@
+| arrayassignment.cpp:9:9:9:10 | 0 | arrayassignment.cpp:10:14:10:14 | x | |
+| arrayassignment.cpp:9:9:9:10 | 0 | arrayassignment.cpp:11:15:11:15 | x | |
+| arrayassignment.cpp:9:9:9:10 | 0 | arrayassignment.cpp:12:13:12:13 | x | |
+| arrayassignment.cpp:9:9:9:10 | 0 | arrayassignment.cpp:16:7:16:7 | x | |
+| arrayassignment.cpp:10:13:10:14 | & ... | arrayassignment.cpp:14:3:14:5 | p_x | |
+| arrayassignment.cpp:10:13:10:14 | & ... | arrayassignment.cpp:17:8:17:10 | p_x | |
+| arrayassignment.cpp:10:14:10:14 | x | arrayassignment.cpp:10:13:10:14 | & ... | |
+| arrayassignment.cpp:11:14:11:15 | & ... | arrayassignment.cpp:18:8:18:11 | p2_x | |
+| arrayassignment.cpp:11:15:11:15 | x | arrayassignment.cpp:11:14:11:15 | & ... | |
+| arrayassignment.cpp:12:13:12:13 | x | arrayassignment.cpp:19:7:19:9 | r_x | |
+| arrayassignment.cpp:14:2:14:5 | * ... [post update] | arrayassignment.cpp:14:3:14:5 | p_x [inner post update] | |
+| arrayassignment.cpp:14:2:14:5 | * ... [post update] | arrayassignment.cpp:17:8:17:10 | p_x | |
+| arrayassignment.cpp:14:2:14:16 | ... = ... | arrayassignment.cpp:14:2:14:5 | * ... [post update] | |
+| arrayassignment.cpp:14:3:14:5 | p_x | arrayassignment.cpp:14:2:14:5 | * ... | TAINT |
+| arrayassignment.cpp:14:9:14:14 | call to source | arrayassignment.cpp:14:2:14:16 | ... = ... | |
+| arrayassignment.cpp:17:8:17:10 | p_x | arrayassignment.cpp:17:7:17:10 | * ... | TAINT |
+| arrayassignment.cpp:18:8:18:11 | p2_x | arrayassignment.cpp:18:7:18:11 | * ... | TAINT |
+| arrayassignment.cpp:24:9:24:10 | 0 | arrayassignment.cpp:25:14:25:14 | x | |
+| arrayassignment.cpp:24:9:24:10 | 0 | arrayassignment.cpp:26:13:26:13 | x | |
+| arrayassignment.cpp:24:9:24:10 | 0 | arrayassignment.cpp:27:14:27:14 | x | |
+| arrayassignment.cpp:24:9:24:10 | 0 | arrayassignment.cpp:31:7:31:7 | x | |
+| arrayassignment.cpp:25:13:25:14 | & ... | arrayassignment.cpp:32:8:32:10 | p_x | |
+| arrayassignment.cpp:25:14:25:14 | x | arrayassignment.cpp:25:13:25:14 | & ... | |
+| arrayassignment.cpp:27:14:27:14 | x | arrayassignment.cpp:34:7:34:10 | r2_x | |
+| arrayassignment.cpp:29:2:29:4 | r_x [post update] | arrayassignment.cpp:33:7:33:9 | r_x | |
+| arrayassignment.cpp:29:2:29:15 | ... = ... | arrayassignment.cpp:29:2:29:4 | r_x [post update] | |
+| arrayassignment.cpp:29:8:29:13 | call to source | arrayassignment.cpp:29:2:29:15 | ... = ... | |
+| arrayassignment.cpp:29:8:29:13 | call to source | arrayassignment.cpp:33:7:33:9 | r_x | |
+| arrayassignment.cpp:32:8:32:10 | p_x | arrayassignment.cpp:32:7:32:10 | * ... | TAINT |
+| arrayassignment.cpp:37:7:37:7 | Unknown literal | arrayassignment.cpp:37:7:37:7 | constructor init of field i | TAINT |
+| arrayassignment.cpp:37:7:37:7 | this | arrayassignment.cpp:37:7:37:7 | constructor init of field i [pre-this] | |
+| arrayassignment.cpp:40:2:40:6 | this | arrayassignment.cpp:40:12:40:15 | constructor init of field i [pre-this] | |
+| arrayassignment.cpp:40:12:40:15 | 0 | arrayassignment.cpp:40:12:40:15 | constructor init of field i | TAINT |
+| arrayassignment.cpp:42:7:42:9 | this | arrayassignment.cpp:42:22:42:22 | this | |
+| arrayassignment.cpp:52:8:52:9 | call to MyInt | arrayassignment.cpp:54:2:54:3 | mi | |
+| arrayassignment.cpp:52:8:52:9 | call to MyInt | arrayassignment.cpp:56:7:56:8 | mi | |
+| arrayassignment.cpp:52:8:52:9 | call to MyInt | arrayassignment.cpp:57:7:57:8 | mi | |
+| arrayassignment.cpp:54:2:54:3 | mi [post update] | arrayassignment.cpp:56:7:56:8 | mi | |
+| arrayassignment.cpp:54:2:54:3 | mi [post update] | arrayassignment.cpp:57:7:57:8 | mi | |
+| arrayassignment.cpp:54:2:54:16 | ... = ... | arrayassignment.cpp:54:5:54:5 | i [post update] | |
+| arrayassignment.cpp:54:9:54:14 | call to source | arrayassignment.cpp:54:2:54:16 | ... = ... | |
+| arrayassignment.cpp:62:8:62:9 | call to MyInt | arrayassignment.cpp:64:2:64:3 | mi | |
+| arrayassignment.cpp:62:8:62:9 | call to MyInt | arrayassignment.cpp:66:7:66:8 | mi | |
+| arrayassignment.cpp:62:8:62:9 | call to MyInt | arrayassignment.cpp:67:7:67:8 | mi | |
+| arrayassignment.cpp:64:2:64:3 | ref arg mi | arrayassignment.cpp:66:7:66:8 | mi | |
+| arrayassignment.cpp:64:2:64:3 | ref arg mi | arrayassignment.cpp:67:7:67:8 | mi | |
+| arrayassignment.cpp:64:2:64:20 | ... = ... | arrayassignment.cpp:64:5:64:7 | call to get [post update] | |
+| arrayassignment.cpp:64:13:64:18 | call to source | arrayassignment.cpp:64:2:64:20 | ... = ... | |
+| arrayassignment.cpp:72:8:72:9 | call to MyInt | arrayassignment.cpp:74:2:74:3 | mi | |
+| arrayassignment.cpp:72:8:72:9 | call to MyInt | arrayassignment.cpp:75:8:75:9 | mi | |
+| arrayassignment.cpp:72:8:72:9 | call to MyInt | arrayassignment.cpp:77:7:77:8 | mi | |
+| arrayassignment.cpp:72:8:72:9 | call to MyInt | arrayassignment.cpp:78:7:78:8 | mi | |
+| arrayassignment.cpp:72:12:72:14 | call to MyInt | arrayassignment.cpp:75:2:75:4 | mi2 | |
+| arrayassignment.cpp:72:12:72:14 | call to MyInt | arrayassignment.cpp:79:7:79:9 | mi2 | |
+| arrayassignment.cpp:72:12:72:14 | call to MyInt | arrayassignment.cpp:80:7:80:9 | mi2 | |
+| arrayassignment.cpp:74:2:74:3 | ref arg mi | arrayassignment.cpp:75:8:75:9 | mi | |
+| arrayassignment.cpp:74:2:74:3 | ref arg mi | arrayassignment.cpp:77:7:77:8 | mi | |
+| arrayassignment.cpp:74:2:74:3 | ref arg mi | arrayassignment.cpp:78:7:78:8 | mi | |
+| arrayassignment.cpp:75:2:75:4 | ref arg mi2 | arrayassignment.cpp:79:7:79:9 | mi2 | |
+| arrayassignment.cpp:75:2:75:4 | ref arg mi2 | arrayassignment.cpp:80:7:80:9 | mi2 | |
+| arrayassignment.cpp:75:8:75:9 | mi | arrayassignment.cpp:75:2:75:4 | ref arg mi2 | TAINT |
+| arrayassignment.cpp:75:8:75:9 | mi | arrayassignment.cpp:75:6:75:6 | call to operator= | TAINT |
+| arrayassignment.cpp:86:2:86:8 | this | arrayassignment.cpp:86:14:86:24 | constructor init of field values [pre-this] | |
+| arrayassignment.cpp:86:14:86:24 | {...} | arrayassignment.cpp:86:14:86:24 | constructor init of field values | TAINT |
+| arrayassignment.cpp:86:22:86:22 | 0 | arrayassignment.cpp:86:14:86:24 | {...} | TAINT |
+| arrayassignment.cpp:88:7:88:9 | this | arrayassignment.cpp:88:27:88:32 | this | |
+| arrayassignment.cpp:88:15:88:15 | i | arrayassignment.cpp:88:34:88:34 | i | |
+| arrayassignment.cpp:88:27:88:32 | values | arrayassignment.cpp:88:27:88:35 | access to array | TAINT |
+| arrayassignment.cpp:88:34:88:34 | i | arrayassignment.cpp:88:27:88:35 | access to array | TAINT |
+| arrayassignment.cpp:97:10:97:11 | call to MyArray | arrayassignment.cpp:99:2:99:3 | ma | |
+| arrayassignment.cpp:97:10:97:11 | call to MyArray | arrayassignment.cpp:101:7:101:8 | ma | |
+| arrayassignment.cpp:99:2:99:3 | ma [post update] | arrayassignment.cpp:101:7:101:8 | ma | |
+| arrayassignment.cpp:99:2:99:13 | access to array [post update] | arrayassignment.cpp:99:5:99:10 | values [inner post update] | |
+| arrayassignment.cpp:99:2:99:24 | ... = ... | arrayassignment.cpp:99:2:99:13 | access to array [post update] | |
+| arrayassignment.cpp:99:5:99:10 | values | arrayassignment.cpp:99:2:99:13 | access to array | |
+| arrayassignment.cpp:99:12:99:12 | 0 | arrayassignment.cpp:99:2:99:13 | access to array | TAINT |
+| arrayassignment.cpp:99:17:99:22 | call to source | arrayassignment.cpp:99:2:99:24 | ... = ... | |
+| arrayassignment.cpp:101:10:101:15 | values | arrayassignment.cpp:101:7:101:18 | access to array | |
+| arrayassignment.cpp:101:17:101:17 | 0 | arrayassignment.cpp:101:7:101:18 | access to array | TAINT |
+| arrayassignment.cpp:106:10:106:11 | call to MyArray | arrayassignment.cpp:108:2:108:3 | ma | |
+| arrayassignment.cpp:106:10:106:11 | call to MyArray | arrayassignment.cpp:110:7:110:8 | ma | |
+| arrayassignment.cpp:108:2:108:3 | ref arg ma | arrayassignment.cpp:110:7:110:8 | ma | |
+| arrayassignment.cpp:108:2:108:21 | ... = ... | arrayassignment.cpp:108:5:108:7 | call to get [post update] | |
+| arrayassignment.cpp:108:14:108:19 | call to source | arrayassignment.cpp:108:2:108:21 | ... = ... | |
+| arrayassignment.cpp:115:10:115:11 | call to MyArray | arrayassignment.cpp:117:2:117:3 | ma | |
+| arrayassignment.cpp:115:10:115:11 | call to MyArray | arrayassignment.cpp:118:8:118:9 | ma | |
+| arrayassignment.cpp:115:10:115:11 | call to MyArray | arrayassignment.cpp:120:7:120:8 | ma | |
+| arrayassignment.cpp:117:2:117:3 | ref arg ma | arrayassignment.cpp:118:8:118:9 | ma | |
+| arrayassignment.cpp:117:2:117:3 | ref arg ma | arrayassignment.cpp:120:7:120:8 | ma | |
+| arrayassignment.cpp:117:2:117:17 | ... = ... | arrayassignment.cpp:117:4:117:4 | call to operator[] [post update] | |
+| arrayassignment.cpp:117:10:117:15 | call to source | arrayassignment.cpp:117:2:117:17 | ... = ... | |
+| arrayassignment.cpp:118:8:118:9 | ma | arrayassignment.cpp:118:2:118:9 | ... = ... | |
+| arrayassignment.cpp:118:8:118:9 | ma | arrayassignment.cpp:121:7:121:9 | ma2 | |
+| arrayassignment.cpp:128:16:128:19 | {...} | arrayassignment.cpp:131:14:131:17 | arr1 | |
+| arrayassignment.cpp:128:16:128:19 | {...} | arrayassignment.cpp:136:7:136:10 | arr1 | |
+| arrayassignment.cpp:128:18:128:18 | 0 | arrayassignment.cpp:128:16:128:19 | {...} | TAINT |
+| arrayassignment.cpp:129:16:129:19 | {...} | arrayassignment.cpp:138:11:138:14 | arr2 | |
+| arrayassignment.cpp:129:16:129:19 | {...} | arrayassignment.cpp:141:7:141:10 | arr2 | |
+| arrayassignment.cpp:129:18:129:18 | 0 | arrayassignment.cpp:129:16:129:19 | {...} | TAINT |
+| arrayassignment.cpp:130:16:130:19 | {...} | arrayassignment.cpp:143:9:143:12 | arr3 | |
+| arrayassignment.cpp:130:16:130:19 | {...} | arrayassignment.cpp:146:7:146:10 | arr3 | |
+| arrayassignment.cpp:130:18:130:18 | 0 | arrayassignment.cpp:130:16:130:19 | {...} | TAINT |
+| arrayassignment.cpp:131:14:131:17 | arr1 | arrayassignment.cpp:131:14:131:20 | access to array | TAINT |
+| arrayassignment.cpp:131:19:131:19 | 5 | arrayassignment.cpp:131:14:131:20 | access to array | TAINT |
+| arrayassignment.cpp:134:2:134:5 | ref1 [post update] | arrayassignment.cpp:135:7:135:10 | ref1 | |
+| arrayassignment.cpp:134:2:134:16 | ... = ... | arrayassignment.cpp:134:2:134:5 | ref1 [post update] | |
+| arrayassignment.cpp:134:9:134:14 | call to source | arrayassignment.cpp:134:2:134:16 | ... = ... | |
+| arrayassignment.cpp:134:9:134:14 | call to source | arrayassignment.cpp:135:7:135:10 | ref1 | |
+| arrayassignment.cpp:136:7:136:10 | arr1 | arrayassignment.cpp:136:7:136:13 | access to array | |
+| arrayassignment.cpp:136:12:136:12 | 5 | arrayassignment.cpp:136:7:136:13 | access to array | TAINT |
+| arrayassignment.cpp:138:9:138:18 | & ... | arrayassignment.cpp:138:2:138:18 | ... = ... | |
+| arrayassignment.cpp:138:9:138:18 | & ... | arrayassignment.cpp:139:3:139:6 | ptr2 | |
+| arrayassignment.cpp:138:9:138:18 | & ... | arrayassignment.cpp:140:8:140:11 | ptr2 | |
+| arrayassignment.cpp:138:11:138:14 | arr2 | arrayassignment.cpp:138:11:138:17 | access to array | TAINT |
+| arrayassignment.cpp:138:11:138:17 | access to array | arrayassignment.cpp:138:9:138:18 | & ... | |
+| arrayassignment.cpp:138:16:138:16 | 5 | arrayassignment.cpp:138:11:138:17 | access to array | TAINT |
+| arrayassignment.cpp:139:2:139:6 | * ... [post update] | arrayassignment.cpp:139:3:139:6 | ptr2 [inner post update] | |
+| arrayassignment.cpp:139:2:139:6 | * ... [post update] | arrayassignment.cpp:140:8:140:11 | ptr2 | |
+| arrayassignment.cpp:139:2:139:17 | ... = ... | arrayassignment.cpp:139:2:139:6 | * ... [post update] | |
+| arrayassignment.cpp:139:3:139:6 | ptr2 | arrayassignment.cpp:139:2:139:6 | * ... | TAINT |
+| arrayassignment.cpp:139:10:139:15 | call to source | arrayassignment.cpp:139:2:139:17 | ... = ... | |
+| arrayassignment.cpp:140:8:140:11 | ptr2 | arrayassignment.cpp:140:7:140:11 | * ... | TAINT |
+| arrayassignment.cpp:141:7:141:10 | arr2 | arrayassignment.cpp:141:7:141:13 | access to array | |
+| arrayassignment.cpp:141:12:141:12 | 5 | arrayassignment.cpp:141:7:141:13 | access to array | TAINT |
+| arrayassignment.cpp:143:9:143:12 | arr3 | arrayassignment.cpp:143:2:143:12 | ... = ... | |
+| arrayassignment.cpp:143:9:143:12 | arr3 | arrayassignment.cpp:144:2:144:5 | ptr3 | |
+| arrayassignment.cpp:143:9:143:12 | arr3 | arrayassignment.cpp:145:7:145:10 | ptr3 | |
+| arrayassignment.cpp:144:2:144:5 | ptr3 | arrayassignment.cpp:144:2:144:8 | access to array | TAINT |
+| arrayassignment.cpp:144:2:144:8 | access to array [post update] | arrayassignment.cpp:144:2:144:5 | ptr3 [inner post update] | |
+| arrayassignment.cpp:144:2:144:8 | access to array [post update] | arrayassignment.cpp:145:7:145:10 | ptr3 | |
+| arrayassignment.cpp:144:2:144:19 | ... = ... | arrayassignment.cpp:144:2:144:8 | access to array [post update] | |
+| arrayassignment.cpp:144:7:144:7 | 5 | arrayassignment.cpp:144:2:144:8 | access to array | TAINT |
+| arrayassignment.cpp:144:12:144:17 | call to source | arrayassignment.cpp:144:2:144:19 | ... = ... | |
+| arrayassignment.cpp:145:7:145:10 | ptr3 | arrayassignment.cpp:145:7:145:13 | access to array | TAINT |
+| arrayassignment.cpp:145:12:145:12 | 5 | arrayassignment.cpp:145:7:145:13 | access to array | TAINT |
+| arrayassignment.cpp:146:7:146:10 | arr3 | arrayassignment.cpp:146:7:146:13 | access to array | |
+| arrayassignment.cpp:146:12:146:12 | 5 | arrayassignment.cpp:146:7:146:13 | access to array | TAINT |
+| copyableclass.cpp:8:2:8:16 | this | copyableclass.cpp:8:28:8:32 | constructor init of field v [pre-this] | |
+| copyableclass.cpp:8:22:8:23 | _v | copyableclass.cpp:8:30:8:31 | _v | |
+| copyableclass.cpp:8:30:8:31 | _v | copyableclass.cpp:8:28:8:32 | constructor init of field v | TAINT |
+| copyableclass.cpp:9:2:9:16 | this | copyableclass.cpp:9:50:9:59 | constructor init of field v [pre-this] | |
+| copyableclass.cpp:9:41:9:45 | other | copyableclass.cpp:9:52:9:56 | other | |
+| copyableclass.cpp:9:58:9:58 | v | copyableclass.cpp:9:50:9:59 | constructor init of field v | TAINT |
+| copyableclass.cpp:9:58:9:58 | v | copyableclass.cpp:9:58:9:58 | v | |
+| copyableclass.cpp:10:19:10:27 | this | copyableclass.cpp:11:3:11:3 | this | |
+| copyableclass.cpp:10:52:10:56 | other | copyableclass.cpp:11:7:11:11 | other | |
+| copyableclass.cpp:11:3:11:3 | this | copyableclass.cpp:12:11:12:14 | this | |
+| copyableclass.cpp:11:3:11:3 | this [post update] | copyableclass.cpp:12:11:12:14 | this | |
+| copyableclass.cpp:11:3:11:13 | ... = ... | copyableclass.cpp:11:3:11:3 | v [post update] | |
+| copyableclass.cpp:11:13:11:13 | v | copyableclass.cpp:11:3:11:13 | ... = ... | |
+| copyableclass.cpp:12:11:12:14 | this | copyableclass.cpp:12:10:12:14 | * ... | TAINT |
+| copyableclass.cpp:21:22:21:22 | 1 | copyableclass.cpp:21:22:21:23 | call to MyCopyableClass | TAINT |
+| copyableclass.cpp:21:22:21:23 | call to MyCopyableClass | copyableclass.cpp:23:22:23:23 | s1 | |
+| copyableclass.cpp:21:22:21:23 | call to MyCopyableClass | copyableclass.cpp:27:8:27:9 | s1 | |
+| copyableclass.cpp:22:23:22:24 | call to MyCopyableClass | copyableclass.cpp:28:8:28:9 | s2 | |
+| copyableclass.cpp:22:24:22:24 | 1 | copyableclass.cpp:22:23:22:24 | call to MyCopyableClass | TAINT |
+| copyableclass.cpp:23:22:23:23 | s1 | copyableclass.cpp:23:22:23:24 | call to MyCopyableClass | |
+| copyableclass.cpp:23:22:23:24 | call to MyCopyableClass | copyableclass.cpp:29:8:29:9 | s3 | |
+| copyableclass.cpp:24:19:24:20 | call to MyCopyableClass | copyableclass.cpp:25:3:25:4 | s4 | |
+| copyableclass.cpp:24:19:24:20 | call to MyCopyableClass | copyableclass.cpp:30:8:30:9 | s4 | |
+| copyableclass.cpp:25:3:25:4 | ref arg s4 | copyableclass.cpp:30:8:30:9 | s4 | |
+| copyableclass.cpp:25:8:25:8 | 1 | copyableclass.cpp:25:8:25:8 | call to MyCopyableClass | TAINT |
+| copyableclass.cpp:25:8:25:8 | call to MyCopyableClass | copyableclass.cpp:25:3:25:4 | ref arg s4 | TAINT |
+| copyableclass.cpp:25:8:25:8 | call to MyCopyableClass | copyableclass.cpp:25:6:25:6 | call to operator= | TAINT |
+| copyableclass.cpp:34:22:34:27 | call to source | copyableclass.cpp:34:22:34:30 | call to MyCopyableClass | TAINT |
+| copyableclass.cpp:34:22:34:30 | call to MyCopyableClass | copyableclass.cpp:36:22:36:23 | s1 | |
+| copyableclass.cpp:34:22:34:30 | call to MyCopyableClass | copyableclass.cpp:40:8:40:9 | s1 | |
+| copyableclass.cpp:35:23:35:31 | call to MyCopyableClass | copyableclass.cpp:41:8:41:9 | s2 | |
+| copyableclass.cpp:35:24:35:29 | call to source | copyableclass.cpp:35:23:35:31 | call to MyCopyableClass | TAINT |
+| copyableclass.cpp:36:22:36:23 | s1 | copyableclass.cpp:36:22:36:24 | call to MyCopyableClass | |
+| copyableclass.cpp:36:22:36:24 | call to MyCopyableClass | copyableclass.cpp:42:8:42:9 | s3 | |
+| copyableclass.cpp:37:19:37:20 | call to MyCopyableClass | copyableclass.cpp:38:3:38:4 | s4 | |
+| copyableclass.cpp:37:19:37:20 | call to MyCopyableClass | copyableclass.cpp:43:8:43:9 | s4 | |
+| copyableclass.cpp:38:3:38:4 | ref arg s4 | copyableclass.cpp:43:8:43:9 | s4 | |
+| copyableclass.cpp:38:8:38:13 | call to source | copyableclass.cpp:38:8:38:15 | call to MyCopyableClass | TAINT |
+| copyableclass.cpp:38:8:38:15 | call to MyCopyableClass | copyableclass.cpp:38:3:38:4 | ref arg s4 | TAINT |
+| copyableclass.cpp:38:8:38:15 | call to MyCopyableClass | copyableclass.cpp:38:6:38:6 | call to operator= | TAINT |
+| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:48:24:48:25 | s1 | |
+| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:49:22:49:23 | s1 | |
+| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:51:8:51:9 | s1 | |
+| copyableclass.cpp:47:19:47:20 | call to MyCopyableClass | copyableclass.cpp:53:8:53:9 | s1 | |
+| copyableclass.cpp:48:23:48:25 | call to MyCopyableClass | copyableclass.cpp:54:8:54:9 | s2 | |
+| copyableclass.cpp:48:24:48:25 | s1 | copyableclass.cpp:48:23:48:25 | call to MyCopyableClass | |
+| copyableclass.cpp:49:22:49:23 | s1 | copyableclass.cpp:49:22:49:24 | call to MyCopyableClass | |
+| copyableclass.cpp:49:22:49:24 | call to MyCopyableClass | copyableclass.cpp:55:8:55:9 | s3 | |
+| copyableclass.cpp:50:19:50:20 | call to MyCopyableClass | copyableclass.cpp:51:3:51:4 | s4 | |
+| copyableclass.cpp:50:19:50:20 | call to MyCopyableClass | copyableclass.cpp:56:8:56:9 | s4 | |
+| copyableclass.cpp:51:3:51:4 | ref arg s4 | copyableclass.cpp:56:8:56:9 | s4 | |
+| copyableclass.cpp:51:8:51:9 | s1 | copyableclass.cpp:51:3:51:4 | ref arg s4 | TAINT |
+| copyableclass.cpp:51:8:51:9 | s1 | copyableclass.cpp:51:6:51:6 | call to operator= | TAINT |
+| copyableclass.cpp:60:23:60:48 | call to MyCopyableClass | copyableclass.cpp:65:8:65:9 | s1 | |
+| copyableclass.cpp:60:40:60:45 | call to source | copyableclass.cpp:60:23:60:48 | call to MyCopyableClass | TAINT |
+| copyableclass.cpp:61:19:61:20 | call to MyCopyableClass | copyableclass.cpp:63:3:63:4 | s2 | |
+| copyableclass.cpp:61:19:61:20 | call to MyCopyableClass | copyableclass.cpp:66:8:66:9 | s2 | |
+| copyableclass.cpp:62:19:62:20 | call to MyCopyableClass | copyableclass.cpp:67:8:67:9 | s3 | |
+| copyableclass.cpp:63:3:63:4 | ref arg s2 | copyableclass.cpp:66:8:66:9 | s2 | |
+| copyableclass.cpp:63:8:63:32 | call to MyCopyableClass | copyableclass.cpp:63:3:63:4 | ref arg s2 | TAINT |
+| copyableclass.cpp:63:8:63:32 | call to MyCopyableClass | copyableclass.cpp:63:6:63:6 | call to operator= | TAINT |
+| copyableclass.cpp:63:24:63:29 | call to source | copyableclass.cpp:63:8:63:32 | call to MyCopyableClass | TAINT |
+| copyableclass.cpp:67:13:67:18 | call to source | copyableclass.cpp:67:13:67:20 | call to MyCopyableClass | TAINT |
+| copyableclass.cpp:67:13:67:20 | call to MyCopyableClass | copyableclass.cpp:67:8:67:9 | ref arg s3 | TAINT |
+| copyableclass.cpp:67:13:67:20 | call to MyCopyableClass | copyableclass.cpp:67:11:67:11 | call to operator= | TAINT |
+| copyableclass_declonly.cpp:21:30:21:30 | 1 | copyableclass_declonly.cpp:21:30:21:31 | call to MyCopyableClassDeclOnly | TAINT |
+| copyableclass_declonly.cpp:21:30:21:31 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:23:30:23:31 | s1 | |
+| copyableclass_declonly.cpp:21:30:21:31 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:27:8:27:9 | s1 | |
+| copyableclass_declonly.cpp:22:31:22:32 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:28:8:28:9 | s2 | |
+| copyableclass_declonly.cpp:22:32:22:32 | 1 | copyableclass_declonly.cpp:22:31:22:32 | call to MyCopyableClassDeclOnly | TAINT |
+| copyableclass_declonly.cpp:23:30:23:31 | s1 | copyableclass_declonly.cpp:23:30:23:32 | call to MyCopyableClassDeclOnly | |
+| copyableclass_declonly.cpp:23:30:23:32 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:29:8:29:9 | s3 | |
+| copyableclass_declonly.cpp:24:27:24:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:25:3:25:4 | s4 | |
+| copyableclass_declonly.cpp:24:27:24:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:30:8:30:9 | s4 | |
+| copyableclass_declonly.cpp:25:3:25:4 | ref arg s4 | copyableclass_declonly.cpp:30:8:30:9 | s4 | |
+| copyableclass_declonly.cpp:25:8:25:8 | 1 | copyableclass_declonly.cpp:25:8:25:8 | call to MyCopyableClassDeclOnly | TAINT |
+| copyableclass_declonly.cpp:25:8:25:8 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:25:3:25:4 | ref arg s4 | TAINT |
+| copyableclass_declonly.cpp:25:8:25:8 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:25:6:25:6 | call to operator= | TAINT |
+| copyableclass_declonly.cpp:34:30:34:35 | call to source | copyableclass_declonly.cpp:34:30:34:38 | call to MyCopyableClassDeclOnly | TAINT |
+| copyableclass_declonly.cpp:34:30:34:38 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:36:30:36:31 | s1 | |
+| copyableclass_declonly.cpp:34:30:34:38 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:40:8:40:9 | s1 | |
+| copyableclass_declonly.cpp:35:31:35:39 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:41:8:41:9 | s2 | |
+| copyableclass_declonly.cpp:35:32:35:37 | call to source | copyableclass_declonly.cpp:35:31:35:39 | call to MyCopyableClassDeclOnly | TAINT |
+| copyableclass_declonly.cpp:36:30:36:31 | s1 | copyableclass_declonly.cpp:36:30:36:32 | call to MyCopyableClassDeclOnly | |
+| copyableclass_declonly.cpp:36:30:36:32 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:42:8:42:9 | s3 | |
+| copyableclass_declonly.cpp:37:27:37:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:38:3:38:4 | s4 | |
+| copyableclass_declonly.cpp:37:27:37:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:43:8:43:9 | s4 | |
+| copyableclass_declonly.cpp:38:3:38:4 | ref arg s4 | copyableclass_declonly.cpp:43:8:43:9 | s4 | |
+| copyableclass_declonly.cpp:38:8:38:13 | call to source | copyableclass_declonly.cpp:38:8:38:15 | call to MyCopyableClassDeclOnly | TAINT |
+| copyableclass_declonly.cpp:38:8:38:15 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:38:3:38:4 | ref arg s4 | TAINT |
+| copyableclass_declonly.cpp:38:8:38:15 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:38:6:38:6 | call to operator= | TAINT |
+| copyableclass_declonly.cpp:47:27:47:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:48:32:48:33 | s1 | |
+| copyableclass_declonly.cpp:47:27:47:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:49:30:49:31 | s1 | |
+| copyableclass_declonly.cpp:47:27:47:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:51:8:51:9 | s1 | |
+| copyableclass_declonly.cpp:47:27:47:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:53:8:53:9 | s1 | |
+| copyableclass_declonly.cpp:48:31:48:33 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:54:8:54:9 | s2 | |
+| copyableclass_declonly.cpp:48:32:48:33 | s1 | copyableclass_declonly.cpp:48:31:48:33 | call to MyCopyableClassDeclOnly | |
+| copyableclass_declonly.cpp:49:30:49:31 | s1 | copyableclass_declonly.cpp:49:30:49:32 | call to MyCopyableClassDeclOnly | |
+| copyableclass_declonly.cpp:49:30:49:32 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:55:8:55:9 | s3 | |
+| copyableclass_declonly.cpp:50:27:50:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:51:3:51:4 | s4 | |
+| copyableclass_declonly.cpp:50:27:50:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:56:8:56:9 | s4 | |
+| copyableclass_declonly.cpp:51:3:51:4 | ref arg s4 | copyableclass_declonly.cpp:56:8:56:9 | s4 | |
+| copyableclass_declonly.cpp:51:8:51:9 | s1 | copyableclass_declonly.cpp:51:3:51:4 | ref arg s4 | TAINT |
+| copyableclass_declonly.cpp:51:8:51:9 | s1 | copyableclass_declonly.cpp:51:6:51:6 | call to operator= | TAINT |
+| copyableclass_declonly.cpp:60:31:60:64 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:65:8:65:9 | s1 | |
+| copyableclass_declonly.cpp:60:56:60:61 | call to source | copyableclass_declonly.cpp:60:31:60:64 | call to MyCopyableClassDeclOnly | TAINT |
+| copyableclass_declonly.cpp:61:27:61:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:63:3:63:4 | s2 | |
+| copyableclass_declonly.cpp:61:27:61:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:66:8:66:9 | s2 | |
+| copyableclass_declonly.cpp:62:27:62:28 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:67:8:67:9 | s3 | |
+| copyableclass_declonly.cpp:63:3:63:4 | ref arg s2 | copyableclass_declonly.cpp:66:8:66:9 | s2 | |
+| copyableclass_declonly.cpp:63:8:63:40 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:63:3:63:4 | ref arg s2 | TAINT |
+| copyableclass_declonly.cpp:63:8:63:40 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:63:6:63:6 | call to operator= | TAINT |
+| copyableclass_declonly.cpp:63:32:63:37 | call to source | copyableclass_declonly.cpp:63:8:63:40 | call to MyCopyableClassDeclOnly | TAINT |
+| copyableclass_declonly.cpp:67:13:67:18 | call to source | copyableclass_declonly.cpp:67:13:67:20 | call to MyCopyableClassDeclOnly | TAINT |
+| copyableclass_declonly.cpp:67:13:67:20 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:67:8:67:9 | ref arg s3 | TAINT |
+| copyableclass_declonly.cpp:67:13:67:20 | call to MyCopyableClassDeclOnly | copyableclass_declonly.cpp:67:11:67:11 | call to operator= | TAINT |
+| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | |
+| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | |
+| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | |
+| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | |
| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | |
| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | |
| file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | |
@@ -89,6 +346,8 @@
| format.cpp:113:21:113:24 | {...} | format.cpp:115:8:115:13 | buffer | |
| format.cpp:113:23:113:23 | 0 | format.cpp:113:21:113:24 | {...} | TAINT |
| format.cpp:114:18:114:23 | ref arg buffer | format.cpp:115:8:115:13 | buffer | |
+| format.cpp:114:31:114:34 | %s | format.cpp:114:18:114:23 | ref arg buffer | TAINT |
+| format.cpp:114:37:114:50 | call to source | format.cpp:114:18:114:23 | ref arg buffer | TAINT |
| format.cpp:119:10:119:11 | 0 | format.cpp:120:29:120:29 | i | |
| format.cpp:119:10:119:11 | 0 | format.cpp:121:8:121:8 | i | |
| format.cpp:120:28:120:29 | ref arg & ... | format.cpp:120:29:120:29 | i [inner post update] | |
@@ -131,63 +390,4921 @@
| format.cpp:158:13:158:18 | call to wcslen | format.cpp:158:13:158:26 | ... / ... | TAINT |
| format.cpp:158:13:158:26 | ... / ... | format.cpp:158:7:158:27 | ... + ... | TAINT |
| format.cpp:158:26:158:26 | 2 | format.cpp:158:13:158:26 | ... / ... | TAINT |
-| stl.cpp:67:12:67:17 | call to source | stl.cpp:71:7:71:7 | a | |
-| stl.cpp:68:16:68:20 | 123 | stl.cpp:68:16:68:21 | call to basic_string | TAINT |
-| stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:72:7:72:7 | b | |
-| stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:74:7:74:7 | b | |
-| stl.cpp:69:16:69:21 | call to source | stl.cpp:69:16:69:24 | call to basic_string | TAINT |
-| stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:73:7:73:7 | c | |
-| stl.cpp:69:16:69:24 | call to basic_string | stl.cpp:75:7:75:7 | c | |
-| stl.cpp:74:7:74:7 | b | stl.cpp:74:9:74:13 | call to c_str | TAINT |
-| stl.cpp:75:7:75:7 | c | stl.cpp:75:9:75:13 | call to c_str | TAINT |
-| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:83:2:83:4 | ss1 | |
-| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:89:7:89:9 | ss1 | |
-| stl.cpp:80:20:80:22 | call to basic_stringstream | stl.cpp:94:7:94:9 | ss1 | |
-| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:84:2:84:4 | ss2 | |
-| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:90:7:90:9 | ss2 | |
-| stl.cpp:80:25:80:27 | call to basic_stringstream | stl.cpp:95:7:95:9 | ss2 | |
-| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:85:2:85:4 | ss3 | |
-| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:91:7:91:9 | ss3 | |
-| stl.cpp:80:30:80:32 | call to basic_stringstream | stl.cpp:96:7:96:9 | ss3 | |
-| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:86:2:86:4 | ss4 | |
-| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:92:7:92:9 | ss4 | |
-| stl.cpp:80:35:80:37 | call to basic_stringstream | stl.cpp:97:7:97:9 | ss4 | |
-| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:87:2:87:4 | ss5 | |
-| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:93:7:93:9 | ss5 | |
-| stl.cpp:80:40:80:42 | call to basic_stringstream | stl.cpp:98:7:98:9 | ss5 | |
-| stl.cpp:81:16:81:21 | call to source | stl.cpp:81:16:81:24 | call to basic_string | TAINT |
-| stl.cpp:81:16:81:24 | call to basic_string | stl.cpp:87:9:87:9 | t | |
-| stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:89:7:89:9 | ss1 | |
-| stl.cpp:83:2:83:4 | ref arg ss1 | stl.cpp:94:7:94:9 | ss1 | |
-| stl.cpp:84:2:84:4 | ref arg ss2 | stl.cpp:90:7:90:9 | ss2 | |
-| stl.cpp:84:2:84:4 | ref arg ss2 | stl.cpp:95:7:95:9 | ss2 | |
-| stl.cpp:85:2:85:4 | ref arg ss3 | stl.cpp:91:7:91:9 | ss3 | |
-| stl.cpp:85:2:85:4 | ref arg ss3 | stl.cpp:96:7:96:9 | ss3 | |
-| stl.cpp:86:2:86:4 | ref arg ss4 | stl.cpp:92:7:92:9 | ss4 | |
-| stl.cpp:86:2:86:4 | ref arg ss4 | stl.cpp:97:7:97:9 | ss4 | |
-| stl.cpp:87:2:87:4 | ref arg ss5 | stl.cpp:93:7:93:9 | ss5 | |
-| stl.cpp:87:2:87:4 | ref arg ss5 | stl.cpp:98:7:98:9 | ss5 | |
-| stl.cpp:101:32:101:37 | source | stl.cpp:106:9:106:14 | source | |
-| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:105:2:105:4 | ss1 | |
-| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:108:7:108:9 | ss1 | |
-| stl.cpp:103:20:103:22 | call to basic_stringstream | stl.cpp:110:7:110:9 | ss1 | |
-| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:106:2:106:4 | ss2 | |
-| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:109:7:109:9 | ss2 | |
-| stl.cpp:103:25:103:27 | call to basic_stringstream | stl.cpp:111:7:111:9 | ss2 | |
-| stl.cpp:105:2:105:4 | ref arg ss1 | stl.cpp:108:7:108:9 | ss1 | |
-| stl.cpp:105:2:105:4 | ref arg ss1 | stl.cpp:110:7:110:9 | ss1 | |
-| stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:109:7:109:9 | ss2 | |
-| stl.cpp:106:2:106:4 | ref arg ss2 | stl.cpp:111:7:111:9 | ss2 | |
-| stl.cpp:124:16:124:28 | call to basic_string | stl.cpp:125:7:125:11 | path1 | |
-| stl.cpp:124:17:124:26 | call to user_input | stl.cpp:124:16:124:28 | call to basic_string | TAINT |
-| stl.cpp:125:7:125:11 | path1 | stl.cpp:125:13:125:17 | call to c_str | TAINT |
-| stl.cpp:128:10:128:19 | call to user_input | stl.cpp:128:10:128:21 | call to basic_string | TAINT |
-| stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:128:2:128:21 | ... = ... | |
-| stl.cpp:128:10:128:21 | call to basic_string | stl.cpp:129:7:129:11 | path2 | |
-| stl.cpp:129:7:129:11 | path2 | stl.cpp:129:13:129:17 | call to c_str | TAINT |
-| stl.cpp:131:15:131:24 | call to user_input | stl.cpp:131:15:131:27 | call to basic_string | TAINT |
-| stl.cpp:131:15:131:27 | call to basic_string | stl.cpp:132:7:132:11 | path3 | |
-| stl.cpp:132:7:132:11 | path3 | stl.cpp:132:13:132:17 | call to c_str | TAINT |
+| map.cpp:21:28:21:28 | call to pair | map.cpp:23:2:23:2 | a | |
+| map.cpp:21:28:21:28 | call to pair | map.cpp:24:7:24:7 | a | |
+| map.cpp:21:28:21:28 | call to pair | map.cpp:25:7:25:7 | a | |
+| map.cpp:21:28:21:28 | call to pair | map.cpp:26:7:26:7 | a | |
+| map.cpp:21:31:21:31 | call to pair | map.cpp:28:2:28:2 | b | |
+| map.cpp:21:31:21:31 | call to pair | map.cpp:29:7:29:7 | b | |
+| map.cpp:21:31:21:31 | call to pair | map.cpp:30:7:30:7 | b | |
+| map.cpp:21:31:21:31 | call to pair | map.cpp:31:7:31:7 | b | |
+| map.cpp:21:34:21:34 | call to pair | map.cpp:33:2:33:2 | c | |
+| map.cpp:21:34:21:34 | call to pair | map.cpp:34:7:34:7 | c | |
+| map.cpp:21:34:21:34 | call to pair | map.cpp:35:7:35:7 | c | |
+| map.cpp:21:34:21:34 | call to pair | map.cpp:36:7:36:7 | c | |
+| map.cpp:23:2:23:2 | a [post update] | map.cpp:24:7:24:7 | a | |
+| map.cpp:23:2:23:2 | a [post update] | map.cpp:25:7:25:7 | a | |
+| map.cpp:23:2:23:2 | a [post update] | map.cpp:26:7:26:7 | a | |
+| map.cpp:23:2:23:16 | ... = ... | map.cpp:23:4:23:8 | first [post update] | |
+| map.cpp:23:2:23:16 | ... = ... | map.cpp:24:9:24:13 | first | |
+| map.cpp:23:12:23:16 | 123 | map.cpp:23:2:23:16 | ... = ... | |
+| map.cpp:24:7:24:7 | a [post update] | map.cpp:25:7:25:7 | a | |
+| map.cpp:24:7:24:7 | a [post update] | map.cpp:26:7:26:7 | a | |
+| map.cpp:25:7:25:7 | a [post update] | map.cpp:26:7:26:7 | a | |
+| map.cpp:28:2:28:2 | b [post update] | map.cpp:29:7:29:7 | b | |
+| map.cpp:28:2:28:2 | b [post update] | map.cpp:30:7:30:7 | b | |
+| map.cpp:28:2:28:2 | b [post update] | map.cpp:31:7:31:7 | b | |
+| map.cpp:28:2:28:19 | ... = ... | map.cpp:28:4:28:8 | first [post update] | |
+| map.cpp:28:2:28:19 | ... = ... | map.cpp:29:9:29:13 | first | |
+| map.cpp:28:12:28:17 | call to source | map.cpp:28:2:28:19 | ... = ... | |
+| map.cpp:29:7:29:7 | b [post update] | map.cpp:30:7:30:7 | b | |
+| map.cpp:29:7:29:7 | b [post update] | map.cpp:31:7:31:7 | b | |
+| map.cpp:30:7:30:7 | b [post update] | map.cpp:31:7:31:7 | b | |
+| map.cpp:33:2:33:2 | c [post update] | map.cpp:34:7:34:7 | c | |
+| map.cpp:33:2:33:2 | c [post update] | map.cpp:35:7:35:7 | c | |
+| map.cpp:33:2:33:2 | c [post update] | map.cpp:36:7:36:7 | c | |
+| map.cpp:33:2:33:20 | ... = ... | map.cpp:33:4:33:9 | second [post update] | |
+| map.cpp:33:2:33:20 | ... = ... | map.cpp:35:9:35:14 | second | |
+| map.cpp:33:13:33:18 | call to source | map.cpp:33:2:33:20 | ... = ... | |
+| map.cpp:34:7:34:7 | c [post update] | map.cpp:35:7:35:7 | c | |
+| map.cpp:34:7:34:7 | c [post update] | map.cpp:36:7:36:7 | c | |
+| map.cpp:35:7:35:7 | c [post update] | map.cpp:36:7:36:7 | c | |
+| map.cpp:38:30:38:42 | call to pair | map.cpp:39:7:39:7 | d | |
+| map.cpp:38:30:38:42 | call to pair | map.cpp:40:7:40:7 | d | |
+| map.cpp:38:30:38:42 | call to pair | map.cpp:41:7:41:7 | d | |
+| map.cpp:38:37:38:41 | 456 | map.cpp:38:30:38:42 | call to pair | TAINT |
+| map.cpp:39:7:39:7 | d [post update] | map.cpp:40:7:40:7 | d | |
+| map.cpp:39:7:39:7 | d [post update] | map.cpp:41:7:41:7 | d | |
+| map.cpp:40:7:40:7 | d [post update] | map.cpp:41:7:41:7 | d | |
+| map.cpp:43:30:43:45 | call to pair | map.cpp:44:7:44:7 | e | |
+| map.cpp:43:30:43:45 | call to pair | map.cpp:45:7:45:7 | e | |
+| map.cpp:43:30:43:45 | call to pair | map.cpp:46:7:46:7 | e | |
+| map.cpp:43:40:43:44 | 456 | map.cpp:43:30:43:45 | call to pair | TAINT |
+| map.cpp:44:7:44:7 | e [post update] | map.cpp:45:7:45:7 | e | |
+| map.cpp:44:7:44:7 | e [post update] | map.cpp:46:7:46:7 | e | |
+| map.cpp:45:7:45:7 | e [post update] | map.cpp:46:7:46:7 | e | |
+| map.cpp:48:30:48:45 | call to pair | map.cpp:49:7:49:7 | f | |
+| map.cpp:48:30:48:45 | call to pair | map.cpp:50:7:50:7 | f | |
+| map.cpp:48:30:48:45 | call to pair | map.cpp:51:7:51:7 | f | |
+| map.cpp:48:30:48:45 | call to pair | map.cpp:53:30:53:30 | f | |
+| map.cpp:48:30:48:45 | call to pair | map.cpp:59:6:59:6 | f | |
+| map.cpp:48:37:48:42 | call to source | map.cpp:48:30:48:45 | call to pair | TAINT |
+| map.cpp:49:7:49:7 | f [post update] | map.cpp:50:7:50:7 | f | |
+| map.cpp:49:7:49:7 | f [post update] | map.cpp:51:7:51:7 | f | |
+| map.cpp:49:7:49:7 | f [post update] | map.cpp:53:30:53:30 | f | |
+| map.cpp:49:7:49:7 | f [post update] | map.cpp:59:6:59:6 | f | |
+| map.cpp:50:7:50:7 | f [post update] | map.cpp:51:7:51:7 | f | |
+| map.cpp:50:7:50:7 | f [post update] | map.cpp:53:30:53:30 | f | |
+| map.cpp:50:7:50:7 | f [post update] | map.cpp:59:6:59:6 | f | |
+| map.cpp:53:30:53:30 | f | map.cpp:54:7:54:7 | g | |
+| map.cpp:53:30:53:30 | f | map.cpp:55:7:55:7 | g | |
+| map.cpp:53:30:53:30 | f | map.cpp:56:7:56:7 | g | |
+| map.cpp:54:7:54:7 | g [post update] | map.cpp:55:7:55:7 | g | |
+| map.cpp:54:7:54:7 | g [post update] | map.cpp:56:7:56:7 | g | |
+| map.cpp:55:7:55:7 | g [post update] | map.cpp:56:7:56:7 | g | |
+| map.cpp:59:6:59:6 | f | map.cpp:59:2:59:6 | ... = ... | |
+| map.cpp:59:6:59:6 | f | map.cpp:60:7:60:7 | h | |
+| map.cpp:59:6:59:6 | f | map.cpp:61:7:61:7 | h | |
+| map.cpp:59:6:59:6 | f | map.cpp:62:7:62:7 | h | |
+| map.cpp:60:7:60:7 | h [post update] | map.cpp:61:7:61:7 | h | |
+| map.cpp:60:7:60:7 | h [post update] | map.cpp:62:7:62:7 | h | |
+| map.cpp:61:7:61:7 | h [post update] | map.cpp:62:7:62:7 | h | |
+| map.cpp:64:30:64:42 | call to pair | map.cpp:68:3:68:3 | i | |
+| map.cpp:64:30:64:42 | call to pair | map.cpp:70:7:70:7 | i | |
+| map.cpp:64:30:64:42 | call to pair | map.cpp:71:7:71:7 | i | |
+| map.cpp:64:30:64:42 | call to pair | map.cpp:72:7:72:7 | i | |
+| map.cpp:64:37:64:41 | 456 | map.cpp:64:30:64:42 | call to pair | TAINT |
+| map.cpp:65:30:65:45 | call to pair | map.cpp:68:10:68:10 | j | |
+| map.cpp:65:30:65:45 | call to pair | map.cpp:73:7:73:7 | j | |
+| map.cpp:65:30:65:45 | call to pair | map.cpp:74:7:74:7 | j | |
+| map.cpp:65:30:65:45 | call to pair | map.cpp:75:7:75:7 | j | |
+| map.cpp:65:37:65:42 | call to source | map.cpp:65:30:65:45 | call to pair | TAINT |
+| map.cpp:66:30:66:45 | call to pair | map.cpp:69:2:69:2 | k | |
+| map.cpp:66:30:66:45 | call to pair | map.cpp:76:7:76:7 | k | |
+| map.cpp:66:30:66:45 | call to pair | map.cpp:77:7:77:7 | k | |
+| map.cpp:66:30:66:45 | call to pair | map.cpp:78:7:78:7 | k | |
+| map.cpp:66:37:66:42 | call to source | map.cpp:66:30:66:45 | call to pair | TAINT |
+| map.cpp:67:30:67:42 | call to pair | map.cpp:69:9:69:9 | l | |
+| map.cpp:67:30:67:42 | call to pair | map.cpp:79:7:79:7 | l | |
+| map.cpp:67:30:67:42 | call to pair | map.cpp:80:7:80:7 | l | |
+| map.cpp:67:30:67:42 | call to pair | map.cpp:81:7:81:7 | l | |
+| map.cpp:67:37:67:41 | 456 | map.cpp:67:30:67:42 | call to pair | TAINT |
+| map.cpp:68:3:68:3 | i | map.cpp:68:10:68:10 | ref arg j | TAINT |
+| map.cpp:68:3:68:3 | ref arg i | map.cpp:70:7:70:7 | i | |
+| map.cpp:68:3:68:3 | ref arg i | map.cpp:71:7:71:7 | i | |
+| map.cpp:68:3:68:3 | ref arg i | map.cpp:72:7:72:7 | i | |
+| map.cpp:68:10:68:10 | j | map.cpp:68:3:68:3 | ref arg i | TAINT |
+| map.cpp:68:10:68:10 | ref arg j | map.cpp:73:7:73:7 | j | |
+| map.cpp:68:10:68:10 | ref arg j | map.cpp:74:7:74:7 | j | |
+| map.cpp:68:10:68:10 | ref arg j | map.cpp:75:7:75:7 | j | |
+| map.cpp:69:2:69:2 | k | map.cpp:69:9:69:9 | ref arg l | TAINT |
+| map.cpp:69:2:69:2 | ref arg k | map.cpp:76:7:76:7 | k | |
+| map.cpp:69:2:69:2 | ref arg k | map.cpp:77:7:77:7 | k | |
+| map.cpp:69:2:69:2 | ref arg k | map.cpp:78:7:78:7 | k | |
+| map.cpp:69:9:69:9 | l | map.cpp:69:2:69:2 | ref arg k | TAINT |
+| map.cpp:69:9:69:9 | ref arg l | map.cpp:79:7:79:7 | l | |
+| map.cpp:69:9:69:9 | ref arg l | map.cpp:80:7:80:7 | l | |
+| map.cpp:69:9:69:9 | ref arg l | map.cpp:81:7:81:7 | l | |
+| map.cpp:70:7:70:7 | i [post update] | map.cpp:71:7:71:7 | i | |
+| map.cpp:70:7:70:7 | i [post update] | map.cpp:72:7:72:7 | i | |
+| map.cpp:71:7:71:7 | i [post update] | map.cpp:72:7:72:7 | i | |
+| map.cpp:73:7:73:7 | j [post update] | map.cpp:74:7:74:7 | j | |
+| map.cpp:73:7:73:7 | j [post update] | map.cpp:75:7:75:7 | j | |
+| map.cpp:74:7:74:7 | j [post update] | map.cpp:75:7:75:7 | j | |
+| map.cpp:76:7:76:7 | k [post update] | map.cpp:77:7:77:7 | k | |
+| map.cpp:76:7:76:7 | k [post update] | map.cpp:78:7:78:7 | k | |
+| map.cpp:77:7:77:7 | k [post update] | map.cpp:78:7:78:7 | k | |
+| map.cpp:79:7:79:7 | l [post update] | map.cpp:80:7:80:7 | l | |
+| map.cpp:79:7:79:7 | l [post update] | map.cpp:81:7:81:7 | l | |
+| map.cpp:80:7:80:7 | l [post update] | map.cpp:81:7:81:7 | l | |
+| map.cpp:83:7:83:15 | call to make_pair | map.cpp:83:7:83:29 | call to pair | TAINT |
+| map.cpp:86:7:86:15 | call to make_pair | map.cpp:86:7:86:32 | call to pair | TAINT |
+| map.cpp:89:7:89:15 | call to make_pair | map.cpp:89:7:89:32 | call to pair | TAINT |
+| map.cpp:94:6:94:14 | call to make_pair | map.cpp:94:6:94:49 | call to pair | TAINT |
+| map.cpp:94:6:94:49 | call to pair | map.cpp:94:2:94:49 | ... = ... | |
+| map.cpp:94:6:94:49 | call to pair | map.cpp:95:7:95:7 | m | |
+| map.cpp:94:6:94:49 | call to pair | map.cpp:96:7:96:7 | m | |
+| map.cpp:94:6:94:49 | call to pair | map.cpp:97:7:97:7 | m | |
+| map.cpp:94:6:94:49 | call to pair | map.cpp:98:7:98:7 | m | |
+| map.cpp:94:6:94:49 | call to pair | map.cpp:99:7:99:7 | m | |
+| map.cpp:95:7:95:7 | m | map.cpp:95:7:95:7 | call to pair | TAINT |
+| map.cpp:97:7:97:7 | m [post update] | map.cpp:98:7:98:7 | m | |
+| map.cpp:97:7:97:7 | m [post update] | map.cpp:99:7:99:7 | m | |
+| map.cpp:98:7:98:7 | m [post update] | map.cpp:99:7:99:7 | m | |
+| map.cpp:105:27:105:28 | call to map | map.cpp:107:7:107:8 | m1 | |
+| map.cpp:105:27:105:28 | call to map | map.cpp:113:7:113:8 | m1 | |
+| map.cpp:105:27:105:28 | call to map | map.cpp:119:7:119:8 | m1 | |
+| map.cpp:105:27:105:28 | call to map | map.cpp:125:7:125:8 | m1 | |
+| map.cpp:105:27:105:28 | call to map | map.cpp:146:12:146:13 | m1 | |
+| map.cpp:105:27:105:28 | call to map | map.cpp:146:30:146:31 | m1 | |
+| map.cpp:105:27:105:28 | call to map | map.cpp:252:1:252:1 | m1 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:108:7:108:8 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:114:7:114:8 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:120:7:120:8 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:126:7:126:8 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:133:30:133:31 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:134:32:134:33 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:136:7:136:8 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:152:12:152:13 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:152:30:152:31 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:182:7:182:8 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:183:7:183:8 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:184:7:184:8 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:185:7:185:8 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:186:7:186:8 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:187:7:187:8 | m2 | |
+| map.cpp:105:31:105:32 | call to map | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:105:35:105:36 | call to map | map.cpp:109:7:109:8 | m3 | |
+| map.cpp:105:35:105:36 | call to map | map.cpp:115:7:115:8 | m3 | |
+| map.cpp:105:35:105:36 | call to map | map.cpp:121:7:121:8 | m3 | |
+| map.cpp:105:35:105:36 | call to map | map.cpp:127:7:127:8 | m3 | |
+| map.cpp:105:35:105:36 | call to map | map.cpp:158:12:158:13 | m3 | |
+| map.cpp:105:35:105:36 | call to map | map.cpp:158:30:158:31 | m3 | |
+| map.cpp:105:35:105:36 | call to map | map.cpp:252:1:252:1 | m3 | |
+| map.cpp:105:39:105:40 | call to map | map.cpp:110:7:110:8 | m4 | |
+| map.cpp:105:39:105:40 | call to map | map.cpp:110:17:110:18 | m4 | |
+| map.cpp:105:39:105:40 | call to map | map.cpp:116:7:116:8 | m4 | |
+| map.cpp:105:39:105:40 | call to map | map.cpp:122:7:122:8 | m4 | |
+| map.cpp:105:39:105:40 | call to map | map.cpp:128:7:128:8 | m4 | |
+| map.cpp:105:39:105:40 | call to map | map.cpp:252:1:252:1 | m4 | |
+| map.cpp:105:43:105:44 | call to map | map.cpp:111:7:111:8 | m5 | |
+| map.cpp:105:43:105:44 | call to map | map.cpp:117:7:117:8 | m5 | |
+| map.cpp:105:43:105:44 | call to map | map.cpp:123:7:123:8 | m5 | |
+| map.cpp:105:43:105:44 | call to map | map.cpp:129:7:129:8 | m5 | |
+| map.cpp:105:43:105:44 | call to map | map.cpp:252:1:252:1 | m5 | |
+| map.cpp:105:47:105:48 | call to map | map.cpp:112:7:112:8 | m6 | |
+| map.cpp:105:47:105:48 | call to map | map.cpp:112:27:112:28 | m6 | |
+| map.cpp:105:47:105:48 | call to map | map.cpp:118:7:118:8 | m6 | |
+| map.cpp:105:47:105:48 | call to map | map.cpp:124:7:124:8 | m6 | |
+| map.cpp:105:47:105:48 | call to map | map.cpp:130:7:130:8 | m6 | |
+| map.cpp:105:47:105:48 | call to map | map.cpp:252:1:252:1 | m6 | |
+| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:113:7:113:8 | m1 | |
+| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:119:7:119:8 | m1 | |
+| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:125:7:125:8 | m1 | |
+| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:146:12:146:13 | m1 | |
+| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:146:30:146:31 | m1 | |
+| map.cpp:107:7:107:8 | ref arg m1 | map.cpp:252:1:252:1 | m1 | |
+| map.cpp:107:17:107:30 | call to make_pair | map.cpp:107:17:107:44 | call to pair | TAINT |
+| map.cpp:107:17:107:44 | call to pair | map.cpp:107:7:107:8 | ref arg m1 | TAINT |
+| map.cpp:107:17:107:44 | call to pair | map.cpp:107:10:107:15 | call to insert | TAINT |
+| map.cpp:107:47:107:51 | first | map.cpp:107:7:107:51 | call to iterator | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:114:7:114:8 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:120:7:120:8 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:126:7:126:8 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:133:30:133:31 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:134:32:134:33 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:136:7:136:8 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:152:12:152:13 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:152:30:152:31 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:182:7:182:8 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
+| map.cpp:108:7:108:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:108:17:108:30 | call to make_pair | map.cpp:108:17:108:47 | call to pair | TAINT |
+| map.cpp:108:17:108:47 | call to pair | map.cpp:108:7:108:8 | ref arg m2 | TAINT |
+| map.cpp:108:17:108:47 | call to pair | map.cpp:108:10:108:15 | call to insert | TAINT |
+| map.cpp:108:50:108:54 | first | map.cpp:108:7:108:54 | call to iterator | |
+| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:115:7:115:8 | m3 | |
+| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:121:7:121:8 | m3 | |
+| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:127:7:127:8 | m3 | |
+| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:158:12:158:13 | m3 | |
+| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:158:30:158:31 | m3 | |
+| map.cpp:109:7:109:8 | ref arg m3 | map.cpp:252:1:252:1 | m3 | |
+| map.cpp:109:17:109:30 | call to make_pair | map.cpp:109:17:109:47 | call to pair | TAINT |
+| map.cpp:109:17:109:47 | call to pair | map.cpp:109:7:109:8 | ref arg m3 | TAINT |
+| map.cpp:109:17:109:47 | call to pair | map.cpp:109:10:109:15 | call to insert | TAINT |
+| map.cpp:109:50:109:54 | first | map.cpp:109:7:109:54 | call to iterator | |
+| map.cpp:110:7:110:8 | ref arg m4 | map.cpp:116:7:116:8 | m4 | |
+| map.cpp:110:7:110:8 | ref arg m4 | map.cpp:122:7:122:8 | m4 | |
+| map.cpp:110:7:110:8 | ref arg m4 | map.cpp:128:7:128:8 | m4 | |
+| map.cpp:110:7:110:8 | ref arg m4 | map.cpp:252:1:252:1 | m4 | |
+| map.cpp:110:17:110:18 | m4 | map.cpp:110:20:110:24 | call to begin | TAINT |
+| map.cpp:110:17:110:18 | ref arg m4 | map.cpp:110:7:110:8 | m4 | |
+| map.cpp:110:17:110:18 | ref arg m4 | map.cpp:116:7:116:8 | m4 | |
+| map.cpp:110:17:110:18 | ref arg m4 | map.cpp:122:7:122:8 | m4 | |
+| map.cpp:110:17:110:18 | ref arg m4 | map.cpp:128:7:128:8 | m4 | |
+| map.cpp:110:17:110:18 | ref arg m4 | map.cpp:252:1:252:1 | m4 | |
+| map.cpp:110:20:110:24 | call to begin | map.cpp:110:17:110:26 | call to iterator | TAINT |
+| map.cpp:110:29:110:70 | call to pair | map.cpp:110:7:110:8 | ref arg m4 | TAINT |
+| map.cpp:110:29:110:70 | call to pair | map.cpp:110:10:110:15 | call to insert | TAINT |
+| map.cpp:110:29:110:70 | call to pair | map.cpp:110:29:110:70 | call to pair | TAINT |
+| map.cpp:110:62:110:67 | call to source | map.cpp:110:29:110:70 | call to pair | TAINT |
+| map.cpp:111:7:111:8 | ref arg m5 | map.cpp:117:7:117:8 | m5 | |
+| map.cpp:111:7:111:8 | ref arg m5 | map.cpp:123:7:123:8 | m5 | |
+| map.cpp:111:7:111:8 | ref arg m5 | map.cpp:129:7:129:8 | m5 | |
+| map.cpp:111:7:111:8 | ref arg m5 | map.cpp:252:1:252:1 | m5 | |
+| map.cpp:111:34:111:39 | call to source | map.cpp:111:7:111:8 | ref arg m5 | TAINT |
+| map.cpp:111:34:111:39 | call to source | map.cpp:111:10:111:25 | call to insert_or_assign | TAINT |
+| map.cpp:111:44:111:48 | first | map.cpp:111:7:111:48 | call to iterator | |
+| map.cpp:112:7:112:8 | ref arg m6 | map.cpp:118:7:118:8 | m6 | |
+| map.cpp:112:7:112:8 | ref arg m6 | map.cpp:124:7:124:8 | m6 | |
+| map.cpp:112:7:112:8 | ref arg m6 | map.cpp:130:7:130:8 | m6 | |
+| map.cpp:112:7:112:8 | ref arg m6 | map.cpp:252:1:252:1 | m6 | |
+| map.cpp:112:27:112:28 | m6 | map.cpp:112:30:112:34 | call to begin | TAINT |
+| map.cpp:112:27:112:28 | ref arg m6 | map.cpp:112:7:112:8 | m6 | |
+| map.cpp:112:27:112:28 | ref arg m6 | map.cpp:118:7:118:8 | m6 | |
+| map.cpp:112:27:112:28 | ref arg m6 | map.cpp:124:7:124:8 | m6 | |
+| map.cpp:112:27:112:28 | ref arg m6 | map.cpp:130:7:130:8 | m6 | |
+| map.cpp:112:27:112:28 | ref arg m6 | map.cpp:252:1:252:1 | m6 | |
+| map.cpp:112:30:112:34 | call to begin | map.cpp:112:27:112:36 | call to iterator | TAINT |
+| map.cpp:112:46:112:51 | call to source | map.cpp:112:7:112:8 | ref arg m6 | TAINT |
+| map.cpp:112:46:112:51 | call to source | map.cpp:112:10:112:25 | call to insert_or_assign | TAINT |
+| map.cpp:113:7:113:8 | m1 | map.cpp:113:7:113:8 | call to map | |
+| map.cpp:114:7:114:8 | m2 | map.cpp:114:7:114:8 | call to map | |
+| map.cpp:115:7:115:8 | m3 | map.cpp:115:7:115:8 | call to map | |
+| map.cpp:116:7:116:8 | m4 | map.cpp:116:7:116:8 | call to map | |
+| map.cpp:117:7:117:8 | m5 | map.cpp:117:7:117:8 | call to map | |
+| map.cpp:118:7:118:8 | m6 | map.cpp:118:7:118:8 | call to map | |
+| map.cpp:119:7:119:8 | m1 | map.cpp:119:10:119:13 | call to find | TAINT |
+| map.cpp:119:7:119:8 | ref arg m1 | map.cpp:125:7:125:8 | m1 | |
+| map.cpp:119:7:119:8 | ref arg m1 | map.cpp:146:12:146:13 | m1 | |
+| map.cpp:119:7:119:8 | ref arg m1 | map.cpp:146:30:146:31 | m1 | |
+| map.cpp:119:7:119:8 | ref arg m1 | map.cpp:252:1:252:1 | m1 | |
+| map.cpp:120:7:120:8 | m2 | map.cpp:120:10:120:13 | call to find | TAINT |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:126:7:126:8 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:133:30:133:31 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:134:32:134:33 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:136:7:136:8 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:152:12:152:13 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:152:30:152:31 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:182:7:182:8 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
+| map.cpp:120:7:120:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:121:7:121:8 | m3 | map.cpp:121:10:121:13 | call to find | TAINT |
+| map.cpp:121:7:121:8 | ref arg m3 | map.cpp:127:7:127:8 | m3 | |
+| map.cpp:121:7:121:8 | ref arg m3 | map.cpp:158:12:158:13 | m3 | |
+| map.cpp:121:7:121:8 | ref arg m3 | map.cpp:158:30:158:31 | m3 | |
+| map.cpp:121:7:121:8 | ref arg m3 | map.cpp:252:1:252:1 | m3 | |
+| map.cpp:122:7:122:8 | m4 | map.cpp:122:10:122:13 | call to find | TAINT |
+| map.cpp:122:7:122:8 | ref arg m4 | map.cpp:128:7:128:8 | m4 | |
+| map.cpp:122:7:122:8 | ref arg m4 | map.cpp:252:1:252:1 | m4 | |
+| map.cpp:123:7:123:8 | m5 | map.cpp:123:10:123:13 | call to find | TAINT |
+| map.cpp:123:7:123:8 | ref arg m5 | map.cpp:129:7:129:8 | m5 | |
+| map.cpp:123:7:123:8 | ref arg m5 | map.cpp:252:1:252:1 | m5 | |
+| map.cpp:124:7:124:8 | m6 | map.cpp:124:10:124:13 | call to find | TAINT |
+| map.cpp:124:7:124:8 | ref arg m6 | map.cpp:130:7:130:8 | m6 | |
+| map.cpp:124:7:124:8 | ref arg m6 | map.cpp:252:1:252:1 | m6 | |
+| map.cpp:125:7:125:8 | m1 | map.cpp:125:10:125:13 | call to find | TAINT |
+| map.cpp:125:7:125:8 | ref arg m1 | map.cpp:146:12:146:13 | m1 | |
+| map.cpp:125:7:125:8 | ref arg m1 | map.cpp:146:30:146:31 | m1 | |
+| map.cpp:125:7:125:8 | ref arg m1 | map.cpp:252:1:252:1 | m1 | |
+| map.cpp:126:7:126:8 | m2 | map.cpp:126:10:126:13 | call to find | TAINT |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:133:30:133:31 | m2 | |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:134:32:134:33 | m2 | |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:136:7:136:8 | m2 | |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:152:12:152:13 | m2 | |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:152:30:152:31 | m2 | |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:182:7:182:8 | m2 | |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
+| map.cpp:126:7:126:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:127:7:127:8 | m3 | map.cpp:127:10:127:13 | call to find | TAINT |
+| map.cpp:127:7:127:8 | ref arg m3 | map.cpp:158:12:158:13 | m3 | |
+| map.cpp:127:7:127:8 | ref arg m3 | map.cpp:158:30:158:31 | m3 | |
+| map.cpp:127:7:127:8 | ref arg m3 | map.cpp:252:1:252:1 | m3 | |
+| map.cpp:128:7:128:8 | m4 | map.cpp:128:10:128:13 | call to find | TAINT |
+| map.cpp:128:7:128:8 | ref arg m4 | map.cpp:252:1:252:1 | m4 | |
+| map.cpp:129:7:129:8 | m5 | map.cpp:129:10:129:13 | call to find | TAINT |
+| map.cpp:129:7:129:8 | ref arg m5 | map.cpp:252:1:252:1 | m5 | |
+| map.cpp:130:7:130:8 | m6 | map.cpp:130:10:130:13 | call to find | TAINT |
+| map.cpp:130:7:130:8 | ref arg m6 | map.cpp:252:1:252:1 | m6 | |
+| map.cpp:133:30:133:31 | m2 | map.cpp:133:30:133:32 | call to map | |
+| map.cpp:133:30:133:32 | call to map | map.cpp:137:7:137:8 | m7 | |
+| map.cpp:133:30:133:32 | call to map | map.cpp:140:7:140:8 | m7 | |
+| map.cpp:133:30:133:32 | call to map | map.cpp:252:1:252:1 | m7 | |
+| map.cpp:134:31:134:33 | call to map | map.cpp:138:7:138:8 | m8 | |
+| map.cpp:134:31:134:33 | call to map | map.cpp:141:7:141:8 | m8 | |
+| map.cpp:134:31:134:33 | call to map | map.cpp:252:1:252:1 | m8 | |
+| map.cpp:134:32:134:33 | m2 | map.cpp:134:31:134:33 | call to map | |
+| map.cpp:135:27:135:28 | call to map | map.cpp:136:2:136:3 | m9 | |
+| map.cpp:135:27:135:28 | call to map | map.cpp:139:7:139:8 | m9 | |
+| map.cpp:135:27:135:28 | call to map | map.cpp:142:7:142:8 | m9 | |
+| map.cpp:135:27:135:28 | call to map | map.cpp:252:1:252:1 | m9 | |
+| map.cpp:136:2:136:3 | ref arg m9 | map.cpp:139:7:139:8 | m9 | |
+| map.cpp:136:2:136:3 | ref arg m9 | map.cpp:142:7:142:8 | m9 | |
+| map.cpp:136:2:136:3 | ref arg m9 | map.cpp:252:1:252:1 | m9 | |
+| map.cpp:136:7:136:8 | m2 | map.cpp:136:2:136:3 | ref arg m9 | TAINT |
+| map.cpp:136:7:136:8 | m2 | map.cpp:136:5:136:5 | call to operator= | TAINT |
+| map.cpp:137:7:137:8 | m7 | map.cpp:137:7:137:8 | call to map | |
+| map.cpp:138:7:138:8 | m8 | map.cpp:138:7:138:8 | call to map | |
+| map.cpp:139:7:139:8 | m9 | map.cpp:139:7:139:8 | call to map | |
+| map.cpp:140:7:140:8 | m7 | map.cpp:140:10:140:13 | call to find | TAINT |
+| map.cpp:140:7:140:8 | ref arg m7 | map.cpp:252:1:252:1 | m7 | |
+| map.cpp:141:7:141:8 | m8 | map.cpp:141:10:141:13 | call to find | TAINT |
+| map.cpp:141:7:141:8 | ref arg m8 | map.cpp:252:1:252:1 | m8 | |
+| map.cpp:142:7:142:8 | m9 | map.cpp:142:10:142:13 | call to find | TAINT |
+| map.cpp:142:7:142:8 | ref arg m9 | map.cpp:252:1:252:1 | m9 | |
+| map.cpp:146:12:146:13 | m1 | map.cpp:146:15:146:19 | call to begin | TAINT |
+| map.cpp:146:12:146:13 | ref arg m1 | map.cpp:146:30:146:31 | m1 | |
+| map.cpp:146:12:146:13 | ref arg m1 | map.cpp:252:1:252:1 | m1 | |
+| map.cpp:146:15:146:19 | call to begin | map.cpp:146:7:146:21 | ... = ... | |
+| map.cpp:146:15:146:19 | call to begin | map.cpp:146:24:146:25 | i1 | |
+| map.cpp:146:15:146:19 | call to begin | map.cpp:146:40:146:41 | i1 | |
+| map.cpp:146:15:146:19 | call to begin | map.cpp:148:9:148:10 | i1 | |
+| map.cpp:146:15:146:19 | call to begin | map.cpp:149:8:149:9 | i1 | |
+| map.cpp:146:15:146:19 | call to begin | map.cpp:150:8:150:9 | i1 | |
+| map.cpp:146:30:146:31 | m1 | map.cpp:146:33:146:35 | call to end | TAINT |
+| map.cpp:146:30:146:31 | ref arg m1 | map.cpp:146:30:146:31 | m1 | |
+| map.cpp:146:30:146:31 | ref arg m1 | map.cpp:252:1:252:1 | m1 | |
+| map.cpp:146:40:146:41 | i1 | map.cpp:146:42:146:42 | call to operator++ | |
+| map.cpp:146:40:146:41 | ref arg i1 | map.cpp:146:24:146:25 | i1 | |
+| map.cpp:146:40:146:41 | ref arg i1 | map.cpp:146:40:146:41 | i1 | |
+| map.cpp:146:40:146:41 | ref arg i1 | map.cpp:148:9:148:10 | i1 | |
+| map.cpp:146:40:146:41 | ref arg i1 | map.cpp:149:8:149:9 | i1 | |
+| map.cpp:146:40:146:41 | ref arg i1 | map.cpp:150:8:150:9 | i1 | |
+| map.cpp:148:8:148:8 | call to operator* | map.cpp:148:8:148:10 | call to pair | TAINT |
+| map.cpp:148:9:148:10 | i1 | map.cpp:148:8:148:8 | call to operator* | TAINT |
+| map.cpp:149:8:149:9 | i1 | map.cpp:149:10:149:10 | call to operator-> | TAINT |
+| map.cpp:150:8:150:9 | i1 | map.cpp:150:10:150:10 | call to operator-> | TAINT |
+| map.cpp:152:12:152:13 | m2 | map.cpp:152:15:152:19 | call to begin | TAINT |
+| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:152:30:152:31 | m2 | |
+| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:182:7:182:8 | m2 | |
+| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
+| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
+| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
+| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
+| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
+| map.cpp:152:12:152:13 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:152:15:152:19 | call to begin | map.cpp:152:7:152:21 | ... = ... | |
+| map.cpp:152:15:152:19 | call to begin | map.cpp:152:24:152:25 | i2 | |
+| map.cpp:152:15:152:19 | call to begin | map.cpp:152:40:152:41 | i2 | |
+| map.cpp:152:15:152:19 | call to begin | map.cpp:154:9:154:10 | i2 | |
+| map.cpp:152:15:152:19 | call to begin | map.cpp:155:8:155:9 | i2 | |
+| map.cpp:152:15:152:19 | call to begin | map.cpp:156:8:156:9 | i2 | |
+| map.cpp:152:15:152:19 | call to begin | map.cpp:161:8:161:9 | i2 | |
+| map.cpp:152:15:152:19 | call to begin | map.cpp:162:8:162:9 | i2 | |
+| map.cpp:152:30:152:31 | m2 | map.cpp:152:33:152:35 | call to end | TAINT |
+| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:152:30:152:31 | m2 | |
+| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:182:7:182:8 | m2 | |
+| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
+| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
+| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
+| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
+| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
+| map.cpp:152:30:152:31 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:152:40:152:41 | i2 | map.cpp:152:42:152:42 | call to operator++ | |
+| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:152:24:152:25 | i2 | |
+| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:152:40:152:41 | i2 | |
+| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:154:9:154:10 | i2 | |
+| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:155:8:155:9 | i2 | |
+| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:156:8:156:9 | i2 | |
+| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:161:8:161:9 | i2 | |
+| map.cpp:152:40:152:41 | ref arg i2 | map.cpp:162:8:162:9 | i2 | |
+| map.cpp:154:8:154:8 | call to operator* | map.cpp:154:8:154:10 | call to pair | TAINT |
+| map.cpp:154:9:154:10 | i2 | map.cpp:154:8:154:8 | call to operator* | TAINT |
+| map.cpp:155:8:155:9 | i2 | map.cpp:155:10:155:10 | call to operator-> | TAINT |
+| map.cpp:156:8:156:9 | i2 | map.cpp:156:10:156:10 | call to operator-> | TAINT |
+| map.cpp:158:12:158:13 | m3 | map.cpp:158:15:158:19 | call to begin | TAINT |
+| map.cpp:158:12:158:13 | ref arg m3 | map.cpp:158:30:158:31 | m3 | |
+| map.cpp:158:12:158:13 | ref arg m3 | map.cpp:252:1:252:1 | m3 | |
+| map.cpp:158:15:158:19 | call to begin | map.cpp:158:7:158:21 | ... = ... | |
+| map.cpp:158:15:158:19 | call to begin | map.cpp:158:24:158:25 | i3 | |
+| map.cpp:158:15:158:19 | call to begin | map.cpp:158:40:158:41 | i3 | |
+| map.cpp:158:15:158:19 | call to begin | map.cpp:160:9:160:10 | i3 | |
+| map.cpp:158:30:158:31 | m3 | map.cpp:158:33:158:35 | call to end | TAINT |
+| map.cpp:158:30:158:31 | ref arg m3 | map.cpp:158:30:158:31 | m3 | |
+| map.cpp:158:30:158:31 | ref arg m3 | map.cpp:252:1:252:1 | m3 | |
+| map.cpp:158:40:158:41 | i3 | map.cpp:158:42:158:42 | call to operator++ | |
+| map.cpp:158:40:158:41 | ref arg i3 | map.cpp:158:24:158:25 | i3 | |
+| map.cpp:158:40:158:41 | ref arg i3 | map.cpp:158:40:158:41 | i3 | |
+| map.cpp:158:40:158:41 | ref arg i3 | map.cpp:160:9:160:10 | i3 | |
+| map.cpp:160:8:160:8 | call to operator* | map.cpp:160:8:160:10 | call to pair | TAINT |
+| map.cpp:160:9:160:10 | i3 | map.cpp:160:8:160:8 | call to operator* | TAINT |
+| map.cpp:161:8:161:9 | i2 | map.cpp:161:10:161:10 | call to operator-> | TAINT |
+| map.cpp:162:8:162:9 | i2 | map.cpp:162:10:162:10 | call to operator-> | TAINT |
+| map.cpp:166:27:166:29 | call to map | map.cpp:167:7:167:9 | m10 | |
+| map.cpp:166:27:166:29 | call to map | map.cpp:171:7:171:9 | m10 | |
+| map.cpp:166:27:166:29 | call to map | map.cpp:252:1:252:1 | m10 | |
+| map.cpp:166:32:166:34 | call to map | map.cpp:168:7:168:9 | m11 | |
+| map.cpp:166:32:166:34 | call to map | map.cpp:172:7:172:9 | m11 | |
+| map.cpp:166:32:166:34 | call to map | map.cpp:252:1:252:1 | m11 | |
+| map.cpp:166:37:166:39 | call to map | map.cpp:169:7:169:9 | m12 | |
+| map.cpp:166:37:166:39 | call to map | map.cpp:173:7:173:9 | m12 | |
+| map.cpp:166:37:166:39 | call to map | map.cpp:252:1:252:1 | m12 | |
+| map.cpp:166:42:166:44 | call to map | map.cpp:170:7:170:9 | m13 | |
+| map.cpp:166:42:166:44 | call to map | map.cpp:174:7:174:9 | m13 | |
+| map.cpp:166:42:166:44 | call to map | map.cpp:252:1:252:1 | m13 | |
+| map.cpp:167:7:167:9 | m10 | map.cpp:167:10:167:10 | call to operator[] | TAINT |
+| map.cpp:167:7:167:9 | ref arg m10 | map.cpp:171:7:171:9 | m10 | |
+| map.cpp:167:7:167:9 | ref arg m10 | map.cpp:252:1:252:1 | m10 | |
+| map.cpp:167:7:167:24 | ... = ... | map.cpp:167:10:167:10 | call to operator[] [post update] | |
+| map.cpp:167:10:167:10 | call to operator[] [post update] | map.cpp:167:7:167:9 | ref arg m10 | TAINT |
+| map.cpp:167:20:167:24 | def | map.cpp:167:7:167:24 | ... = ... | |
+| map.cpp:168:7:168:9 | m11 | map.cpp:168:10:168:10 | call to operator[] | TAINT |
+| map.cpp:168:7:168:9 | ref arg m11 | map.cpp:172:7:172:9 | m11 | |
+| map.cpp:168:7:168:9 | ref arg m11 | map.cpp:252:1:252:1 | m11 | |
+| map.cpp:168:7:168:27 | ... = ... | map.cpp:168:10:168:10 | call to operator[] [post update] | |
+| map.cpp:168:10:168:10 | call to operator[] [post update] | map.cpp:168:7:168:9 | ref arg m11 | TAINT |
+| map.cpp:168:20:168:25 | call to source | map.cpp:168:7:168:27 | ... = ... | |
+| map.cpp:169:7:169:9 | m12 | map.cpp:169:11:169:12 | call to at | TAINT |
+| map.cpp:169:7:169:9 | ref arg m12 | map.cpp:173:7:173:9 | m12 | |
+| map.cpp:169:7:169:9 | ref arg m12 | map.cpp:252:1:252:1 | m12 | |
+| map.cpp:169:7:169:27 | ... = ... | map.cpp:169:11:169:12 | call to at [post update] | |
+| map.cpp:169:11:169:12 | call to at [post update] | map.cpp:169:7:169:9 | ref arg m12 | TAINT |
+| map.cpp:169:23:169:27 | def | map.cpp:169:7:169:27 | ... = ... | |
+| map.cpp:170:7:170:9 | m13 | map.cpp:170:11:170:12 | call to at | TAINT |
+| map.cpp:170:7:170:9 | ref arg m13 | map.cpp:174:7:174:9 | m13 | |
+| map.cpp:170:7:170:9 | ref arg m13 | map.cpp:252:1:252:1 | m13 | |
+| map.cpp:170:7:170:30 | ... = ... | map.cpp:170:11:170:12 | call to at [post update] | |
+| map.cpp:170:11:170:12 | call to at [post update] | map.cpp:170:7:170:9 | ref arg m13 | TAINT |
+| map.cpp:170:23:170:28 | call to source | map.cpp:170:7:170:30 | ... = ... | |
+| map.cpp:171:7:171:9 | m10 | map.cpp:171:10:171:10 | call to operator[] | TAINT |
+| map.cpp:171:7:171:9 | ref arg m10 | map.cpp:252:1:252:1 | m10 | |
+| map.cpp:172:7:172:9 | m11 | map.cpp:172:10:172:10 | call to operator[] | TAINT |
+| map.cpp:172:7:172:9 | ref arg m11 | map.cpp:252:1:252:1 | m11 | |
+| map.cpp:173:7:173:9 | m12 | map.cpp:173:10:173:10 | call to operator[] | TAINT |
+| map.cpp:173:7:173:9 | ref arg m12 | map.cpp:252:1:252:1 | m12 | |
+| map.cpp:174:7:174:9 | m13 | map.cpp:174:10:174:10 | call to operator[] | TAINT |
+| map.cpp:174:7:174:9 | ref arg m13 | map.cpp:252:1:252:1 | m13 | |
+| map.cpp:177:27:177:29 | call to map | map.cpp:178:2:178:4 | m14 | |
+| map.cpp:177:27:177:29 | call to map | map.cpp:179:2:179:4 | m14 | |
+| map.cpp:177:27:177:29 | call to map | map.cpp:180:2:180:4 | m14 | |
+| map.cpp:177:27:177:29 | call to map | map.cpp:181:2:181:4 | m14 | |
+| map.cpp:177:27:177:29 | call to map | map.cpp:252:1:252:1 | m14 | |
+| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:179:2:179:4 | m14 | |
+| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:180:2:180:4 | m14 | |
+| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:181:2:181:4 | m14 | |
+| map.cpp:178:2:178:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
+| map.cpp:178:13:178:26 | call to make_pair | map.cpp:178:13:178:36 | call to pair | TAINT |
+| map.cpp:178:13:178:36 | call to pair | map.cpp:178:2:178:4 | ref arg m14 | TAINT |
+| map.cpp:178:13:178:36 | call to pair | map.cpp:178:6:178:11 | call to insert | TAINT |
+| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:180:2:180:4 | m14 | |
+| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:181:2:181:4 | m14 | |
+| map.cpp:179:2:179:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
+| map.cpp:179:13:179:26 | call to make_pair | map.cpp:179:13:179:41 | call to pair | TAINT |
+| map.cpp:179:13:179:41 | call to pair | map.cpp:179:2:179:4 | ref arg m14 | TAINT |
+| map.cpp:179:13:179:41 | call to pair | map.cpp:179:6:179:11 | call to insert | TAINT |
+| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:181:2:181:4 | m14 | |
+| map.cpp:180:2:180:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
+| map.cpp:180:13:180:26 | call to make_pair | map.cpp:180:13:180:41 | call to pair | TAINT |
+| map.cpp:180:13:180:41 | call to pair | map.cpp:180:2:180:4 | ref arg m14 | TAINT |
+| map.cpp:180:13:180:41 | call to pair | map.cpp:180:6:180:11 | call to insert | TAINT |
+| map.cpp:181:2:181:4 | ref arg m14 | map.cpp:252:1:252:1 | m14 | |
+| map.cpp:181:13:181:26 | call to make_pair | map.cpp:181:13:181:36 | call to pair | TAINT |
+| map.cpp:181:13:181:36 | call to pair | map.cpp:181:2:181:4 | ref arg m14 | TAINT |
+| map.cpp:181:13:181:36 | call to pair | map.cpp:181:6:181:11 | call to insert | TAINT |
+| map.cpp:182:7:182:8 | m2 | map.cpp:182:10:182:20 | call to lower_bound | TAINT |
+| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:183:7:183:8 | m2 | |
+| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
+| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
+| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
+| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
+| map.cpp:182:7:182:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:183:7:183:8 | m2 | map.cpp:183:10:183:20 | call to upper_bound | TAINT |
+| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:184:7:184:8 | m2 | |
+| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
+| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
+| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
+| map.cpp:183:7:183:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:184:7:184:8 | m2 | map.cpp:184:10:184:20 | call to equal_range | TAINT |
+| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:185:7:185:8 | m2 | |
+| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
+| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
+| map.cpp:184:7:184:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:184:27:184:31 | first | map.cpp:184:7:184:31 | call to iterator | |
+| map.cpp:185:7:185:8 | m2 | map.cpp:185:10:185:20 | call to equal_range | TAINT |
+| map.cpp:185:7:185:8 | ref arg m2 | map.cpp:186:7:186:8 | m2 | |
+| map.cpp:185:7:185:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
+| map.cpp:185:7:185:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:185:27:185:32 | second | map.cpp:185:7:185:32 | call to iterator | |
+| map.cpp:186:7:186:8 | m2 | map.cpp:186:10:186:20 | call to upper_bound | TAINT |
+| map.cpp:186:7:186:8 | ref arg m2 | map.cpp:187:7:187:8 | m2 | |
+| map.cpp:186:7:186:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:187:7:187:8 | m2 | map.cpp:187:10:187:20 | call to equal_range | TAINT |
+| map.cpp:187:7:187:8 | ref arg m2 | map.cpp:252:1:252:1 | m2 | |
+| map.cpp:187:27:187:32 | second | map.cpp:187:7:187:32 | call to iterator | |
+| map.cpp:190:27:190:29 | call to map | map.cpp:191:2:191:4 | m15 | |
+| map.cpp:190:27:190:29 | call to map | map.cpp:193:7:193:9 | m15 | |
+| map.cpp:190:27:190:29 | call to map | map.cpp:197:2:197:4 | m15 | |
+| map.cpp:190:27:190:29 | call to map | map.cpp:199:7:199:9 | m15 | |
+| map.cpp:190:27:190:29 | call to map | map.cpp:252:1:252:1 | m15 | |
+| map.cpp:190:32:190:34 | call to map | map.cpp:194:7:194:9 | m16 | |
+| map.cpp:190:32:190:34 | call to map | map.cpp:197:11:197:13 | m16 | |
+| map.cpp:190:32:190:34 | call to map | map.cpp:200:7:200:9 | m16 | |
+| map.cpp:190:32:190:34 | call to map | map.cpp:252:1:252:1 | m16 | |
+| map.cpp:190:37:190:39 | call to map | map.cpp:195:7:195:9 | m17 | |
+| map.cpp:190:37:190:39 | call to map | map.cpp:198:2:198:4 | m17 | |
+| map.cpp:190:37:190:39 | call to map | map.cpp:201:7:201:9 | m17 | |
+| map.cpp:190:37:190:39 | call to map | map.cpp:252:1:252:1 | m17 | |
+| map.cpp:190:42:190:44 | call to map | map.cpp:192:2:192:4 | m18 | |
+| map.cpp:190:42:190:44 | call to map | map.cpp:196:7:196:9 | m18 | |
+| map.cpp:190:42:190:44 | call to map | map.cpp:198:11:198:13 | m18 | |
+| map.cpp:190:42:190:44 | call to map | map.cpp:202:7:202:9 | m18 | |
+| map.cpp:190:42:190:44 | call to map | map.cpp:252:1:252:1 | m18 | |
+| map.cpp:191:2:191:4 | ref arg m15 | map.cpp:193:7:193:9 | m15 | |
+| map.cpp:191:2:191:4 | ref arg m15 | map.cpp:197:2:197:4 | m15 | |
+| map.cpp:191:2:191:4 | ref arg m15 | map.cpp:199:7:199:9 | m15 | |
+| map.cpp:191:2:191:4 | ref arg m15 | map.cpp:252:1:252:1 | m15 | |
+| map.cpp:191:13:191:57 | call to pair | map.cpp:191:2:191:4 | ref arg m15 | TAINT |
+| map.cpp:191:13:191:57 | call to pair | map.cpp:191:6:191:11 | call to insert | TAINT |
+| map.cpp:191:13:191:57 | call to pair | map.cpp:191:13:191:57 | call to pair | TAINT |
+| map.cpp:191:49:191:54 | call to source | map.cpp:191:13:191:57 | call to pair | TAINT |
+| map.cpp:192:2:192:4 | ref arg m18 | map.cpp:196:7:196:9 | m18 | |
+| map.cpp:192:2:192:4 | ref arg m18 | map.cpp:198:11:198:13 | m18 | |
+| map.cpp:192:2:192:4 | ref arg m18 | map.cpp:202:7:202:9 | m18 | |
+| map.cpp:192:2:192:4 | ref arg m18 | map.cpp:252:1:252:1 | m18 | |
+| map.cpp:192:13:192:57 | call to pair | map.cpp:192:2:192:4 | ref arg m18 | TAINT |
+| map.cpp:192:13:192:57 | call to pair | map.cpp:192:6:192:11 | call to insert | TAINT |
+| map.cpp:192:13:192:57 | call to pair | map.cpp:192:13:192:57 | call to pair | TAINT |
+| map.cpp:192:49:192:54 | call to source | map.cpp:192:13:192:57 | call to pair | TAINT |
+| map.cpp:193:7:193:9 | m15 | map.cpp:193:7:193:9 | call to map | |
+| map.cpp:194:7:194:9 | m16 | map.cpp:194:7:194:9 | call to map | |
+| map.cpp:195:7:195:9 | m17 | map.cpp:195:7:195:9 | call to map | |
+| map.cpp:196:7:196:9 | m18 | map.cpp:196:7:196:9 | call to map | |
+| map.cpp:197:2:197:4 | m15 | map.cpp:197:11:197:13 | ref arg m16 | TAINT |
+| map.cpp:197:2:197:4 | ref arg m15 | map.cpp:199:7:199:9 | m15 | |
+| map.cpp:197:2:197:4 | ref arg m15 | map.cpp:252:1:252:1 | m15 | |
+| map.cpp:197:11:197:13 | m16 | map.cpp:197:2:197:4 | ref arg m15 | TAINT |
+| map.cpp:197:11:197:13 | ref arg m16 | map.cpp:200:7:200:9 | m16 | |
+| map.cpp:197:11:197:13 | ref arg m16 | map.cpp:252:1:252:1 | m16 | |
+| map.cpp:198:2:198:4 | m17 | map.cpp:198:11:198:13 | ref arg m18 | TAINT |
+| map.cpp:198:2:198:4 | ref arg m17 | map.cpp:201:7:201:9 | m17 | |
+| map.cpp:198:2:198:4 | ref arg m17 | map.cpp:252:1:252:1 | m17 | |
+| map.cpp:198:11:198:13 | m18 | map.cpp:198:2:198:4 | ref arg m17 | TAINT |
+| map.cpp:198:11:198:13 | ref arg m18 | map.cpp:202:7:202:9 | m18 | |
+| map.cpp:198:11:198:13 | ref arg m18 | map.cpp:252:1:252:1 | m18 | |
+| map.cpp:199:7:199:9 | m15 | map.cpp:199:7:199:9 | call to map | |
+| map.cpp:200:7:200:9 | m16 | map.cpp:200:7:200:9 | call to map | |
+| map.cpp:201:7:201:9 | m17 | map.cpp:201:7:201:9 | call to map | |
+| map.cpp:202:7:202:9 | m18 | map.cpp:202:7:202:9 | call to map | |
+| map.cpp:205:27:205:29 | call to map | map.cpp:206:2:206:4 | m19 | |
+| map.cpp:205:27:205:29 | call to map | map.cpp:210:7:210:9 | m19 | |
+| map.cpp:205:27:205:29 | call to map | map.cpp:214:2:214:4 | m19 | |
+| map.cpp:205:27:205:29 | call to map | map.cpp:216:7:216:9 | m19 | |
+| map.cpp:205:27:205:29 | call to map | map.cpp:252:1:252:1 | m19 | |
+| map.cpp:205:32:205:34 | call to map | map.cpp:207:2:207:4 | m20 | |
+| map.cpp:205:32:205:34 | call to map | map.cpp:211:7:211:9 | m20 | |
+| map.cpp:205:32:205:34 | call to map | map.cpp:214:12:214:14 | m20 | |
+| map.cpp:205:32:205:34 | call to map | map.cpp:217:7:217:9 | m20 | |
+| map.cpp:205:32:205:34 | call to map | map.cpp:252:1:252:1 | m20 | |
+| map.cpp:205:37:205:39 | call to map | map.cpp:208:2:208:4 | m21 | |
+| map.cpp:205:37:205:39 | call to map | map.cpp:212:7:212:9 | m21 | |
+| map.cpp:205:37:205:39 | call to map | map.cpp:215:2:215:4 | m21 | |
+| map.cpp:205:37:205:39 | call to map | map.cpp:218:7:218:9 | m21 | |
+| map.cpp:205:37:205:39 | call to map | map.cpp:252:1:252:1 | m21 | |
+| map.cpp:205:42:205:44 | call to map | map.cpp:209:2:209:4 | m22 | |
+| map.cpp:205:42:205:44 | call to map | map.cpp:213:7:213:9 | m22 | |
+| map.cpp:205:42:205:44 | call to map | map.cpp:215:12:215:14 | m22 | |
+| map.cpp:205:42:205:44 | call to map | map.cpp:219:7:219:9 | m22 | |
+| map.cpp:205:42:205:44 | call to map | map.cpp:252:1:252:1 | m22 | |
+| map.cpp:206:2:206:4 | ref arg m19 | map.cpp:210:7:210:9 | m19 | |
+| map.cpp:206:2:206:4 | ref arg m19 | map.cpp:214:2:214:4 | m19 | |
+| map.cpp:206:2:206:4 | ref arg m19 | map.cpp:216:7:216:9 | m19 | |
+| map.cpp:206:2:206:4 | ref arg m19 | map.cpp:252:1:252:1 | m19 | |
+| map.cpp:206:13:206:57 | call to pair | map.cpp:206:2:206:4 | ref arg m19 | TAINT |
+| map.cpp:206:13:206:57 | call to pair | map.cpp:206:6:206:11 | call to insert | TAINT |
+| map.cpp:206:13:206:57 | call to pair | map.cpp:206:13:206:57 | call to pair | TAINT |
+| map.cpp:206:49:206:54 | call to source | map.cpp:206:13:206:57 | call to pair | TAINT |
+| map.cpp:207:2:207:4 | ref arg m20 | map.cpp:211:7:211:9 | m20 | |
+| map.cpp:207:2:207:4 | ref arg m20 | map.cpp:214:12:214:14 | m20 | |
+| map.cpp:207:2:207:4 | ref arg m20 | map.cpp:217:7:217:9 | m20 | |
+| map.cpp:207:2:207:4 | ref arg m20 | map.cpp:252:1:252:1 | m20 | |
+| map.cpp:207:13:207:51 | call to pair | map.cpp:207:2:207:4 | ref arg m20 | TAINT |
+| map.cpp:207:13:207:51 | call to pair | map.cpp:207:6:207:11 | call to insert | TAINT |
+| map.cpp:207:13:207:51 | call to pair | map.cpp:207:13:207:51 | call to pair | TAINT |
+| map.cpp:207:46:207:50 | def | map.cpp:207:13:207:51 | call to pair | TAINT |
+| map.cpp:208:2:208:4 | ref arg m21 | map.cpp:212:7:212:9 | m21 | |
+| map.cpp:208:2:208:4 | ref arg m21 | map.cpp:215:2:215:4 | m21 | |
+| map.cpp:208:2:208:4 | ref arg m21 | map.cpp:218:7:218:9 | m21 | |
+| map.cpp:208:2:208:4 | ref arg m21 | map.cpp:252:1:252:1 | m21 | |
+| map.cpp:208:13:208:51 | call to pair | map.cpp:208:2:208:4 | ref arg m21 | TAINT |
+| map.cpp:208:13:208:51 | call to pair | map.cpp:208:6:208:11 | call to insert | TAINT |
+| map.cpp:208:13:208:51 | call to pair | map.cpp:208:13:208:51 | call to pair | TAINT |
+| map.cpp:208:46:208:50 | def | map.cpp:208:13:208:51 | call to pair | TAINT |
+| map.cpp:209:2:209:4 | ref arg m22 | map.cpp:213:7:213:9 | m22 | |
+| map.cpp:209:2:209:4 | ref arg m22 | map.cpp:215:12:215:14 | m22 | |
+| map.cpp:209:2:209:4 | ref arg m22 | map.cpp:219:7:219:9 | m22 | |
+| map.cpp:209:2:209:4 | ref arg m22 | map.cpp:252:1:252:1 | m22 | |
+| map.cpp:209:13:209:57 | call to pair | map.cpp:209:2:209:4 | ref arg m22 | TAINT |
+| map.cpp:209:13:209:57 | call to pair | map.cpp:209:6:209:11 | call to insert | TAINT |
+| map.cpp:209:13:209:57 | call to pair | map.cpp:209:13:209:57 | call to pair | TAINT |
+| map.cpp:209:49:209:54 | call to source | map.cpp:209:13:209:57 | call to pair | TAINT |
+| map.cpp:210:7:210:9 | m19 | map.cpp:210:7:210:9 | call to map | |
+| map.cpp:211:7:211:9 | m20 | map.cpp:211:7:211:9 | call to map | |
+| map.cpp:212:7:212:9 | m21 | map.cpp:212:7:212:9 | call to map | |
+| map.cpp:213:7:213:9 | m22 | map.cpp:213:7:213:9 | call to map | |
+| map.cpp:214:2:214:4 | ref arg m19 | map.cpp:216:7:216:9 | m19 | |
+| map.cpp:214:2:214:4 | ref arg m19 | map.cpp:252:1:252:1 | m19 | |
+| map.cpp:214:12:214:14 | m20 | map.cpp:214:2:214:4 | ref arg m19 | TAINT |
+| map.cpp:214:12:214:14 | ref arg m20 | map.cpp:217:7:217:9 | m20 | |
+| map.cpp:214:12:214:14 | ref arg m20 | map.cpp:252:1:252:1 | m20 | |
+| map.cpp:215:2:215:4 | ref arg m21 | map.cpp:218:7:218:9 | m21 | |
+| map.cpp:215:2:215:4 | ref arg m21 | map.cpp:252:1:252:1 | m21 | |
+| map.cpp:215:12:215:14 | m22 | map.cpp:215:2:215:4 | ref arg m21 | TAINT |
+| map.cpp:215:12:215:14 | ref arg m22 | map.cpp:219:7:219:9 | m22 | |
+| map.cpp:215:12:215:14 | ref arg m22 | map.cpp:252:1:252:1 | m22 | |
+| map.cpp:216:7:216:9 | m19 | map.cpp:216:7:216:9 | call to map | |
+| map.cpp:217:7:217:9 | m20 | map.cpp:217:7:217:9 | call to map | |
+| map.cpp:218:7:218:9 | m21 | map.cpp:218:7:218:9 | call to map | |
+| map.cpp:219:7:219:9 | m22 | map.cpp:219:7:219:9 | call to map | |
+| map.cpp:222:27:222:29 | call to map | map.cpp:223:2:223:4 | m23 | |
+| map.cpp:222:27:222:29 | call to map | map.cpp:224:2:224:4 | m23 | |
+| map.cpp:222:27:222:29 | call to map | map.cpp:225:7:225:9 | m23 | |
+| map.cpp:222:27:222:29 | call to map | map.cpp:226:7:226:9 | m23 | |
+| map.cpp:222:27:222:29 | call to map | map.cpp:226:17:226:19 | m23 | |
+| map.cpp:222:27:222:29 | call to map | map.cpp:227:7:227:9 | m23 | |
+| map.cpp:222:27:222:29 | call to map | map.cpp:228:2:228:4 | m23 | |
+| map.cpp:222:27:222:29 | call to map | map.cpp:229:7:229:9 | m23 | |
+| map.cpp:222:27:222:29 | call to map | map.cpp:252:1:252:1 | m23 | |
+| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:224:2:224:4 | m23 | |
+| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:225:7:225:9 | m23 | |
+| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:226:7:226:9 | m23 | |
+| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:226:17:226:19 | m23 | |
+| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:227:7:227:9 | m23 | |
+| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:228:2:228:4 | m23 | |
+| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:229:7:229:9 | m23 | |
+| map.cpp:223:2:223:4 | ref arg m23 | map.cpp:252:1:252:1 | m23 | |
+| map.cpp:223:13:223:57 | call to pair | map.cpp:223:2:223:4 | ref arg m23 | TAINT |
+| map.cpp:223:13:223:57 | call to pair | map.cpp:223:6:223:11 | call to insert | TAINT |
+| map.cpp:223:13:223:57 | call to pair | map.cpp:223:13:223:57 | call to pair | TAINT |
+| map.cpp:223:49:223:54 | call to source | map.cpp:223:13:223:57 | call to pair | TAINT |
+| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:225:7:225:9 | m23 | |
+| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:226:7:226:9 | m23 | |
+| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:226:17:226:19 | m23 | |
+| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:227:7:227:9 | m23 | |
+| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:228:2:228:4 | m23 | |
+| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:229:7:229:9 | m23 | |
+| map.cpp:224:2:224:4 | ref arg m23 | map.cpp:252:1:252:1 | m23 | |
+| map.cpp:224:13:224:57 | call to pair | map.cpp:224:2:224:4 | ref arg m23 | TAINT |
+| map.cpp:224:13:224:57 | call to pair | map.cpp:224:6:224:11 | call to insert | TAINT |
+| map.cpp:224:13:224:57 | call to pair | map.cpp:224:13:224:57 | call to pair | TAINT |
+| map.cpp:224:49:224:54 | call to source | map.cpp:224:13:224:57 | call to pair | TAINT |
+| map.cpp:225:7:225:9 | m23 | map.cpp:225:7:225:9 | call to map | |
+| map.cpp:226:7:226:9 | m23 | map.cpp:226:11:226:15 | call to erase | TAINT |
+| map.cpp:226:7:226:9 | ref arg m23 | map.cpp:227:7:227:9 | m23 | |
+| map.cpp:226:7:226:9 | ref arg m23 | map.cpp:228:2:228:4 | m23 | |
+| map.cpp:226:7:226:9 | ref arg m23 | map.cpp:229:7:229:9 | m23 | |
+| map.cpp:226:7:226:9 | ref arg m23 | map.cpp:252:1:252:1 | m23 | |
+| map.cpp:226:17:226:19 | m23 | map.cpp:226:21:226:25 | call to begin | TAINT |
+| map.cpp:226:17:226:19 | ref arg m23 | map.cpp:226:7:226:9 | m23 | |
+| map.cpp:226:17:226:19 | ref arg m23 | map.cpp:227:7:227:9 | m23 | |
+| map.cpp:226:17:226:19 | ref arg m23 | map.cpp:228:2:228:4 | m23 | |
+| map.cpp:226:17:226:19 | ref arg m23 | map.cpp:229:7:229:9 | m23 | |
+| map.cpp:226:17:226:19 | ref arg m23 | map.cpp:252:1:252:1 | m23 | |
+| map.cpp:227:7:227:9 | m23 | map.cpp:227:7:227:9 | call to map | |
+| map.cpp:228:2:228:4 | ref arg m23 | map.cpp:229:7:229:9 | m23 | |
+| map.cpp:228:2:228:4 | ref arg m23 | map.cpp:252:1:252:1 | m23 | |
+| map.cpp:229:7:229:9 | m23 | map.cpp:229:7:229:9 | call to map | |
+| map.cpp:232:27:232:29 | call to map | map.cpp:233:7:233:9 | m24 | |
+| map.cpp:232:27:232:29 | call to map | map.cpp:234:7:234:9 | m24 | |
+| map.cpp:232:27:232:29 | call to map | map.cpp:235:7:235:9 | m24 | |
+| map.cpp:232:27:232:29 | call to map | map.cpp:236:7:236:9 | m24 | |
+| map.cpp:232:27:232:29 | call to map | map.cpp:252:1:252:1 | m24 | |
+| map.cpp:232:32:232:34 | call to map | map.cpp:237:7:237:9 | m25 | |
+| map.cpp:232:32:232:34 | call to map | map.cpp:237:24:237:26 | m25 | |
+| map.cpp:232:32:232:34 | call to map | map.cpp:238:7:238:9 | m25 | |
+| map.cpp:232:32:232:34 | call to map | map.cpp:239:7:239:9 | m25 | |
+| map.cpp:232:32:232:34 | call to map | map.cpp:239:24:239:26 | m25 | |
+| map.cpp:232:32:232:34 | call to map | map.cpp:240:7:240:9 | m25 | |
+| map.cpp:232:32:232:34 | call to map | map.cpp:252:1:252:1 | m25 | |
+| map.cpp:233:7:233:9 | m24 | map.cpp:233:11:233:17 | call to emplace | TAINT |
+| map.cpp:233:7:233:9 | ref arg m24 | map.cpp:234:7:234:9 | m24 | |
+| map.cpp:233:7:233:9 | ref arg m24 | map.cpp:235:7:235:9 | m24 | |
+| map.cpp:233:7:233:9 | ref arg m24 | map.cpp:236:7:236:9 | m24 | |
+| map.cpp:233:7:233:9 | ref arg m24 | map.cpp:252:1:252:1 | m24 | |
+| map.cpp:233:26:233:30 | def | map.cpp:233:7:233:9 | ref arg m24 | TAINT |
+| map.cpp:233:26:233:30 | def | map.cpp:233:11:233:17 | call to emplace | TAINT |
+| map.cpp:233:33:233:37 | first | map.cpp:233:7:233:37 | call to iterator | |
+| map.cpp:234:7:234:9 | m24 | map.cpp:234:7:234:9 | call to map | |
+| map.cpp:235:7:235:9 | m24 | map.cpp:235:11:235:17 | call to emplace | TAINT |
+| map.cpp:235:7:235:9 | ref arg m24 | map.cpp:236:7:236:9 | m24 | |
+| map.cpp:235:7:235:9 | ref arg m24 | map.cpp:252:1:252:1 | m24 | |
+| map.cpp:235:26:235:31 | call to source | map.cpp:235:7:235:9 | ref arg m24 | TAINT |
+| map.cpp:235:26:235:31 | call to source | map.cpp:235:11:235:17 | call to emplace | TAINT |
+| map.cpp:235:36:235:40 | first | map.cpp:235:7:235:40 | call to iterator | |
+| map.cpp:236:7:236:9 | m24 | map.cpp:236:7:236:9 | call to map | |
+| map.cpp:237:7:237:9 | m25 | map.cpp:237:11:237:22 | call to emplace_hint | TAINT |
+| map.cpp:237:7:237:9 | ref arg m25 | map.cpp:238:7:238:9 | m25 | |
+| map.cpp:237:7:237:9 | ref arg m25 | map.cpp:239:7:239:9 | m25 | |
+| map.cpp:237:7:237:9 | ref arg m25 | map.cpp:239:24:239:26 | m25 | |
+| map.cpp:237:7:237:9 | ref arg m25 | map.cpp:240:7:240:9 | m25 | |
+| map.cpp:237:7:237:9 | ref arg m25 | map.cpp:252:1:252:1 | m25 | |
+| map.cpp:237:24:237:26 | m25 | map.cpp:237:28:237:32 | call to begin | TAINT |
+| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:237:7:237:9 | m25 | |
+| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:238:7:238:9 | m25 | |
+| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:239:7:239:9 | m25 | |
+| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:239:24:239:26 | m25 | |
+| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:240:7:240:9 | m25 | |
+| map.cpp:237:24:237:26 | ref arg m25 | map.cpp:252:1:252:1 | m25 | |
+| map.cpp:237:28:237:32 | call to begin | map.cpp:237:24:237:34 | call to iterator | TAINT |
+| map.cpp:237:44:237:48 | def | map.cpp:237:7:237:9 | ref arg m25 | TAINT |
+| map.cpp:237:44:237:48 | def | map.cpp:237:11:237:22 | call to emplace_hint | TAINT |
+| map.cpp:238:7:238:9 | m25 | map.cpp:238:7:238:9 | call to map | |
+| map.cpp:239:7:239:9 | m25 | map.cpp:239:11:239:22 | call to emplace_hint | TAINT |
+| map.cpp:239:7:239:9 | ref arg m25 | map.cpp:240:7:240:9 | m25 | |
+| map.cpp:239:7:239:9 | ref arg m25 | map.cpp:252:1:252:1 | m25 | |
+| map.cpp:239:24:239:26 | m25 | map.cpp:239:28:239:32 | call to begin | TAINT |
+| map.cpp:239:24:239:26 | ref arg m25 | map.cpp:239:7:239:9 | m25 | |
+| map.cpp:239:24:239:26 | ref arg m25 | map.cpp:240:7:240:9 | m25 | |
+| map.cpp:239:24:239:26 | ref arg m25 | map.cpp:252:1:252:1 | m25 | |
+| map.cpp:239:28:239:32 | call to begin | map.cpp:239:24:239:34 | call to iterator | TAINT |
+| map.cpp:239:44:239:49 | call to source | map.cpp:239:7:239:9 | ref arg m25 | TAINT |
+| map.cpp:239:44:239:49 | call to source | map.cpp:239:11:239:22 | call to emplace_hint | TAINT |
+| map.cpp:240:7:240:9 | m25 | map.cpp:240:7:240:9 | call to map | |
+| map.cpp:243:27:243:29 | call to map | map.cpp:244:7:244:9 | m26 | |
+| map.cpp:243:27:243:29 | call to map | map.cpp:245:7:245:9 | m26 | |
+| map.cpp:243:27:243:29 | call to map | map.cpp:246:7:246:9 | m26 | |
+| map.cpp:243:27:243:29 | call to map | map.cpp:247:7:247:9 | m26 | |
+| map.cpp:243:27:243:29 | call to map | map.cpp:252:1:252:1 | m26 | |
+| map.cpp:243:32:243:34 | call to map | map.cpp:248:7:248:9 | m27 | |
+| map.cpp:243:32:243:34 | call to map | map.cpp:248:23:248:25 | m27 | |
+| map.cpp:243:32:243:34 | call to map | map.cpp:249:7:249:9 | m27 | |
+| map.cpp:243:32:243:34 | call to map | map.cpp:250:7:250:9 | m27 | |
+| map.cpp:243:32:243:34 | call to map | map.cpp:250:23:250:25 | m27 | |
+| map.cpp:243:32:243:34 | call to map | map.cpp:251:7:251:9 | m27 | |
+| map.cpp:243:32:243:34 | call to map | map.cpp:252:1:252:1 | m27 | |
+| map.cpp:244:7:244:9 | m26 | map.cpp:244:11:244:21 | call to try_emplace | TAINT |
+| map.cpp:244:7:244:9 | ref arg m26 | map.cpp:245:7:245:9 | m26 | |
+| map.cpp:244:7:244:9 | ref arg m26 | map.cpp:246:7:246:9 | m26 | |
+| map.cpp:244:7:244:9 | ref arg m26 | map.cpp:247:7:247:9 | m26 | |
+| map.cpp:244:7:244:9 | ref arg m26 | map.cpp:252:1:252:1 | m26 | |
+| map.cpp:244:30:244:34 | def | map.cpp:244:7:244:9 | ref arg m26 | TAINT |
+| map.cpp:244:30:244:34 | def | map.cpp:244:11:244:21 | call to try_emplace | TAINT |
+| map.cpp:244:37:244:41 | first | map.cpp:244:7:244:41 | call to iterator | |
+| map.cpp:245:7:245:9 | m26 | map.cpp:245:7:245:9 | call to map | |
+| map.cpp:246:7:246:9 | m26 | map.cpp:246:11:246:21 | call to try_emplace | TAINT |
+| map.cpp:246:7:246:9 | ref arg m26 | map.cpp:247:7:247:9 | m26 | |
+| map.cpp:246:7:246:9 | ref arg m26 | map.cpp:252:1:252:1 | m26 | |
+| map.cpp:246:30:246:35 | call to source | map.cpp:246:7:246:9 | ref arg m26 | TAINT |
+| map.cpp:246:30:246:35 | call to source | map.cpp:246:11:246:21 | call to try_emplace | TAINT |
+| map.cpp:246:40:246:44 | first | map.cpp:246:7:246:44 | call to iterator | |
+| map.cpp:247:7:247:9 | m26 | map.cpp:247:7:247:9 | call to map | |
+| map.cpp:248:7:248:9 | m27 | map.cpp:248:11:248:21 | call to try_emplace | TAINT |
+| map.cpp:248:7:248:9 | ref arg m27 | map.cpp:249:7:249:9 | m27 | |
+| map.cpp:248:7:248:9 | ref arg m27 | map.cpp:250:7:250:9 | m27 | |
+| map.cpp:248:7:248:9 | ref arg m27 | map.cpp:250:23:250:25 | m27 | |
+| map.cpp:248:7:248:9 | ref arg m27 | map.cpp:251:7:251:9 | m27 | |
+| map.cpp:248:7:248:9 | ref arg m27 | map.cpp:252:1:252:1 | m27 | |
+| map.cpp:248:23:248:25 | m27 | map.cpp:248:27:248:31 | call to begin | TAINT |
+| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:248:7:248:9 | m27 | |
+| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:249:7:249:9 | m27 | |
+| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:250:7:250:9 | m27 | |
+| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:250:23:250:25 | m27 | |
+| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:251:7:251:9 | m27 | |
+| map.cpp:248:23:248:25 | ref arg m27 | map.cpp:252:1:252:1 | m27 | |
+| map.cpp:248:27:248:31 | call to begin | map.cpp:248:23:248:33 | call to iterator | TAINT |
+| map.cpp:248:43:248:47 | def | map.cpp:248:7:248:9 | ref arg m27 | TAINT |
+| map.cpp:248:43:248:47 | def | map.cpp:248:11:248:21 | call to try_emplace | TAINT |
+| map.cpp:249:7:249:9 | m27 | map.cpp:249:7:249:9 | call to map | |
+| map.cpp:250:7:250:9 | m27 | map.cpp:250:11:250:21 | call to try_emplace | TAINT |
+| map.cpp:250:7:250:9 | ref arg m27 | map.cpp:251:7:251:9 | m27 | |
+| map.cpp:250:7:250:9 | ref arg m27 | map.cpp:252:1:252:1 | m27 | |
+| map.cpp:250:23:250:25 | m27 | map.cpp:250:27:250:31 | call to begin | TAINT |
+| map.cpp:250:23:250:25 | ref arg m27 | map.cpp:250:7:250:9 | m27 | |
+| map.cpp:250:23:250:25 | ref arg m27 | map.cpp:251:7:251:9 | m27 | |
+| map.cpp:250:23:250:25 | ref arg m27 | map.cpp:252:1:252:1 | m27 | |
+| map.cpp:250:27:250:31 | call to begin | map.cpp:250:23:250:33 | call to iterator | TAINT |
+| map.cpp:250:43:250:48 | call to source | map.cpp:250:7:250:9 | ref arg m27 | TAINT |
+| map.cpp:250:43:250:48 | call to source | map.cpp:250:11:250:21 | call to try_emplace | TAINT |
+| map.cpp:251:7:251:9 | m27 | map.cpp:251:7:251:9 | call to map | |
+| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:259:7:259:8 | m1 | |
+| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:265:7:265:8 | m1 | |
+| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:271:7:271:8 | m1 | |
+| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:277:7:277:8 | m1 | |
+| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:298:12:298:13 | m1 | |
+| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:298:30:298:31 | m1 | |
+| map.cpp:257:37:257:38 | call to unordered_map | map.cpp:438:1:438:1 | m1 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:260:7:260:8 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:266:7:266:8 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:272:7:272:8 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:278:7:278:8 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:285:40:285:41 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:286:42:286:43 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:288:7:288:8 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:304:12:304:13 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:304:30:304:31 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:334:7:334:8 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:335:7:335:8 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:336:7:336:8 | m2 | |
+| map.cpp:257:41:257:42 | call to unordered_map | map.cpp:438:1:438:1 | m2 | |
+| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:261:7:261:8 | m3 | |
+| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:267:7:267:8 | m3 | |
+| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:273:7:273:8 | m3 | |
+| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:279:7:279:8 | m3 | |
+| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:310:12:310:13 | m3 | |
+| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:310:30:310:31 | m3 | |
+| map.cpp:257:45:257:46 | call to unordered_map | map.cpp:438:1:438:1 | m3 | |
+| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:262:7:262:8 | m4 | |
+| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:262:17:262:18 | m4 | |
+| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:268:7:268:8 | m4 | |
+| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:274:7:274:8 | m4 | |
+| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:280:7:280:8 | m4 | |
+| map.cpp:257:49:257:50 | call to unordered_map | map.cpp:438:1:438:1 | m4 | |
+| map.cpp:257:53:257:54 | call to unordered_map | map.cpp:263:7:263:8 | m5 | |
+| map.cpp:257:53:257:54 | call to unordered_map | map.cpp:269:7:269:8 | m5 | |
+| map.cpp:257:53:257:54 | call to unordered_map | map.cpp:275:7:275:8 | m5 | |
+| map.cpp:257:53:257:54 | call to unordered_map | map.cpp:281:7:281:8 | m5 | |
+| map.cpp:257:53:257:54 | call to unordered_map | map.cpp:438:1:438:1 | m5 | |
+| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:264:7:264:8 | m6 | |
+| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:264:27:264:28 | m6 | |
+| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:270:7:270:8 | m6 | |
+| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:276:7:276:8 | m6 | |
+| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:282:7:282:8 | m6 | |
+| map.cpp:257:57:257:58 | call to unordered_map | map.cpp:438:1:438:1 | m6 | |
+| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:265:7:265:8 | m1 | |
+| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:271:7:271:8 | m1 | |
+| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:277:7:277:8 | m1 | |
+| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:298:12:298:13 | m1 | |
+| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:298:30:298:31 | m1 | |
+| map.cpp:259:7:259:8 | ref arg m1 | map.cpp:438:1:438:1 | m1 | |
+| map.cpp:259:17:259:30 | call to make_pair | map.cpp:259:17:259:44 | call to pair | TAINT |
+| map.cpp:259:17:259:44 | call to pair | map.cpp:259:7:259:8 | ref arg m1 | TAINT |
+| map.cpp:259:17:259:44 | call to pair | map.cpp:259:10:259:15 | call to insert | TAINT |
+| map.cpp:259:47:259:51 | first | map.cpp:259:7:259:51 | call to iterator | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:266:7:266:8 | m2 | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:272:7:272:8 | m2 | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:278:7:278:8 | m2 | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:285:40:285:41 | m2 | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:286:42:286:43 | m2 | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:288:7:288:8 | m2 | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:304:12:304:13 | m2 | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:304:30:304:31 | m2 | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:334:7:334:8 | m2 | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
+| map.cpp:260:7:260:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
+| map.cpp:260:17:260:30 | call to make_pair | map.cpp:260:17:260:47 | call to pair | TAINT |
+| map.cpp:260:17:260:47 | call to pair | map.cpp:260:7:260:8 | ref arg m2 | TAINT |
+| map.cpp:260:17:260:47 | call to pair | map.cpp:260:10:260:15 | call to insert | TAINT |
+| map.cpp:260:50:260:54 | first | map.cpp:260:7:260:54 | call to iterator | |
+| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:267:7:267:8 | m3 | |
+| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:273:7:273:8 | m3 | |
+| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:279:7:279:8 | m3 | |
+| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:310:12:310:13 | m3 | |
+| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:310:30:310:31 | m3 | |
+| map.cpp:261:7:261:8 | ref arg m3 | map.cpp:438:1:438:1 | m3 | |
+| map.cpp:261:17:261:30 | call to make_pair | map.cpp:261:17:261:47 | call to pair | TAINT |
+| map.cpp:261:17:261:47 | call to pair | map.cpp:261:7:261:8 | ref arg m3 | TAINT |
+| map.cpp:261:17:261:47 | call to pair | map.cpp:261:10:261:15 | call to insert | TAINT |
+| map.cpp:261:50:261:54 | first | map.cpp:261:7:261:54 | call to iterator | |
+| map.cpp:262:7:262:8 | ref arg m4 | map.cpp:268:7:268:8 | m4 | |
+| map.cpp:262:7:262:8 | ref arg m4 | map.cpp:274:7:274:8 | m4 | |
+| map.cpp:262:7:262:8 | ref arg m4 | map.cpp:280:7:280:8 | m4 | |
+| map.cpp:262:7:262:8 | ref arg m4 | map.cpp:438:1:438:1 | m4 | |
+| map.cpp:262:17:262:18 | m4 | map.cpp:262:20:262:24 | call to begin | TAINT |
+| map.cpp:262:17:262:18 | ref arg m4 | map.cpp:262:7:262:8 | m4 | |
+| map.cpp:262:17:262:18 | ref arg m4 | map.cpp:268:7:268:8 | m4 | |
+| map.cpp:262:17:262:18 | ref arg m4 | map.cpp:274:7:274:8 | m4 | |
+| map.cpp:262:17:262:18 | ref arg m4 | map.cpp:280:7:280:8 | m4 | |
+| map.cpp:262:17:262:18 | ref arg m4 | map.cpp:438:1:438:1 | m4 | |
+| map.cpp:262:20:262:24 | call to begin | map.cpp:262:17:262:26 | call to iterator | TAINT |
+| map.cpp:262:29:262:70 | call to pair | map.cpp:262:7:262:8 | ref arg m4 | TAINT |
+| map.cpp:262:29:262:70 | call to pair | map.cpp:262:10:262:15 | call to insert | TAINT |
+| map.cpp:262:29:262:70 | call to pair | map.cpp:262:29:262:70 | call to pair | TAINT |
+| map.cpp:262:62:262:67 | call to source | map.cpp:262:29:262:70 | call to pair | TAINT |
+| map.cpp:263:7:263:8 | ref arg m5 | map.cpp:269:7:269:8 | m5 | |
+| map.cpp:263:7:263:8 | ref arg m5 | map.cpp:275:7:275:8 | m5 | |
+| map.cpp:263:7:263:8 | ref arg m5 | map.cpp:281:7:281:8 | m5 | |
+| map.cpp:263:7:263:8 | ref arg m5 | map.cpp:438:1:438:1 | m5 | |
+| map.cpp:263:34:263:39 | call to source | map.cpp:263:7:263:8 | ref arg m5 | TAINT |
+| map.cpp:263:34:263:39 | call to source | map.cpp:263:10:263:25 | call to insert_or_assign | TAINT |
+| map.cpp:263:44:263:48 | first | map.cpp:263:7:263:48 | call to iterator | |
+| map.cpp:264:7:264:8 | ref arg m6 | map.cpp:270:7:270:8 | m6 | |
+| map.cpp:264:7:264:8 | ref arg m6 | map.cpp:276:7:276:8 | m6 | |
+| map.cpp:264:7:264:8 | ref arg m6 | map.cpp:282:7:282:8 | m6 | |
+| map.cpp:264:7:264:8 | ref arg m6 | map.cpp:438:1:438:1 | m6 | |
+| map.cpp:264:27:264:28 | m6 | map.cpp:264:30:264:34 | call to begin | TAINT |
+| map.cpp:264:27:264:28 | ref arg m6 | map.cpp:264:7:264:8 | m6 | |
+| map.cpp:264:27:264:28 | ref arg m6 | map.cpp:270:7:270:8 | m6 | |
+| map.cpp:264:27:264:28 | ref arg m6 | map.cpp:276:7:276:8 | m6 | |
+| map.cpp:264:27:264:28 | ref arg m6 | map.cpp:282:7:282:8 | m6 | |
+| map.cpp:264:27:264:28 | ref arg m6 | map.cpp:438:1:438:1 | m6 | |
+| map.cpp:264:30:264:34 | call to begin | map.cpp:264:27:264:36 | call to iterator | TAINT |
+| map.cpp:264:46:264:51 | call to source | map.cpp:264:7:264:8 | ref arg m6 | TAINT |
+| map.cpp:264:46:264:51 | call to source | map.cpp:264:10:264:25 | call to insert_or_assign | TAINT |
+| map.cpp:265:7:265:8 | m1 | map.cpp:265:7:265:8 | call to unordered_map | |
+| map.cpp:266:7:266:8 | m2 | map.cpp:266:7:266:8 | call to unordered_map | |
+| map.cpp:267:7:267:8 | m3 | map.cpp:267:7:267:8 | call to unordered_map | |
+| map.cpp:268:7:268:8 | m4 | map.cpp:268:7:268:8 | call to unordered_map | |
+| map.cpp:269:7:269:8 | m5 | map.cpp:269:7:269:8 | call to unordered_map | |
+| map.cpp:270:7:270:8 | m6 | map.cpp:270:7:270:8 | call to unordered_map | |
+| map.cpp:271:7:271:8 | m1 | map.cpp:271:10:271:13 | call to find | TAINT |
+| map.cpp:271:7:271:8 | ref arg m1 | map.cpp:277:7:277:8 | m1 | |
+| map.cpp:271:7:271:8 | ref arg m1 | map.cpp:298:12:298:13 | m1 | |
+| map.cpp:271:7:271:8 | ref arg m1 | map.cpp:298:30:298:31 | m1 | |
+| map.cpp:271:7:271:8 | ref arg m1 | map.cpp:438:1:438:1 | m1 | |
+| map.cpp:272:7:272:8 | m2 | map.cpp:272:10:272:13 | call to find | TAINT |
+| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:278:7:278:8 | m2 | |
+| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:285:40:285:41 | m2 | |
+| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:286:42:286:43 | m2 | |
+| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:288:7:288:8 | m2 | |
+| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:304:12:304:13 | m2 | |
+| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:304:30:304:31 | m2 | |
+| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:334:7:334:8 | m2 | |
+| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
+| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
+| map.cpp:272:7:272:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
+| map.cpp:273:7:273:8 | m3 | map.cpp:273:10:273:13 | call to find | TAINT |
+| map.cpp:273:7:273:8 | ref arg m3 | map.cpp:279:7:279:8 | m3 | |
+| map.cpp:273:7:273:8 | ref arg m3 | map.cpp:310:12:310:13 | m3 | |
+| map.cpp:273:7:273:8 | ref arg m3 | map.cpp:310:30:310:31 | m3 | |
+| map.cpp:273:7:273:8 | ref arg m3 | map.cpp:438:1:438:1 | m3 | |
+| map.cpp:274:7:274:8 | m4 | map.cpp:274:10:274:13 | call to find | TAINT |
+| map.cpp:274:7:274:8 | ref arg m4 | map.cpp:280:7:280:8 | m4 | |
+| map.cpp:274:7:274:8 | ref arg m4 | map.cpp:438:1:438:1 | m4 | |
+| map.cpp:275:7:275:8 | m5 | map.cpp:275:10:275:13 | call to find | TAINT |
+| map.cpp:275:7:275:8 | ref arg m5 | map.cpp:281:7:281:8 | m5 | |
+| map.cpp:275:7:275:8 | ref arg m5 | map.cpp:438:1:438:1 | m5 | |
+| map.cpp:276:7:276:8 | m6 | map.cpp:276:10:276:13 | call to find | TAINT |
+| map.cpp:276:7:276:8 | ref arg m6 | map.cpp:282:7:282:8 | m6 | |
+| map.cpp:276:7:276:8 | ref arg m6 | map.cpp:438:1:438:1 | m6 | |
+| map.cpp:277:7:277:8 | m1 | map.cpp:277:10:277:13 | call to find | TAINT |
+| map.cpp:277:7:277:8 | ref arg m1 | map.cpp:298:12:298:13 | m1 | |
+| map.cpp:277:7:277:8 | ref arg m1 | map.cpp:298:30:298:31 | m1 | |
+| map.cpp:277:7:277:8 | ref arg m1 | map.cpp:438:1:438:1 | m1 | |
+| map.cpp:278:7:278:8 | m2 | map.cpp:278:10:278:13 | call to find | TAINT |
+| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:285:40:285:41 | m2 | |
+| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:286:42:286:43 | m2 | |
+| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:288:7:288:8 | m2 | |
+| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:304:12:304:13 | m2 | |
+| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:304:30:304:31 | m2 | |
+| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:334:7:334:8 | m2 | |
+| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
+| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
+| map.cpp:278:7:278:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
+| map.cpp:279:7:279:8 | m3 | map.cpp:279:10:279:13 | call to find | TAINT |
+| map.cpp:279:7:279:8 | ref arg m3 | map.cpp:310:12:310:13 | m3 | |
+| map.cpp:279:7:279:8 | ref arg m3 | map.cpp:310:30:310:31 | m3 | |
+| map.cpp:279:7:279:8 | ref arg m3 | map.cpp:438:1:438:1 | m3 | |
+| map.cpp:280:7:280:8 | m4 | map.cpp:280:10:280:13 | call to find | TAINT |
+| map.cpp:280:7:280:8 | ref arg m4 | map.cpp:438:1:438:1 | m4 | |
+| map.cpp:281:7:281:8 | m5 | map.cpp:281:10:281:13 | call to find | TAINT |
+| map.cpp:281:7:281:8 | ref arg m5 | map.cpp:438:1:438:1 | m5 | |
+| map.cpp:282:7:282:8 | m6 | map.cpp:282:10:282:13 | call to find | TAINT |
+| map.cpp:282:7:282:8 | ref arg m6 | map.cpp:438:1:438:1 | m6 | |
+| map.cpp:285:40:285:41 | m2 | map.cpp:285:40:285:42 | call to unordered_map | |
+| map.cpp:285:40:285:42 | call to unordered_map | map.cpp:289:7:289:8 | m7 | |
+| map.cpp:285:40:285:42 | call to unordered_map | map.cpp:292:7:292:8 | m7 | |
+| map.cpp:285:40:285:42 | call to unordered_map | map.cpp:438:1:438:1 | m7 | |
+| map.cpp:286:41:286:43 | call to unordered_map | map.cpp:290:7:290:8 | m8 | |
+| map.cpp:286:41:286:43 | call to unordered_map | map.cpp:293:7:293:8 | m8 | |
+| map.cpp:286:41:286:43 | call to unordered_map | map.cpp:438:1:438:1 | m8 | |
+| map.cpp:286:42:286:43 | m2 | map.cpp:286:41:286:43 | call to unordered_map | |
+| map.cpp:287:37:287:38 | call to unordered_map | map.cpp:288:2:288:3 | m9 | |
+| map.cpp:287:37:287:38 | call to unordered_map | map.cpp:291:7:291:8 | m9 | |
+| map.cpp:287:37:287:38 | call to unordered_map | map.cpp:294:7:294:8 | m9 | |
+| map.cpp:287:37:287:38 | call to unordered_map | map.cpp:438:1:438:1 | m9 | |
+| map.cpp:288:2:288:3 | ref arg m9 | map.cpp:291:7:291:8 | m9 | |
+| map.cpp:288:2:288:3 | ref arg m9 | map.cpp:294:7:294:8 | m9 | |
+| map.cpp:288:2:288:3 | ref arg m9 | map.cpp:438:1:438:1 | m9 | |
+| map.cpp:288:7:288:8 | m2 | map.cpp:288:2:288:3 | ref arg m9 | TAINT |
+| map.cpp:288:7:288:8 | m2 | map.cpp:288:5:288:5 | call to operator= | TAINT |
+| map.cpp:289:7:289:8 | m7 | map.cpp:289:7:289:8 | call to unordered_map | |
+| map.cpp:290:7:290:8 | m8 | map.cpp:290:7:290:8 | call to unordered_map | |
+| map.cpp:291:7:291:8 | m9 | map.cpp:291:7:291:8 | call to unordered_map | |
+| map.cpp:292:7:292:8 | m7 | map.cpp:292:10:292:13 | call to find | TAINT |
+| map.cpp:292:7:292:8 | ref arg m7 | map.cpp:438:1:438:1 | m7 | |
+| map.cpp:293:7:293:8 | m8 | map.cpp:293:10:293:13 | call to find | TAINT |
+| map.cpp:293:7:293:8 | ref arg m8 | map.cpp:438:1:438:1 | m8 | |
+| map.cpp:294:7:294:8 | m9 | map.cpp:294:10:294:13 | call to find | TAINT |
+| map.cpp:294:7:294:8 | ref arg m9 | map.cpp:438:1:438:1 | m9 | |
+| map.cpp:298:12:298:13 | m1 | map.cpp:298:15:298:19 | call to begin | TAINT |
+| map.cpp:298:12:298:13 | ref arg m1 | map.cpp:298:30:298:31 | m1 | |
+| map.cpp:298:12:298:13 | ref arg m1 | map.cpp:438:1:438:1 | m1 | |
+| map.cpp:298:15:298:19 | call to begin | map.cpp:298:7:298:21 | ... = ... | |
+| map.cpp:298:15:298:19 | call to begin | map.cpp:298:24:298:25 | i1 | |
+| map.cpp:298:15:298:19 | call to begin | map.cpp:298:40:298:41 | i1 | |
+| map.cpp:298:15:298:19 | call to begin | map.cpp:300:9:300:10 | i1 | |
+| map.cpp:298:15:298:19 | call to begin | map.cpp:301:8:301:9 | i1 | |
+| map.cpp:298:15:298:19 | call to begin | map.cpp:302:8:302:9 | i1 | |
+| map.cpp:298:30:298:31 | m1 | map.cpp:298:33:298:35 | call to end | TAINT |
+| map.cpp:298:30:298:31 | ref arg m1 | map.cpp:298:30:298:31 | m1 | |
+| map.cpp:298:30:298:31 | ref arg m1 | map.cpp:438:1:438:1 | m1 | |
+| map.cpp:298:40:298:41 | i1 | map.cpp:298:42:298:42 | call to operator++ | |
+| map.cpp:298:40:298:41 | ref arg i1 | map.cpp:298:24:298:25 | i1 | |
+| map.cpp:298:40:298:41 | ref arg i1 | map.cpp:298:40:298:41 | i1 | |
+| map.cpp:298:40:298:41 | ref arg i1 | map.cpp:300:9:300:10 | i1 | |
+| map.cpp:298:40:298:41 | ref arg i1 | map.cpp:301:8:301:9 | i1 | |
+| map.cpp:298:40:298:41 | ref arg i1 | map.cpp:302:8:302:9 | i1 | |
+| map.cpp:300:8:300:8 | call to operator* | map.cpp:300:8:300:10 | call to pair | TAINT |
+| map.cpp:300:9:300:10 | i1 | map.cpp:300:8:300:8 | call to operator* | TAINT |
+| map.cpp:301:8:301:9 | i1 | map.cpp:301:10:301:10 | call to operator-> | TAINT |
+| map.cpp:302:8:302:9 | i1 | map.cpp:302:10:302:10 | call to operator-> | TAINT |
+| map.cpp:304:12:304:13 | m2 | map.cpp:304:15:304:19 | call to begin | TAINT |
+| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:304:30:304:31 | m2 | |
+| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:334:7:334:8 | m2 | |
+| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
+| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
+| map.cpp:304:12:304:13 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
+| map.cpp:304:15:304:19 | call to begin | map.cpp:304:7:304:21 | ... = ... | |
+| map.cpp:304:15:304:19 | call to begin | map.cpp:304:24:304:25 | i2 | |
+| map.cpp:304:15:304:19 | call to begin | map.cpp:304:40:304:41 | i2 | |
+| map.cpp:304:15:304:19 | call to begin | map.cpp:306:9:306:10 | i2 | |
+| map.cpp:304:15:304:19 | call to begin | map.cpp:307:8:307:9 | i2 | |
+| map.cpp:304:15:304:19 | call to begin | map.cpp:308:8:308:9 | i2 | |
+| map.cpp:304:15:304:19 | call to begin | map.cpp:313:8:313:9 | i2 | |
+| map.cpp:304:15:304:19 | call to begin | map.cpp:314:8:314:9 | i2 | |
+| map.cpp:304:30:304:31 | m2 | map.cpp:304:33:304:35 | call to end | TAINT |
+| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:304:30:304:31 | m2 | |
+| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:334:7:334:8 | m2 | |
+| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
+| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
+| map.cpp:304:30:304:31 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
+| map.cpp:304:40:304:41 | i2 | map.cpp:304:42:304:42 | call to operator++ | |
+| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:304:24:304:25 | i2 | |
+| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:304:40:304:41 | i2 | |
+| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:306:9:306:10 | i2 | |
+| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:307:8:307:9 | i2 | |
+| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:308:8:308:9 | i2 | |
+| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:313:8:313:9 | i2 | |
+| map.cpp:304:40:304:41 | ref arg i2 | map.cpp:314:8:314:9 | i2 | |
+| map.cpp:306:8:306:8 | call to operator* | map.cpp:306:8:306:10 | call to pair | TAINT |
+| map.cpp:306:9:306:10 | i2 | map.cpp:306:8:306:8 | call to operator* | TAINT |
+| map.cpp:307:8:307:9 | i2 | map.cpp:307:10:307:10 | call to operator-> | TAINT |
+| map.cpp:308:8:308:9 | i2 | map.cpp:308:10:308:10 | call to operator-> | TAINT |
+| map.cpp:310:12:310:13 | m3 | map.cpp:310:15:310:19 | call to begin | TAINT |
+| map.cpp:310:12:310:13 | ref arg m3 | map.cpp:310:30:310:31 | m3 | |
+| map.cpp:310:12:310:13 | ref arg m3 | map.cpp:438:1:438:1 | m3 | |
+| map.cpp:310:15:310:19 | call to begin | map.cpp:310:7:310:21 | ... = ... | |
+| map.cpp:310:15:310:19 | call to begin | map.cpp:310:24:310:25 | i3 | |
+| map.cpp:310:15:310:19 | call to begin | map.cpp:310:40:310:41 | i3 | |
+| map.cpp:310:15:310:19 | call to begin | map.cpp:312:9:312:10 | i3 | |
+| map.cpp:310:30:310:31 | m3 | map.cpp:310:33:310:35 | call to end | TAINT |
+| map.cpp:310:30:310:31 | ref arg m3 | map.cpp:310:30:310:31 | m3 | |
+| map.cpp:310:30:310:31 | ref arg m3 | map.cpp:438:1:438:1 | m3 | |
+| map.cpp:310:40:310:41 | i3 | map.cpp:310:42:310:42 | call to operator++ | |
+| map.cpp:310:40:310:41 | ref arg i3 | map.cpp:310:24:310:25 | i3 | |
+| map.cpp:310:40:310:41 | ref arg i3 | map.cpp:310:40:310:41 | i3 | |
+| map.cpp:310:40:310:41 | ref arg i3 | map.cpp:312:9:312:10 | i3 | |
+| map.cpp:312:8:312:8 | call to operator* | map.cpp:312:8:312:10 | call to pair | TAINT |
+| map.cpp:312:9:312:10 | i3 | map.cpp:312:8:312:8 | call to operator* | TAINT |
+| map.cpp:313:8:313:9 | i2 | map.cpp:313:10:313:10 | call to operator-> | TAINT |
+| map.cpp:314:8:314:9 | i2 | map.cpp:314:10:314:10 | call to operator-> | TAINT |
+| map.cpp:318:37:318:39 | call to unordered_map | map.cpp:319:7:319:9 | m10 | |
+| map.cpp:318:37:318:39 | call to unordered_map | map.cpp:323:7:323:9 | m10 | |
+| map.cpp:318:37:318:39 | call to unordered_map | map.cpp:438:1:438:1 | m10 | |
+| map.cpp:318:42:318:44 | call to unordered_map | map.cpp:320:7:320:9 | m11 | |
+| map.cpp:318:42:318:44 | call to unordered_map | map.cpp:324:7:324:9 | m11 | |
+| map.cpp:318:42:318:44 | call to unordered_map | map.cpp:438:1:438:1 | m11 | |
+| map.cpp:318:47:318:49 | call to unordered_map | map.cpp:321:7:321:9 | m12 | |
+| map.cpp:318:47:318:49 | call to unordered_map | map.cpp:325:7:325:9 | m12 | |
+| map.cpp:318:47:318:49 | call to unordered_map | map.cpp:438:1:438:1 | m12 | |
+| map.cpp:318:52:318:54 | call to unordered_map | map.cpp:322:7:322:9 | m13 | |
+| map.cpp:318:52:318:54 | call to unordered_map | map.cpp:326:7:326:9 | m13 | |
+| map.cpp:318:52:318:54 | call to unordered_map | map.cpp:438:1:438:1 | m13 | |
+| map.cpp:319:7:319:9 | m10 | map.cpp:319:10:319:10 | call to operator[] | TAINT |
+| map.cpp:319:7:319:9 | ref arg m10 | map.cpp:323:7:323:9 | m10 | |
+| map.cpp:319:7:319:9 | ref arg m10 | map.cpp:438:1:438:1 | m10 | |
+| map.cpp:319:7:319:24 | ... = ... | map.cpp:319:10:319:10 | call to operator[] [post update] | |
+| map.cpp:319:10:319:10 | call to operator[] [post update] | map.cpp:319:7:319:9 | ref arg m10 | TAINT |
+| map.cpp:319:20:319:24 | def | map.cpp:319:7:319:24 | ... = ... | |
+| map.cpp:320:7:320:9 | m11 | map.cpp:320:10:320:10 | call to operator[] | TAINT |
+| map.cpp:320:7:320:9 | ref arg m11 | map.cpp:324:7:324:9 | m11 | |
+| map.cpp:320:7:320:9 | ref arg m11 | map.cpp:438:1:438:1 | m11 | |
+| map.cpp:320:7:320:27 | ... = ... | map.cpp:320:10:320:10 | call to operator[] [post update] | |
+| map.cpp:320:10:320:10 | call to operator[] [post update] | map.cpp:320:7:320:9 | ref arg m11 | TAINT |
+| map.cpp:320:20:320:25 | call to source | map.cpp:320:7:320:27 | ... = ... | |
+| map.cpp:321:7:321:9 | m12 | map.cpp:321:11:321:12 | call to at | TAINT |
+| map.cpp:321:7:321:9 | ref arg m12 | map.cpp:325:7:325:9 | m12 | |
+| map.cpp:321:7:321:9 | ref arg m12 | map.cpp:438:1:438:1 | m12 | |
+| map.cpp:321:7:321:27 | ... = ... | map.cpp:321:11:321:12 | call to at [post update] | |
+| map.cpp:321:11:321:12 | call to at [post update] | map.cpp:321:7:321:9 | ref arg m12 | TAINT |
+| map.cpp:321:23:321:27 | def | map.cpp:321:7:321:27 | ... = ... | |
+| map.cpp:322:7:322:9 | m13 | map.cpp:322:11:322:12 | call to at | TAINT |
+| map.cpp:322:7:322:9 | ref arg m13 | map.cpp:326:7:326:9 | m13 | |
+| map.cpp:322:7:322:9 | ref arg m13 | map.cpp:438:1:438:1 | m13 | |
+| map.cpp:322:7:322:30 | ... = ... | map.cpp:322:11:322:12 | call to at [post update] | |
+| map.cpp:322:11:322:12 | call to at [post update] | map.cpp:322:7:322:9 | ref arg m13 | TAINT |
+| map.cpp:322:23:322:28 | call to source | map.cpp:322:7:322:30 | ... = ... | |
+| map.cpp:323:7:323:9 | m10 | map.cpp:323:10:323:10 | call to operator[] | TAINT |
+| map.cpp:323:7:323:9 | ref arg m10 | map.cpp:438:1:438:1 | m10 | |
+| map.cpp:324:7:324:9 | m11 | map.cpp:324:10:324:10 | call to operator[] | TAINT |
+| map.cpp:324:7:324:9 | ref arg m11 | map.cpp:438:1:438:1 | m11 | |
+| map.cpp:325:7:325:9 | m12 | map.cpp:325:10:325:10 | call to operator[] | TAINT |
+| map.cpp:325:7:325:9 | ref arg m12 | map.cpp:438:1:438:1 | m12 | |
+| map.cpp:326:7:326:9 | m13 | map.cpp:326:10:326:10 | call to operator[] | TAINT |
+| map.cpp:326:7:326:9 | ref arg m13 | map.cpp:438:1:438:1 | m13 | |
+| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:330:2:330:4 | m14 | |
+| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:331:2:331:4 | m14 | |
+| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:332:2:332:4 | m14 | |
+| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:333:2:333:4 | m14 | |
+| map.cpp:329:37:329:39 | call to unordered_map | map.cpp:438:1:438:1 | m14 | |
+| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:331:2:331:4 | m14 | |
+| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:332:2:332:4 | m14 | |
+| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:333:2:333:4 | m14 | |
+| map.cpp:330:2:330:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | |
+| map.cpp:330:13:330:26 | call to make_pair | map.cpp:330:13:330:36 | call to pair | TAINT |
+| map.cpp:330:13:330:36 | call to pair | map.cpp:330:2:330:4 | ref arg m14 | TAINT |
+| map.cpp:330:13:330:36 | call to pair | map.cpp:330:6:330:11 | call to insert | TAINT |
+| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:332:2:332:4 | m14 | |
+| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:333:2:333:4 | m14 | |
+| map.cpp:331:2:331:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | |
+| map.cpp:331:13:331:26 | call to make_pair | map.cpp:331:13:331:41 | call to pair | TAINT |
+| map.cpp:331:13:331:41 | call to pair | map.cpp:331:2:331:4 | ref arg m14 | TAINT |
+| map.cpp:331:13:331:41 | call to pair | map.cpp:331:6:331:11 | call to insert | TAINT |
+| map.cpp:332:2:332:4 | ref arg m14 | map.cpp:333:2:333:4 | m14 | |
+| map.cpp:332:2:332:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | |
+| map.cpp:332:13:332:26 | call to make_pair | map.cpp:332:13:332:41 | call to pair | TAINT |
+| map.cpp:332:13:332:41 | call to pair | map.cpp:332:2:332:4 | ref arg m14 | TAINT |
+| map.cpp:332:13:332:41 | call to pair | map.cpp:332:6:332:11 | call to insert | TAINT |
+| map.cpp:333:2:333:4 | ref arg m14 | map.cpp:438:1:438:1 | m14 | |
+| map.cpp:333:13:333:26 | call to make_pair | map.cpp:333:13:333:36 | call to pair | TAINT |
+| map.cpp:333:13:333:36 | call to pair | map.cpp:333:2:333:4 | ref arg m14 | TAINT |
+| map.cpp:333:13:333:36 | call to pair | map.cpp:333:6:333:11 | call to insert | TAINT |
+| map.cpp:334:7:334:8 | m2 | map.cpp:334:10:334:20 | call to equal_range | TAINT |
+| map.cpp:334:7:334:8 | ref arg m2 | map.cpp:335:7:335:8 | m2 | |
+| map.cpp:334:7:334:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
+| map.cpp:334:7:334:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
+| map.cpp:334:27:334:31 | first | map.cpp:334:7:334:31 | call to iterator | |
+| map.cpp:335:7:335:8 | m2 | map.cpp:335:10:335:20 | call to equal_range | TAINT |
+| map.cpp:335:7:335:8 | ref arg m2 | map.cpp:336:7:336:8 | m2 | |
+| map.cpp:335:7:335:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
+| map.cpp:335:27:335:32 | second | map.cpp:335:7:335:32 | call to iterator | |
+| map.cpp:336:7:336:8 | m2 | map.cpp:336:10:336:20 | call to equal_range | TAINT |
+| map.cpp:336:7:336:8 | ref arg m2 | map.cpp:438:1:438:1 | m2 | |
+| map.cpp:336:27:336:32 | second | map.cpp:336:7:336:32 | call to iterator | |
+| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:340:2:340:4 | m15 | |
+| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:342:7:342:9 | m15 | |
+| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:346:2:346:4 | m15 | |
+| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:348:7:348:9 | m15 | |
+| map.cpp:339:37:339:39 | call to unordered_map | map.cpp:438:1:438:1 | m15 | |
+| map.cpp:339:42:339:44 | call to unordered_map | map.cpp:343:7:343:9 | m16 | |
+| map.cpp:339:42:339:44 | call to unordered_map | map.cpp:346:11:346:13 | m16 | |
+| map.cpp:339:42:339:44 | call to unordered_map | map.cpp:349:7:349:9 | m16 | |
+| map.cpp:339:42:339:44 | call to unordered_map | map.cpp:438:1:438:1 | m16 | |
+| map.cpp:339:47:339:49 | call to unordered_map | map.cpp:344:7:344:9 | m17 | |
+| map.cpp:339:47:339:49 | call to unordered_map | map.cpp:347:2:347:4 | m17 | |
+| map.cpp:339:47:339:49 | call to unordered_map | map.cpp:350:7:350:9 | m17 | |
+| map.cpp:339:47:339:49 | call to unordered_map | map.cpp:438:1:438:1 | m17 | |
+| map.cpp:339:52:339:54 | call to unordered_map | map.cpp:341:2:341:4 | m18 | |
+| map.cpp:339:52:339:54 | call to unordered_map | map.cpp:345:7:345:9 | m18 | |
+| map.cpp:339:52:339:54 | call to unordered_map | map.cpp:347:11:347:13 | m18 | |
+| map.cpp:339:52:339:54 | call to unordered_map | map.cpp:351:7:351:9 | m18 | |
+| map.cpp:339:52:339:54 | call to unordered_map | map.cpp:438:1:438:1 | m18 | |
+| map.cpp:340:2:340:4 | ref arg m15 | map.cpp:342:7:342:9 | m15 | |
+| map.cpp:340:2:340:4 | ref arg m15 | map.cpp:346:2:346:4 | m15 | |
+| map.cpp:340:2:340:4 | ref arg m15 | map.cpp:348:7:348:9 | m15 | |
+| map.cpp:340:2:340:4 | ref arg m15 | map.cpp:438:1:438:1 | m15 | |
+| map.cpp:340:13:340:57 | call to pair | map.cpp:340:2:340:4 | ref arg m15 | TAINT |
+| map.cpp:340:13:340:57 | call to pair | map.cpp:340:6:340:11 | call to insert | TAINT |
+| map.cpp:340:13:340:57 | call to pair | map.cpp:340:13:340:57 | call to pair | TAINT |
+| map.cpp:340:49:340:54 | call to source | map.cpp:340:13:340:57 | call to pair | TAINT |
+| map.cpp:341:2:341:4 | ref arg m18 | map.cpp:345:7:345:9 | m18 | |
+| map.cpp:341:2:341:4 | ref arg m18 | map.cpp:347:11:347:13 | m18 | |
+| map.cpp:341:2:341:4 | ref arg m18 | map.cpp:351:7:351:9 | m18 | |
+| map.cpp:341:2:341:4 | ref arg m18 | map.cpp:438:1:438:1 | m18 | |
+| map.cpp:341:13:341:57 | call to pair | map.cpp:341:2:341:4 | ref arg m18 | TAINT |
+| map.cpp:341:13:341:57 | call to pair | map.cpp:341:6:341:11 | call to insert | TAINT |
+| map.cpp:341:13:341:57 | call to pair | map.cpp:341:13:341:57 | call to pair | TAINT |
+| map.cpp:341:49:341:54 | call to source | map.cpp:341:13:341:57 | call to pair | TAINT |
+| map.cpp:342:7:342:9 | m15 | map.cpp:342:7:342:9 | call to unordered_map | |
+| map.cpp:343:7:343:9 | m16 | map.cpp:343:7:343:9 | call to unordered_map | |
+| map.cpp:344:7:344:9 | m17 | map.cpp:344:7:344:9 | call to unordered_map | |
+| map.cpp:345:7:345:9 | m18 | map.cpp:345:7:345:9 | call to unordered_map | |
+| map.cpp:346:2:346:4 | m15 | map.cpp:346:11:346:13 | ref arg m16 | TAINT |
+| map.cpp:346:2:346:4 | ref arg m15 | map.cpp:348:7:348:9 | m15 | |
+| map.cpp:346:2:346:4 | ref arg m15 | map.cpp:438:1:438:1 | m15 | |
+| map.cpp:346:11:346:13 | m16 | map.cpp:346:2:346:4 | ref arg m15 | TAINT |
+| map.cpp:346:11:346:13 | ref arg m16 | map.cpp:349:7:349:9 | m16 | |
+| map.cpp:346:11:346:13 | ref arg m16 | map.cpp:438:1:438:1 | m16 | |
+| map.cpp:347:2:347:4 | m17 | map.cpp:347:11:347:13 | ref arg m18 | TAINT |
+| map.cpp:347:2:347:4 | ref arg m17 | map.cpp:350:7:350:9 | m17 | |
+| map.cpp:347:2:347:4 | ref arg m17 | map.cpp:438:1:438:1 | m17 | |
+| map.cpp:347:11:347:13 | m18 | map.cpp:347:2:347:4 | ref arg m17 | TAINT |
+| map.cpp:347:11:347:13 | ref arg m18 | map.cpp:351:7:351:9 | m18 | |
+| map.cpp:347:11:347:13 | ref arg m18 | map.cpp:438:1:438:1 | m18 | |
+| map.cpp:348:7:348:9 | m15 | map.cpp:348:7:348:9 | call to unordered_map | |
+| map.cpp:349:7:349:9 | m16 | map.cpp:349:7:349:9 | call to unordered_map | |
+| map.cpp:350:7:350:9 | m17 | map.cpp:350:7:350:9 | call to unordered_map | |
+| map.cpp:351:7:351:9 | m18 | map.cpp:351:7:351:9 | call to unordered_map | |
+| map.cpp:354:37:354:39 | call to unordered_map | map.cpp:355:2:355:4 | m19 | |
+| map.cpp:354:37:354:39 | call to unordered_map | map.cpp:359:7:359:9 | m19 | |
+| map.cpp:354:37:354:39 | call to unordered_map | map.cpp:363:2:363:4 | m19 | |
+| map.cpp:354:37:354:39 | call to unordered_map | map.cpp:365:7:365:9 | m19 | |
+| map.cpp:354:37:354:39 | call to unordered_map | map.cpp:438:1:438:1 | m19 | |
+| map.cpp:354:42:354:44 | call to unordered_map | map.cpp:356:2:356:4 | m20 | |
+| map.cpp:354:42:354:44 | call to unordered_map | map.cpp:360:7:360:9 | m20 | |
+| map.cpp:354:42:354:44 | call to unordered_map | map.cpp:363:12:363:14 | m20 | |
+| map.cpp:354:42:354:44 | call to unordered_map | map.cpp:366:7:366:9 | m20 | |
+| map.cpp:354:42:354:44 | call to unordered_map | map.cpp:438:1:438:1 | m20 | |
+| map.cpp:354:47:354:49 | call to unordered_map | map.cpp:357:2:357:4 | m21 | |
+| map.cpp:354:47:354:49 | call to unordered_map | map.cpp:361:7:361:9 | m21 | |
+| map.cpp:354:47:354:49 | call to unordered_map | map.cpp:364:2:364:4 | m21 | |
+| map.cpp:354:47:354:49 | call to unordered_map | map.cpp:367:7:367:9 | m21 | |
+| map.cpp:354:47:354:49 | call to unordered_map | map.cpp:438:1:438:1 | m21 | |
+| map.cpp:354:52:354:54 | call to unordered_map | map.cpp:358:2:358:4 | m22 | |
+| map.cpp:354:52:354:54 | call to unordered_map | map.cpp:362:7:362:9 | m22 | |
+| map.cpp:354:52:354:54 | call to unordered_map | map.cpp:364:12:364:14 | m22 | |
+| map.cpp:354:52:354:54 | call to unordered_map | map.cpp:368:7:368:9 | m22 | |
+| map.cpp:354:52:354:54 | call to unordered_map | map.cpp:438:1:438:1 | m22 | |
+| map.cpp:355:2:355:4 | ref arg m19 | map.cpp:359:7:359:9 | m19 | |
+| map.cpp:355:2:355:4 | ref arg m19 | map.cpp:363:2:363:4 | m19 | |
+| map.cpp:355:2:355:4 | ref arg m19 | map.cpp:365:7:365:9 | m19 | |
+| map.cpp:355:2:355:4 | ref arg m19 | map.cpp:438:1:438:1 | m19 | |
+| map.cpp:355:13:355:57 | call to pair | map.cpp:355:2:355:4 | ref arg m19 | TAINT |
+| map.cpp:355:13:355:57 | call to pair | map.cpp:355:6:355:11 | call to insert | TAINT |
+| map.cpp:355:13:355:57 | call to pair | map.cpp:355:13:355:57 | call to pair | TAINT |
+| map.cpp:355:49:355:54 | call to source | map.cpp:355:13:355:57 | call to pair | TAINT |
+| map.cpp:356:2:356:4 | ref arg m20 | map.cpp:360:7:360:9 | m20 | |
+| map.cpp:356:2:356:4 | ref arg m20 | map.cpp:363:12:363:14 | m20 | |
+| map.cpp:356:2:356:4 | ref arg m20 | map.cpp:366:7:366:9 | m20 | |
+| map.cpp:356:2:356:4 | ref arg m20 | map.cpp:438:1:438:1 | m20 | |
+| map.cpp:356:13:356:51 | call to pair | map.cpp:356:2:356:4 | ref arg m20 | TAINT |
+| map.cpp:356:13:356:51 | call to pair | map.cpp:356:6:356:11 | call to insert | TAINT |
+| map.cpp:356:13:356:51 | call to pair | map.cpp:356:13:356:51 | call to pair | TAINT |
+| map.cpp:356:46:356:50 | def | map.cpp:356:13:356:51 | call to pair | TAINT |
+| map.cpp:357:2:357:4 | ref arg m21 | map.cpp:361:7:361:9 | m21 | |
+| map.cpp:357:2:357:4 | ref arg m21 | map.cpp:364:2:364:4 | m21 | |
+| map.cpp:357:2:357:4 | ref arg m21 | map.cpp:367:7:367:9 | m21 | |
+| map.cpp:357:2:357:4 | ref arg m21 | map.cpp:438:1:438:1 | m21 | |
+| map.cpp:357:13:357:51 | call to pair | map.cpp:357:2:357:4 | ref arg m21 | TAINT |
+| map.cpp:357:13:357:51 | call to pair | map.cpp:357:6:357:11 | call to insert | TAINT |
+| map.cpp:357:13:357:51 | call to pair | map.cpp:357:13:357:51 | call to pair | TAINT |
+| map.cpp:357:46:357:50 | def | map.cpp:357:13:357:51 | call to pair | TAINT |
+| map.cpp:358:2:358:4 | ref arg m22 | map.cpp:362:7:362:9 | m22 | |
+| map.cpp:358:2:358:4 | ref arg m22 | map.cpp:364:12:364:14 | m22 | |
+| map.cpp:358:2:358:4 | ref arg m22 | map.cpp:368:7:368:9 | m22 | |
+| map.cpp:358:2:358:4 | ref arg m22 | map.cpp:438:1:438:1 | m22 | |
+| map.cpp:358:13:358:57 | call to pair | map.cpp:358:2:358:4 | ref arg m22 | TAINT |
+| map.cpp:358:13:358:57 | call to pair | map.cpp:358:6:358:11 | call to insert | TAINT |
+| map.cpp:358:13:358:57 | call to pair | map.cpp:358:13:358:57 | call to pair | TAINT |
+| map.cpp:358:49:358:54 | call to source | map.cpp:358:13:358:57 | call to pair | TAINT |
+| map.cpp:359:7:359:9 | m19 | map.cpp:359:7:359:9 | call to unordered_map | |
+| map.cpp:360:7:360:9 | m20 | map.cpp:360:7:360:9 | call to unordered_map | |
+| map.cpp:361:7:361:9 | m21 | map.cpp:361:7:361:9 | call to unordered_map | |
+| map.cpp:362:7:362:9 | m22 | map.cpp:362:7:362:9 | call to unordered_map | |
+| map.cpp:363:2:363:4 | ref arg m19 | map.cpp:365:7:365:9 | m19 | |
+| map.cpp:363:2:363:4 | ref arg m19 | map.cpp:438:1:438:1 | m19 | |
+| map.cpp:363:12:363:14 | m20 | map.cpp:363:2:363:4 | ref arg m19 | TAINT |
+| map.cpp:363:12:363:14 | ref arg m20 | map.cpp:366:7:366:9 | m20 | |
+| map.cpp:363:12:363:14 | ref arg m20 | map.cpp:438:1:438:1 | m20 | |
+| map.cpp:364:2:364:4 | ref arg m21 | map.cpp:367:7:367:9 | m21 | |
+| map.cpp:364:2:364:4 | ref arg m21 | map.cpp:438:1:438:1 | m21 | |
+| map.cpp:364:12:364:14 | m22 | map.cpp:364:2:364:4 | ref arg m21 | TAINT |
+| map.cpp:364:12:364:14 | ref arg m22 | map.cpp:368:7:368:9 | m22 | |
+| map.cpp:364:12:364:14 | ref arg m22 | map.cpp:438:1:438:1 | m22 | |
+| map.cpp:365:7:365:9 | m19 | map.cpp:365:7:365:9 | call to unordered_map | |
+| map.cpp:366:7:366:9 | m20 | map.cpp:366:7:366:9 | call to unordered_map | |
+| map.cpp:367:7:367:9 | m21 | map.cpp:367:7:367:9 | call to unordered_map | |
+| map.cpp:368:7:368:9 | m22 | map.cpp:368:7:368:9 | call to unordered_map | |
+| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:372:2:372:4 | m23 | |
+| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:373:2:373:4 | m23 | |
+| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:374:7:374:9 | m23 | |
+| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:375:7:375:9 | m23 | |
+| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:375:17:375:19 | m23 | |
+| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:376:7:376:9 | m23 | |
+| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:377:2:377:4 | m23 | |
+| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:378:7:378:9 | m23 | |
+| map.cpp:371:37:371:39 | call to unordered_map | map.cpp:438:1:438:1 | m23 | |
+| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:373:2:373:4 | m23 | |
+| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:374:7:374:9 | m23 | |
+| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:375:7:375:9 | m23 | |
+| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:375:17:375:19 | m23 | |
+| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:376:7:376:9 | m23 | |
+| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:377:2:377:4 | m23 | |
+| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:378:7:378:9 | m23 | |
+| map.cpp:372:2:372:4 | ref arg m23 | map.cpp:438:1:438:1 | m23 | |
+| map.cpp:372:13:372:57 | call to pair | map.cpp:372:2:372:4 | ref arg m23 | TAINT |
+| map.cpp:372:13:372:57 | call to pair | map.cpp:372:6:372:11 | call to insert | TAINT |
+| map.cpp:372:13:372:57 | call to pair | map.cpp:372:13:372:57 | call to pair | TAINT |
+| map.cpp:372:49:372:54 | call to source | map.cpp:372:13:372:57 | call to pair | TAINT |
+| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:374:7:374:9 | m23 | |
+| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:375:7:375:9 | m23 | |
+| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:375:17:375:19 | m23 | |
+| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:376:7:376:9 | m23 | |
+| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:377:2:377:4 | m23 | |
+| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:378:7:378:9 | m23 | |
+| map.cpp:373:2:373:4 | ref arg m23 | map.cpp:438:1:438:1 | m23 | |
+| map.cpp:373:13:373:57 | call to pair | map.cpp:373:2:373:4 | ref arg m23 | TAINT |
+| map.cpp:373:13:373:57 | call to pair | map.cpp:373:6:373:11 | call to insert | TAINT |
+| map.cpp:373:13:373:57 | call to pair | map.cpp:373:13:373:57 | call to pair | TAINT |
+| map.cpp:373:49:373:54 | call to source | map.cpp:373:13:373:57 | call to pair | TAINT |
+| map.cpp:374:7:374:9 | m23 | map.cpp:374:7:374:9 | call to unordered_map | |
+| map.cpp:375:7:375:9 | m23 | map.cpp:375:11:375:15 | call to erase | TAINT |
+| map.cpp:375:7:375:9 | ref arg m23 | map.cpp:376:7:376:9 | m23 | |
+| map.cpp:375:7:375:9 | ref arg m23 | map.cpp:377:2:377:4 | m23 | |
+| map.cpp:375:7:375:9 | ref arg m23 | map.cpp:378:7:378:9 | m23 | |
+| map.cpp:375:7:375:9 | ref arg m23 | map.cpp:438:1:438:1 | m23 | |
+| map.cpp:375:17:375:19 | m23 | map.cpp:375:21:375:25 | call to begin | TAINT |
+| map.cpp:375:17:375:19 | ref arg m23 | map.cpp:375:7:375:9 | m23 | |
+| map.cpp:375:17:375:19 | ref arg m23 | map.cpp:376:7:376:9 | m23 | |
+| map.cpp:375:17:375:19 | ref arg m23 | map.cpp:377:2:377:4 | m23 | |
+| map.cpp:375:17:375:19 | ref arg m23 | map.cpp:378:7:378:9 | m23 | |
+| map.cpp:375:17:375:19 | ref arg m23 | map.cpp:438:1:438:1 | m23 | |
+| map.cpp:376:7:376:9 | m23 | map.cpp:376:7:376:9 | call to unordered_map | |
+| map.cpp:377:2:377:4 | ref arg m23 | map.cpp:378:7:378:9 | m23 | |
+| map.cpp:377:2:377:4 | ref arg m23 | map.cpp:438:1:438:1 | m23 | |
+| map.cpp:378:7:378:9 | m23 | map.cpp:378:7:378:9 | call to unordered_map | |
+| map.cpp:381:37:381:39 | call to unordered_map | map.cpp:382:7:382:9 | m24 | |
+| map.cpp:381:37:381:39 | call to unordered_map | map.cpp:383:7:383:9 | m24 | |
+| map.cpp:381:37:381:39 | call to unordered_map | map.cpp:384:7:384:9 | m24 | |
+| map.cpp:381:37:381:39 | call to unordered_map | map.cpp:385:7:385:9 | m24 | |
+| map.cpp:381:37:381:39 | call to unordered_map | map.cpp:438:1:438:1 | m24 | |
+| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:386:7:386:9 | m25 | |
+| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:386:24:386:26 | m25 | |
+| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:387:7:387:9 | m25 | |
+| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:388:7:388:9 | m25 | |
+| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:388:24:388:26 | m25 | |
+| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:389:7:389:9 | m25 | |
+| map.cpp:381:42:381:44 | call to unordered_map | map.cpp:438:1:438:1 | m25 | |
+| map.cpp:382:7:382:9 | m24 | map.cpp:382:11:382:17 | call to emplace | TAINT |
+| map.cpp:382:7:382:9 | ref arg m24 | map.cpp:383:7:383:9 | m24 | |
+| map.cpp:382:7:382:9 | ref arg m24 | map.cpp:384:7:384:9 | m24 | |
+| map.cpp:382:7:382:9 | ref arg m24 | map.cpp:385:7:385:9 | m24 | |
+| map.cpp:382:7:382:9 | ref arg m24 | map.cpp:438:1:438:1 | m24 | |
+| map.cpp:382:26:382:30 | def | map.cpp:382:7:382:9 | ref arg m24 | TAINT |
+| map.cpp:382:26:382:30 | def | map.cpp:382:11:382:17 | call to emplace | TAINT |
+| map.cpp:382:33:382:37 | first | map.cpp:382:7:382:37 | call to iterator | |
+| map.cpp:383:7:383:9 | m24 | map.cpp:383:7:383:9 | call to unordered_map | |
+| map.cpp:384:7:384:9 | m24 | map.cpp:384:11:384:17 | call to emplace | TAINT |
+| map.cpp:384:7:384:9 | ref arg m24 | map.cpp:385:7:385:9 | m24 | |
+| map.cpp:384:7:384:9 | ref arg m24 | map.cpp:438:1:438:1 | m24 | |
+| map.cpp:384:26:384:31 | call to source | map.cpp:384:7:384:9 | ref arg m24 | TAINT |
+| map.cpp:384:26:384:31 | call to source | map.cpp:384:11:384:17 | call to emplace | TAINT |
+| map.cpp:384:36:384:40 | first | map.cpp:384:7:384:40 | call to iterator | |
+| map.cpp:385:7:385:9 | m24 | map.cpp:385:7:385:9 | call to unordered_map | |
+| map.cpp:386:7:386:9 | m25 | map.cpp:386:11:386:22 | call to emplace_hint | TAINT |
+| map.cpp:386:7:386:9 | ref arg m25 | map.cpp:387:7:387:9 | m25 | |
+| map.cpp:386:7:386:9 | ref arg m25 | map.cpp:388:7:388:9 | m25 | |
+| map.cpp:386:7:386:9 | ref arg m25 | map.cpp:388:24:388:26 | m25 | |
+| map.cpp:386:7:386:9 | ref arg m25 | map.cpp:389:7:389:9 | m25 | |
+| map.cpp:386:7:386:9 | ref arg m25 | map.cpp:438:1:438:1 | m25 | |
+| map.cpp:386:24:386:26 | m25 | map.cpp:386:28:386:32 | call to begin | TAINT |
+| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:386:7:386:9 | m25 | |
+| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:387:7:387:9 | m25 | |
+| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:388:7:388:9 | m25 | |
+| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:388:24:388:26 | m25 | |
+| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:389:7:389:9 | m25 | |
+| map.cpp:386:24:386:26 | ref arg m25 | map.cpp:438:1:438:1 | m25 | |
+| map.cpp:386:28:386:32 | call to begin | map.cpp:386:24:386:34 | call to iterator | TAINT |
+| map.cpp:386:44:386:48 | def | map.cpp:386:7:386:9 | ref arg m25 | TAINT |
+| map.cpp:386:44:386:48 | def | map.cpp:386:11:386:22 | call to emplace_hint | TAINT |
+| map.cpp:387:7:387:9 | m25 | map.cpp:387:7:387:9 | call to unordered_map | |
+| map.cpp:388:7:388:9 | m25 | map.cpp:388:11:388:22 | call to emplace_hint | TAINT |
+| map.cpp:388:7:388:9 | ref arg m25 | map.cpp:389:7:389:9 | m25 | |
+| map.cpp:388:7:388:9 | ref arg m25 | map.cpp:438:1:438:1 | m25 | |
+| map.cpp:388:24:388:26 | m25 | map.cpp:388:28:388:32 | call to begin | TAINT |
+| map.cpp:388:24:388:26 | ref arg m25 | map.cpp:388:7:388:9 | m25 | |
+| map.cpp:388:24:388:26 | ref arg m25 | map.cpp:389:7:389:9 | m25 | |
+| map.cpp:388:24:388:26 | ref arg m25 | map.cpp:438:1:438:1 | m25 | |
+| map.cpp:388:28:388:32 | call to begin | map.cpp:388:24:388:34 | call to iterator | TAINT |
+| map.cpp:388:44:388:49 | call to source | map.cpp:388:7:388:9 | ref arg m25 | TAINT |
+| map.cpp:388:44:388:49 | call to source | map.cpp:388:11:388:22 | call to emplace_hint | TAINT |
+| map.cpp:389:7:389:9 | m25 | map.cpp:389:7:389:9 | call to unordered_map | |
+| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:393:7:393:9 | m26 | |
+| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:394:7:394:9 | m26 | |
+| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:395:7:395:9 | m26 | |
+| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:396:7:396:9 | m26 | |
+| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:397:7:397:9 | m26 | |
+| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:398:7:398:9 | m26 | |
+| map.cpp:392:37:392:39 | call to unordered_map | map.cpp:438:1:438:1 | m26 | |
+| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:399:7:399:9 | m27 | |
+| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:399:23:399:25 | m27 | |
+| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:400:7:400:9 | m27 | |
+| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:401:7:401:9 | m27 | |
+| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:401:23:401:25 | m27 | |
+| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:402:7:402:9 | m27 | |
+| map.cpp:392:42:392:44 | call to unordered_map | map.cpp:438:1:438:1 | m27 | |
+| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:403:7:403:9 | m28 | |
+| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:403:23:403:25 | m28 | |
+| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:404:7:404:9 | m28 | |
+| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:405:7:405:9 | m28 | |
+| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:405:23:405:25 | m28 | |
+| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:406:7:406:9 | m28 | |
+| map.cpp:392:47:392:49 | call to unordered_map | map.cpp:438:1:438:1 | m28 | |
+| map.cpp:393:7:393:9 | m26 | map.cpp:393:11:393:21 | call to try_emplace | TAINT |
+| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:394:7:394:9 | m26 | |
+| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:395:7:395:9 | m26 | |
+| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:396:7:396:9 | m26 | |
+| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:397:7:397:9 | m26 | |
+| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:398:7:398:9 | m26 | |
+| map.cpp:393:7:393:9 | ref arg m26 | map.cpp:438:1:438:1 | m26 | |
+| map.cpp:393:30:393:34 | def | map.cpp:393:7:393:9 | ref arg m26 | TAINT |
+| map.cpp:393:30:393:34 | def | map.cpp:393:11:393:21 | call to try_emplace | TAINT |
+| map.cpp:393:37:393:41 | first | map.cpp:393:7:393:41 | call to iterator | |
+| map.cpp:394:7:394:9 | m26 | map.cpp:394:11:394:21 | call to try_emplace | TAINT |
+| map.cpp:394:7:394:9 | ref arg m26 | map.cpp:395:7:395:9 | m26 | |
+| map.cpp:394:7:394:9 | ref arg m26 | map.cpp:396:7:396:9 | m26 | |
+| map.cpp:394:7:394:9 | ref arg m26 | map.cpp:397:7:397:9 | m26 | |
+| map.cpp:394:7:394:9 | ref arg m26 | map.cpp:398:7:398:9 | m26 | |
+| map.cpp:394:7:394:9 | ref arg m26 | map.cpp:438:1:438:1 | m26 | |
+| map.cpp:394:30:394:34 | def | map.cpp:394:7:394:9 | ref arg m26 | TAINT |
+| map.cpp:394:30:394:34 | def | map.cpp:394:11:394:21 | call to try_emplace | TAINT |
+| map.cpp:395:7:395:9 | m26 | map.cpp:395:7:395:9 | call to unordered_map | |
+| map.cpp:396:7:396:9 | m26 | map.cpp:396:11:396:21 | call to try_emplace | TAINT |
+| map.cpp:396:7:396:9 | ref arg m26 | map.cpp:397:7:397:9 | m26 | |
+| map.cpp:396:7:396:9 | ref arg m26 | map.cpp:398:7:398:9 | m26 | |
+| map.cpp:396:7:396:9 | ref arg m26 | map.cpp:438:1:438:1 | m26 | |
+| map.cpp:396:30:396:35 | call to source | map.cpp:396:7:396:9 | ref arg m26 | TAINT |
+| map.cpp:396:30:396:35 | call to source | map.cpp:396:11:396:21 | call to try_emplace | TAINT |
+| map.cpp:396:40:396:44 | first | map.cpp:396:7:396:44 | call to iterator | |
+| map.cpp:397:7:397:9 | m26 | map.cpp:397:11:397:21 | call to try_emplace | TAINT |
+| map.cpp:397:7:397:9 | ref arg m26 | map.cpp:398:7:398:9 | m26 | |
+| map.cpp:397:7:397:9 | ref arg m26 | map.cpp:438:1:438:1 | m26 | |
+| map.cpp:397:30:397:35 | call to source | map.cpp:397:7:397:9 | ref arg m26 | TAINT |
+| map.cpp:397:30:397:35 | call to source | map.cpp:397:11:397:21 | call to try_emplace | TAINT |
+| map.cpp:398:7:398:9 | m26 | map.cpp:398:7:398:9 | call to unordered_map | |
+| map.cpp:399:7:399:9 | m27 | map.cpp:399:11:399:21 | call to try_emplace | TAINT |
+| map.cpp:399:7:399:9 | ref arg m27 | map.cpp:400:7:400:9 | m27 | |
+| map.cpp:399:7:399:9 | ref arg m27 | map.cpp:401:7:401:9 | m27 | |
+| map.cpp:399:7:399:9 | ref arg m27 | map.cpp:401:23:401:25 | m27 | |
+| map.cpp:399:7:399:9 | ref arg m27 | map.cpp:402:7:402:9 | m27 | |
+| map.cpp:399:7:399:9 | ref arg m27 | map.cpp:438:1:438:1 | m27 | |
+| map.cpp:399:23:399:25 | m27 | map.cpp:399:27:399:31 | call to begin | TAINT |
+| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:399:7:399:9 | m27 | |
+| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:400:7:400:9 | m27 | |
+| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:401:7:401:9 | m27 | |
+| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:401:23:401:25 | m27 | |
+| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:402:7:402:9 | m27 | |
+| map.cpp:399:23:399:25 | ref arg m27 | map.cpp:438:1:438:1 | m27 | |
+| map.cpp:399:27:399:31 | call to begin | map.cpp:399:23:399:33 | call to iterator | TAINT |
+| map.cpp:399:43:399:47 | def | map.cpp:399:7:399:9 | ref arg m27 | TAINT |
+| map.cpp:399:43:399:47 | def | map.cpp:399:11:399:21 | call to try_emplace | TAINT |
+| map.cpp:400:7:400:9 | m27 | map.cpp:400:7:400:9 | call to unordered_map | |
+| map.cpp:401:7:401:9 | m27 | map.cpp:401:11:401:21 | call to try_emplace | TAINT |
+| map.cpp:401:7:401:9 | ref arg m27 | map.cpp:402:7:402:9 | m27 | |
+| map.cpp:401:7:401:9 | ref arg m27 | map.cpp:438:1:438:1 | m27 | |
+| map.cpp:401:23:401:25 | m27 | map.cpp:401:27:401:31 | call to begin | TAINT |
+| map.cpp:401:23:401:25 | ref arg m27 | map.cpp:401:7:401:9 | m27 | |
+| map.cpp:401:23:401:25 | ref arg m27 | map.cpp:402:7:402:9 | m27 | |
+| map.cpp:401:23:401:25 | ref arg m27 | map.cpp:438:1:438:1 | m27 | |
+| map.cpp:401:27:401:31 | call to begin | map.cpp:401:23:401:33 | call to iterator | TAINT |
+| map.cpp:401:43:401:48 | call to source | map.cpp:401:7:401:9 | ref arg m27 | TAINT |
+| map.cpp:401:43:401:48 | call to source | map.cpp:401:11:401:21 | call to try_emplace | TAINT |
+| map.cpp:402:7:402:9 | m27 | map.cpp:402:7:402:9 | call to unordered_map | |
+| map.cpp:403:7:403:9 | m28 | map.cpp:403:11:403:21 | call to try_emplace | TAINT |
+| map.cpp:403:7:403:9 | ref arg m28 | map.cpp:404:7:404:9 | m28 | |
+| map.cpp:403:7:403:9 | ref arg m28 | map.cpp:405:7:405:9 | m28 | |
+| map.cpp:403:7:403:9 | ref arg m28 | map.cpp:405:23:405:25 | m28 | |
+| map.cpp:403:7:403:9 | ref arg m28 | map.cpp:406:7:406:9 | m28 | |
+| map.cpp:403:7:403:9 | ref arg m28 | map.cpp:438:1:438:1 | m28 | |
+| map.cpp:403:23:403:25 | m28 | map.cpp:403:27:403:31 | call to begin | TAINT |
+| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:403:7:403:9 | m28 | |
+| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:404:7:404:9 | m28 | |
+| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:405:7:405:9 | m28 | |
+| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:405:23:405:25 | m28 | |
+| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:406:7:406:9 | m28 | |
+| map.cpp:403:23:403:25 | ref arg m28 | map.cpp:438:1:438:1 | m28 | |
+| map.cpp:403:27:403:31 | call to begin | map.cpp:403:23:403:33 | call to iterator | TAINT |
+| map.cpp:403:43:403:47 | def | map.cpp:403:7:403:9 | ref arg m28 | TAINT |
+| map.cpp:403:43:403:47 | def | map.cpp:403:11:403:21 | call to try_emplace | TAINT |
+| map.cpp:404:7:404:9 | m28 | map.cpp:404:7:404:9 | call to unordered_map | |
+| map.cpp:405:7:405:9 | m28 | map.cpp:405:11:405:21 | call to try_emplace | TAINT |
+| map.cpp:405:7:405:9 | ref arg m28 | map.cpp:406:7:406:9 | m28 | |
+| map.cpp:405:7:405:9 | ref arg m28 | map.cpp:438:1:438:1 | m28 | |
+| map.cpp:405:23:405:25 | m28 | map.cpp:405:27:405:31 | call to begin | TAINT |
+| map.cpp:405:23:405:25 | ref arg m28 | map.cpp:405:7:405:9 | m28 | |
+| map.cpp:405:23:405:25 | ref arg m28 | map.cpp:406:7:406:9 | m28 | |
+| map.cpp:405:23:405:25 | ref arg m28 | map.cpp:438:1:438:1 | m28 | |
+| map.cpp:405:27:405:31 | call to begin | map.cpp:405:23:405:33 | call to iterator | TAINT |
+| map.cpp:405:46:405:50 | def | map.cpp:405:7:405:9 | ref arg m28 | TAINT |
+| map.cpp:405:46:405:50 | def | map.cpp:405:11:405:21 | call to try_emplace | TAINT |
+| map.cpp:406:7:406:9 | m28 | map.cpp:406:7:406:9 | call to unordered_map | |
+| map.cpp:409:50:409:52 | call to unordered_map | map.cpp:410:7:410:9 | m29 | |
+| map.cpp:409:50:409:52 | call to unordered_map | map.cpp:411:7:411:9 | m29 | |
+| map.cpp:409:50:409:52 | call to unordered_map | map.cpp:412:7:412:9 | m29 | |
+| map.cpp:409:50:409:52 | call to unordered_map | map.cpp:438:1:438:1 | m29 | |
+| map.cpp:409:55:409:57 | call to unordered_map | map.cpp:413:7:413:9 | m30 | |
+| map.cpp:409:55:409:57 | call to unordered_map | map.cpp:414:7:414:9 | m30 | |
+| map.cpp:409:55:409:57 | call to unordered_map | map.cpp:415:7:415:9 | m30 | |
+| map.cpp:409:55:409:57 | call to unordered_map | map.cpp:438:1:438:1 | m30 | |
+| map.cpp:409:60:409:62 | call to unordered_map | map.cpp:416:7:416:9 | m31 | |
+| map.cpp:409:60:409:62 | call to unordered_map | map.cpp:417:7:417:9 | m31 | |
+| map.cpp:409:60:409:62 | call to unordered_map | map.cpp:418:7:418:9 | m31 | |
+| map.cpp:409:60:409:62 | call to unordered_map | map.cpp:438:1:438:1 | m31 | |
+| map.cpp:409:65:409:67 | call to unordered_map | map.cpp:419:7:419:9 | m32 | |
+| map.cpp:409:65:409:67 | call to unordered_map | map.cpp:420:7:420:9 | m32 | |
+| map.cpp:409:65:409:67 | call to unordered_map | map.cpp:421:7:421:9 | m32 | |
+| map.cpp:409:65:409:67 | call to unordered_map | map.cpp:438:1:438:1 | m32 | |
+| map.cpp:410:7:410:9 | m29 | map.cpp:410:11:410:21 | call to try_emplace | TAINT |
+| map.cpp:410:7:410:9 | ref arg m29 | map.cpp:411:7:411:9 | m29 | |
+| map.cpp:410:7:410:9 | ref arg m29 | map.cpp:412:7:412:9 | m29 | |
+| map.cpp:410:7:410:9 | ref arg m29 | map.cpp:438:1:438:1 | m29 | |
+| map.cpp:410:11:410:21 | call to try_emplace | map.cpp:410:7:410:34 | call to pair | TAINT |
+| map.cpp:410:30:410:30 | 1 | map.cpp:410:7:410:9 | ref arg m29 | TAINT |
+| map.cpp:410:30:410:30 | 1 | map.cpp:410:11:410:21 | call to try_emplace | TAINT |
+| map.cpp:410:33:410:33 | 2 | map.cpp:410:7:410:9 | ref arg m29 | TAINT |
+| map.cpp:410:33:410:33 | 2 | map.cpp:410:11:410:21 | call to try_emplace | TAINT |
+| map.cpp:411:7:411:9 | m29 | map.cpp:411:7:411:9 | call to unordered_map | |
+| map.cpp:412:7:412:9 | m29 | map.cpp:412:10:412:10 | call to operator[] | TAINT |
+| map.cpp:412:7:412:9 | ref arg m29 | map.cpp:438:1:438:1 | m29 | |
+| map.cpp:412:10:412:10 | call to operator[] | map.cpp:412:7:412:16 | call to pair | TAINT |
+| map.cpp:413:7:413:9 | m30 | map.cpp:413:11:413:21 | call to try_emplace | TAINT |
+| map.cpp:413:7:413:9 | ref arg m30 | map.cpp:414:7:414:9 | m30 | |
+| map.cpp:413:7:413:9 | ref arg m30 | map.cpp:415:7:415:9 | m30 | |
+| map.cpp:413:7:413:9 | ref arg m30 | map.cpp:438:1:438:1 | m30 | |
+| map.cpp:413:11:413:21 | call to try_emplace | map.cpp:413:7:413:37 | call to pair | TAINT |
+| map.cpp:413:33:413:33 | 1 | map.cpp:413:7:413:9 | ref arg m30 | TAINT |
+| map.cpp:413:33:413:33 | 1 | map.cpp:413:11:413:21 | call to try_emplace | TAINT |
+| map.cpp:413:36:413:36 | 2 | map.cpp:413:7:413:9 | ref arg m30 | TAINT |
+| map.cpp:413:36:413:36 | 2 | map.cpp:413:11:413:21 | call to try_emplace | TAINT |
+| map.cpp:414:7:414:9 | m30 | map.cpp:414:7:414:9 | call to unordered_map | |
+| map.cpp:415:7:415:9 | m30 | map.cpp:415:10:415:10 | call to operator[] | TAINT |
+| map.cpp:415:7:415:9 | ref arg m30 | map.cpp:438:1:438:1 | m30 | |
+| map.cpp:415:10:415:10 | call to operator[] | map.cpp:415:7:415:16 | call to pair | TAINT |
+| map.cpp:416:7:416:9 | m31 | map.cpp:416:11:416:21 | call to try_emplace | TAINT |
+| map.cpp:416:7:416:9 | ref arg m31 | map.cpp:417:7:417:9 | m31 | |
+| map.cpp:416:7:416:9 | ref arg m31 | map.cpp:418:7:418:9 | m31 | |
+| map.cpp:416:7:416:9 | ref arg m31 | map.cpp:438:1:438:1 | m31 | |
+| map.cpp:416:11:416:21 | call to try_emplace | map.cpp:416:7:416:41 | call to pair | TAINT |
+| map.cpp:416:30:416:35 | call to source | map.cpp:416:7:416:9 | ref arg m31 | TAINT |
+| map.cpp:416:30:416:35 | call to source | map.cpp:416:11:416:21 | call to try_emplace | TAINT |
+| map.cpp:416:40:416:40 | 2 | map.cpp:416:7:416:9 | ref arg m31 | TAINT |
+| map.cpp:416:40:416:40 | 2 | map.cpp:416:11:416:21 | call to try_emplace | TAINT |
+| map.cpp:417:7:417:9 | m31 | map.cpp:417:7:417:9 | call to unordered_map | |
+| map.cpp:418:7:418:9 | m31 | map.cpp:418:10:418:10 | call to operator[] | TAINT |
+| map.cpp:418:7:418:9 | ref arg m31 | map.cpp:438:1:438:1 | m31 | |
+| map.cpp:418:10:418:10 | call to operator[] | map.cpp:418:7:418:16 | call to pair | TAINT |
+| map.cpp:419:7:419:9 | m32 | map.cpp:419:11:419:21 | call to try_emplace | TAINT |
+| map.cpp:419:7:419:9 | ref arg m32 | map.cpp:420:7:420:9 | m32 | |
+| map.cpp:419:7:419:9 | ref arg m32 | map.cpp:421:7:421:9 | m32 | |
+| map.cpp:419:7:419:9 | ref arg m32 | map.cpp:438:1:438:1 | m32 | |
+| map.cpp:419:11:419:21 | call to try_emplace | map.cpp:419:7:419:41 | call to pair | TAINT |
+| map.cpp:419:30:419:30 | 1 | map.cpp:419:7:419:9 | ref arg m32 | TAINT |
+| map.cpp:419:30:419:30 | 1 | map.cpp:419:11:419:21 | call to try_emplace | TAINT |
+| map.cpp:419:33:419:38 | call to source | map.cpp:419:7:419:9 | ref arg m32 | TAINT |
+| map.cpp:419:33:419:38 | call to source | map.cpp:419:11:419:21 | call to try_emplace | TAINT |
+| map.cpp:420:7:420:9 | m32 | map.cpp:420:7:420:9 | call to unordered_map | |
+| map.cpp:421:7:421:9 | m32 | map.cpp:421:10:421:10 | call to operator[] | TAINT |
+| map.cpp:421:7:421:9 | ref arg m32 | map.cpp:438:1:438:1 | m32 | |
+| map.cpp:421:10:421:10 | call to operator[] | map.cpp:421:7:421:16 | call to pair | TAINT |
+| map.cpp:424:37:424:39 | call to unordered_map | map.cpp:425:7:425:9 | m33 | |
+| map.cpp:424:37:424:39 | call to unordered_map | map.cpp:426:7:426:9 | m33 | |
+| map.cpp:424:37:424:39 | call to unordered_map | map.cpp:438:1:438:1 | m33 | |
+| map.cpp:425:7:425:9 | m33 | map.cpp:425:11:425:17 | call to emplace | TAINT |
+| map.cpp:425:7:425:9 | ref arg m33 | map.cpp:426:7:426:9 | m33 | |
+| map.cpp:425:7:425:9 | ref arg m33 | map.cpp:438:1:438:1 | m33 | |
+| map.cpp:425:29:425:33 | def | map.cpp:425:7:425:9 | ref arg m33 | TAINT |
+| map.cpp:425:29:425:33 | def | map.cpp:425:11:425:17 | call to emplace | TAINT |
+| map.cpp:425:36:425:40 | first | map.cpp:425:7:425:40 | call to iterator | |
+| map.cpp:426:7:426:9 | m33 | map.cpp:426:7:426:9 | call to unordered_map | |
+| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:429:7:429:9 | m34 | |
+| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:430:7:430:9 | m34 | |
+| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:431:7:431:9 | m34 | |
+| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:432:7:432:9 | m34 | |
+| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:433:7:433:9 | m34 | |
+| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:433:24:433:26 | m34 | |
+| map.cpp:428:37:428:39 | call to unordered_map | map.cpp:438:1:438:1 | m34 | |
+| map.cpp:428:42:428:44 | call to unordered_map | map.cpp:434:7:434:9 | m35 | |
+| map.cpp:428:42:428:44 | call to unordered_map | map.cpp:435:7:435:9 | m35 | |
+| map.cpp:428:42:428:44 | call to unordered_map | map.cpp:436:7:436:9 | m35 | |
+| map.cpp:428:42:428:44 | call to unordered_map | map.cpp:437:7:437:9 | m35 | |
+| map.cpp:428:42:428:44 | call to unordered_map | map.cpp:438:1:438:1 | m35 | |
+| map.cpp:429:7:429:9 | m34 | map.cpp:429:11:429:17 | call to emplace | TAINT |
+| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:430:7:430:9 | m34 | |
+| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:431:7:431:9 | m34 | |
+| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:432:7:432:9 | m34 | |
+| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:433:7:433:9 | m34 | |
+| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:433:24:433:26 | m34 | |
+| map.cpp:429:7:429:9 | ref arg m34 | map.cpp:438:1:438:1 | m34 | |
+| map.cpp:429:19:429:57 | call to pair | map.cpp:429:7:429:9 | ref arg m34 | TAINT |
+| map.cpp:429:19:429:57 | call to pair | map.cpp:429:11:429:17 | call to emplace | TAINT |
+| map.cpp:429:52:429:56 | def | map.cpp:429:19:429:57 | call to pair | TAINT |
+| map.cpp:429:60:429:64 | first | map.cpp:429:7:429:64 | call to iterator | |
+| map.cpp:430:7:430:9 | m34 | map.cpp:430:7:430:9 | call to unordered_map | |
+| map.cpp:431:7:431:9 | m34 | map.cpp:431:11:431:17 | call to emplace | TAINT |
+| map.cpp:431:7:431:9 | ref arg m34 | map.cpp:432:7:432:9 | m34 | |
+| map.cpp:431:7:431:9 | ref arg m34 | map.cpp:433:7:433:9 | m34 | |
+| map.cpp:431:7:431:9 | ref arg m34 | map.cpp:433:24:433:26 | m34 | |
+| map.cpp:431:7:431:9 | ref arg m34 | map.cpp:438:1:438:1 | m34 | |
+| map.cpp:431:19:431:60 | call to pair | map.cpp:431:7:431:9 | ref arg m34 | TAINT |
+| map.cpp:431:19:431:60 | call to pair | map.cpp:431:11:431:17 | call to emplace | TAINT |
+| map.cpp:431:52:431:57 | call to source | map.cpp:431:19:431:60 | call to pair | TAINT |
+| map.cpp:431:63:431:67 | first | map.cpp:431:7:431:67 | call to iterator | |
+| map.cpp:432:7:432:9 | m34 | map.cpp:432:7:432:9 | call to unordered_map | |
+| map.cpp:433:7:433:9 | m34 | map.cpp:433:11:433:22 | call to emplace_hint | TAINT |
+| map.cpp:433:7:433:9 | ref arg m34 | map.cpp:438:1:438:1 | m34 | |
+| map.cpp:433:24:433:26 | m34 | map.cpp:433:28:433:32 | call to begin | TAINT |
+| map.cpp:433:24:433:26 | ref arg m34 | map.cpp:433:7:433:9 | m34 | |
+| map.cpp:433:24:433:26 | ref arg m34 | map.cpp:438:1:438:1 | m34 | |
+| map.cpp:433:28:433:32 | call to begin | map.cpp:433:24:433:34 | call to iterator | TAINT |
+| map.cpp:433:44:433:48 | def | map.cpp:433:7:433:9 | ref arg m34 | TAINT |
+| map.cpp:433:44:433:48 | def | map.cpp:433:11:433:22 | call to emplace_hint | TAINT |
+| map.cpp:434:7:434:9 | m35 | map.cpp:434:7:434:9 | ref arg m35 | TAINT |
+| map.cpp:434:7:434:9 | m35 | map.cpp:434:11:434:17 | call to emplace | TAINT |
+| map.cpp:434:7:434:9 | ref arg m35 | map.cpp:435:7:435:9 | m35 | |
+| map.cpp:434:7:434:9 | ref arg m35 | map.cpp:436:7:436:9 | m35 | |
+| map.cpp:434:7:434:9 | ref arg m35 | map.cpp:437:7:437:9 | m35 | |
+| map.cpp:434:7:434:9 | ref arg m35 | map.cpp:438:1:438:1 | m35 | |
+| map.cpp:434:21:434:25 | first | map.cpp:434:7:434:25 | call to iterator | |
+| map.cpp:435:7:435:9 | m35 | map.cpp:435:7:435:9 | call to unordered_map | |
+| map.cpp:436:7:436:9 | m35 | map.cpp:436:11:436:17 | call to emplace | TAINT |
+| map.cpp:436:7:436:9 | ref arg m35 | map.cpp:437:7:437:9 | m35 | |
+| map.cpp:436:7:436:9 | ref arg m35 | map.cpp:438:1:438:1 | m35 | |
+| map.cpp:436:19:436:60 | call to pair | map.cpp:436:7:436:9 | ref arg m35 | TAINT |
+| map.cpp:436:19:436:60 | call to pair | map.cpp:436:11:436:17 | call to emplace | TAINT |
+| map.cpp:436:55:436:59 | def | map.cpp:436:19:436:60 | call to pair | TAINT |
+| map.cpp:436:63:436:67 | first | map.cpp:436:7:436:67 | call to iterator | |
+| map.cpp:437:7:437:9 | m35 | map.cpp:437:7:437:9 | call to unordered_map | |
+| movableclass.cpp:8:2:8:15 | this | movableclass.cpp:8:27:8:31 | constructor init of field v [pre-this] | |
+| movableclass.cpp:8:21:8:22 | _v | movableclass.cpp:8:29:8:30 | _v | |
+| movableclass.cpp:8:29:8:30 | _v | movableclass.cpp:8:27:8:31 | constructor init of field v | TAINT |
+| movableclass.cpp:9:2:9:15 | this | movableclass.cpp:10:3:10:3 | this | |
+| movableclass.cpp:9:34:9:38 | other | movableclass.cpp:9:34:9:38 | other | |
+| movableclass.cpp:9:34:9:38 | other | movableclass.cpp:10:7:10:11 | other | |
+| movableclass.cpp:9:34:9:38 | other | movableclass.cpp:11:3:11:7 | other | |
+| movableclass.cpp:10:3:10:13 | ... = ... | movableclass.cpp:10:3:10:3 | v [post update] | |
+| movableclass.cpp:10:13:10:13 | v | movableclass.cpp:10:3:10:13 | ... = ... | |
+| movableclass.cpp:11:3:11:7 | other [post update] | movableclass.cpp:9:34:9:38 | other | |
+| movableclass.cpp:11:3:11:13 | ... = ... | movableclass.cpp:11:9:11:9 | v [post update] | |
+| movableclass.cpp:11:13:11:13 | 0 | movableclass.cpp:11:3:11:13 | ... = ... | |
+| movableclass.cpp:13:18:13:26 | this | movableclass.cpp:14:3:14:3 | this | |
+| movableclass.cpp:13:45:13:49 | other | movableclass.cpp:13:45:13:49 | other | |
+| movableclass.cpp:13:45:13:49 | other | movableclass.cpp:14:7:14:11 | other | |
+| movableclass.cpp:13:45:13:49 | other | movableclass.cpp:15:3:15:7 | other | |
+| movableclass.cpp:14:3:14:3 | this | movableclass.cpp:16:11:16:14 | this | |
+| movableclass.cpp:14:3:14:3 | this [post update] | movableclass.cpp:16:11:16:14 | this | |
+| movableclass.cpp:14:3:14:13 | ... = ... | movableclass.cpp:14:3:14:3 | v [post update] | |
+| movableclass.cpp:14:13:14:13 | v | movableclass.cpp:14:3:14:13 | ... = ... | |
+| movableclass.cpp:15:3:15:7 | other [post update] | movableclass.cpp:13:45:13:49 | other | |
+| movableclass.cpp:15:3:15:13 | ... = ... | movableclass.cpp:15:9:15:9 | v [post update] | |
+| movableclass.cpp:15:13:15:13 | 0 | movableclass.cpp:15:3:15:13 | ... = ... | |
+| movableclass.cpp:16:11:16:14 | this | movableclass.cpp:16:10:16:14 | * ... | TAINT |
+| movableclass.cpp:22:57:22:57 | 1 | movableclass.cpp:22:42:22:58 | call to MyMovableClass | TAINT |
+| movableclass.cpp:23:55:23:60 | call to source | movableclass.cpp:23:40:23:63 | call to MyMovableClass | TAINT |
+| movableclass.cpp:28:21:28:21 | 1 | movableclass.cpp:28:21:28:22 | call to MyMovableClass | TAINT |
+| movableclass.cpp:28:21:28:22 | call to MyMovableClass | movableclass.cpp:33:8:33:9 | s1 | |
+| movableclass.cpp:29:22:29:23 | call to MyMovableClass | movableclass.cpp:34:8:34:9 | s2 | |
+| movableclass.cpp:29:23:29:23 | 1 | movableclass.cpp:29:22:29:23 | call to MyMovableClass | TAINT |
+| movableclass.cpp:30:18:30:19 | call to MyMovableClass | movableclass.cpp:31:3:31:4 | s3 | |
+| movableclass.cpp:30:18:30:19 | call to MyMovableClass | movableclass.cpp:35:8:35:9 | s3 | |
+| movableclass.cpp:31:3:31:4 | ref arg s3 | movableclass.cpp:35:8:35:9 | s3 | |
+| movableclass.cpp:31:8:31:8 | 1 | movableclass.cpp:31:8:31:8 | call to MyMovableClass | TAINT |
+| movableclass.cpp:31:8:31:8 | call to MyMovableClass | movableclass.cpp:31:3:31:4 | ref arg s3 | TAINT |
+| movableclass.cpp:31:8:31:8 | call to MyMovableClass | movableclass.cpp:31:6:31:6 | call to operator= | TAINT |
+| movableclass.cpp:39:21:39:26 | call to source | movableclass.cpp:39:21:39:29 | call to MyMovableClass | TAINT |
+| movableclass.cpp:39:21:39:29 | call to MyMovableClass | movableclass.cpp:44:8:44:9 | s1 | |
+| movableclass.cpp:40:22:40:30 | call to MyMovableClass | movableclass.cpp:45:8:45:9 | s2 | |
+| movableclass.cpp:40:23:40:28 | call to source | movableclass.cpp:40:22:40:30 | call to MyMovableClass | TAINT |
+| movableclass.cpp:41:18:41:19 | call to MyMovableClass | movableclass.cpp:42:3:42:4 | s3 | |
+| movableclass.cpp:41:18:41:19 | call to MyMovableClass | movableclass.cpp:46:8:46:9 | s3 | |
+| movableclass.cpp:42:3:42:4 | ref arg s3 | movableclass.cpp:46:8:46:9 | s3 | |
+| movableclass.cpp:42:8:42:13 | call to source | movableclass.cpp:42:8:42:15 | call to MyMovableClass | TAINT |
+| movableclass.cpp:42:8:42:15 | call to MyMovableClass | movableclass.cpp:42:3:42:4 | ref arg s3 | TAINT |
+| movableclass.cpp:42:8:42:15 | call to MyMovableClass | movableclass.cpp:42:6:42:6 | call to operator= | TAINT |
+| movableclass.cpp:50:22:50:46 | call to MyMovableClass | movableclass.cpp:54:8:54:9 | s1 | |
+| movableclass.cpp:50:38:50:43 | call to source | movableclass.cpp:50:22:50:46 | call to MyMovableClass | TAINT |
+| movableclass.cpp:51:18:51:19 | call to MyMovableClass | movableclass.cpp:52:3:52:4 | s2 | |
+| movableclass.cpp:51:18:51:19 | call to MyMovableClass | movableclass.cpp:55:8:55:9 | s2 | |
+| movableclass.cpp:52:3:52:4 | ref arg s2 | movableclass.cpp:55:8:55:9 | s2 | |
+| movableclass.cpp:52:8:52:31 | call to MyMovableClass | movableclass.cpp:52:3:52:4 | ref arg s2 | TAINT |
+| movableclass.cpp:52:8:52:31 | call to MyMovableClass | movableclass.cpp:52:6:52:6 | call to operator= | TAINT |
+| movableclass.cpp:52:23:52:28 | call to source | movableclass.cpp:52:8:52:31 | call to MyMovableClass | TAINT |
+| movableclass.cpp:59:21:59:32 | call to getUnTainted | movableclass.cpp:59:21:59:35 | call to MyMovableClass | |
+| movableclass.cpp:59:21:59:35 | call to MyMovableClass | movableclass.cpp:63:8:63:9 | s1 | |
+| movableclass.cpp:60:21:60:30 | call to getTainted | movableclass.cpp:60:21:60:33 | call to MyMovableClass | |
+| movableclass.cpp:60:21:60:33 | call to MyMovableClass | movableclass.cpp:64:8:64:9 | s2 | |
+| movableclass.cpp:61:18:61:19 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | s3 | |
+| movableclass.cpp:65:13:65:18 | call to source | movableclass.cpp:65:13:65:20 | call to MyMovableClass | TAINT |
+| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:8:65:9 | ref arg s3 | TAINT |
+| movableclass.cpp:65:13:65:20 | call to MyMovableClass | movableclass.cpp:65:11:65:11 | call to operator= | TAINT |
+| set.cpp:17:19:17:20 | call to set | set.cpp:19:7:19:8 | s1 | |
+| set.cpp:17:19:17:20 | call to set | set.cpp:23:12:23:13 | s1 | |
+| set.cpp:17:19:17:20 | call to set | set.cpp:23:24:23:25 | s1 | |
+| set.cpp:17:19:17:20 | call to set | set.cpp:25:7:25:8 | s1 | |
+| set.cpp:17:19:17:20 | call to set | set.cpp:31:7:31:8 | s1 | |
+| set.cpp:17:19:17:20 | call to set | set.cpp:55:12:55:13 | s1 | |
+| set.cpp:17:19:17:20 | call to set | set.cpp:55:30:55:31 | s1 | |
+| set.cpp:17:19:17:20 | call to set | set.cpp:126:1:126:1 | s1 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:20:7:20:8 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:24:12:24:13 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:24:24:24:25 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:26:7:26:8 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:32:7:32:8 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:39:22:39:23 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:40:24:40:25 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:41:22:41:23 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:41:34:41:35 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:43:8:43:9 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:59:12:59:13 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:59:30:59:31 | s2 | |
+| set.cpp:17:23:17:24 | call to set | set.cpp:126:1:126:1 | s2 | |
+| set.cpp:17:27:17:28 | call to set | set.cpp:21:7:21:8 | s3 | |
+| set.cpp:17:27:17:28 | call to set | set.cpp:21:17:21:18 | s3 | |
+| set.cpp:17:27:17:28 | call to set | set.cpp:27:7:27:8 | s3 | |
+| set.cpp:17:27:17:28 | call to set | set.cpp:33:7:33:8 | s3 | |
+| set.cpp:17:27:17:28 | call to set | set.cpp:126:1:126:1 | s3 | |
+| set.cpp:17:31:17:32 | call to set | set.cpp:22:7:22:8 | s4 | |
+| set.cpp:17:31:17:32 | call to set | set.cpp:22:17:22:18 | s4 | |
+| set.cpp:17:31:17:32 | call to set | set.cpp:28:7:28:8 | s4 | |
+| set.cpp:17:31:17:32 | call to set | set.cpp:34:7:34:8 | s4 | |
+| set.cpp:17:31:17:32 | call to set | set.cpp:126:1:126:1 | s4 | |
+| set.cpp:17:35:17:36 | call to set | set.cpp:23:2:23:3 | s5 | |
+| set.cpp:17:35:17:36 | call to set | set.cpp:29:7:29:8 | s5 | |
+| set.cpp:17:35:17:36 | call to set | set.cpp:35:7:35:8 | s5 | |
+| set.cpp:17:35:17:36 | call to set | set.cpp:126:1:126:1 | s5 | |
+| set.cpp:17:39:17:40 | call to set | set.cpp:24:2:24:3 | s6 | |
+| set.cpp:17:39:17:40 | call to set | set.cpp:30:7:30:8 | s6 | |
+| set.cpp:17:39:17:40 | call to set | set.cpp:36:7:36:8 | s6 | |
+| set.cpp:17:39:17:40 | call to set | set.cpp:126:1:126:1 | s6 | |
+| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:23:12:23:13 | s1 | |
+| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:23:24:23:25 | s1 | |
+| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:25:7:25:8 | s1 | |
+| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:31:7:31:8 | s1 | |
+| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:55:12:55:13 | s1 | |
+| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:55:30:55:31 | s1 | |
+| set.cpp:19:7:19:8 | ref arg s1 | set.cpp:126:1:126:1 | s1 | |
+| set.cpp:19:17:19:21 | abc | set.cpp:19:7:19:8 | ref arg s1 | TAINT |
+| set.cpp:19:17:19:21 | abc | set.cpp:19:10:19:15 | call to insert | TAINT |
+| set.cpp:19:24:19:28 | first | set.cpp:19:7:19:28 | call to iterator | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:24:12:24:13 | s2 | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:24:24:24:25 | s2 | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:26:7:26:8 | s2 | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:32:7:32:8 | s2 | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:39:22:39:23 | s2 | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:40:24:40:25 | s2 | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:41:22:41:23 | s2 | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:41:34:41:35 | s2 | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:43:8:43:9 | s2 | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:59:12:59:13 | s2 | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:59:30:59:31 | s2 | |
+| set.cpp:20:7:20:8 | ref arg s2 | set.cpp:126:1:126:1 | s2 | |
+| set.cpp:20:17:20:22 | call to source | set.cpp:20:7:20:8 | ref arg s2 | TAINT |
+| set.cpp:20:17:20:22 | call to source | set.cpp:20:10:20:15 | call to insert | TAINT |
+| set.cpp:20:27:20:31 | first | set.cpp:20:7:20:31 | call to iterator | |
+| set.cpp:21:7:21:8 | ref arg s3 | set.cpp:27:7:27:8 | s3 | |
+| set.cpp:21:7:21:8 | ref arg s3 | set.cpp:33:7:33:8 | s3 | |
+| set.cpp:21:7:21:8 | ref arg s3 | set.cpp:126:1:126:1 | s3 | |
+| set.cpp:21:17:21:18 | ref arg s3 | set.cpp:21:7:21:8 | s3 | |
+| set.cpp:21:17:21:18 | ref arg s3 | set.cpp:27:7:27:8 | s3 | |
+| set.cpp:21:17:21:18 | ref arg s3 | set.cpp:33:7:33:8 | s3 | |
+| set.cpp:21:17:21:18 | ref arg s3 | set.cpp:126:1:126:1 | s3 | |
+| set.cpp:21:17:21:18 | s3 | set.cpp:21:20:21:24 | call to begin | TAINT |
+| set.cpp:21:20:21:24 | call to begin | set.cpp:21:17:21:26 | call to iterator | TAINT |
+| set.cpp:21:29:21:33 | abc | set.cpp:21:7:21:8 | ref arg s3 | TAINT |
+| set.cpp:21:29:21:33 | abc | set.cpp:21:10:21:15 | call to insert | TAINT |
+| set.cpp:22:7:22:8 | ref arg s4 | set.cpp:28:7:28:8 | s4 | |
+| set.cpp:22:7:22:8 | ref arg s4 | set.cpp:34:7:34:8 | s4 | |
+| set.cpp:22:7:22:8 | ref arg s4 | set.cpp:126:1:126:1 | s4 | |
+| set.cpp:22:17:22:18 | ref arg s4 | set.cpp:22:7:22:8 | s4 | |
+| set.cpp:22:17:22:18 | ref arg s4 | set.cpp:28:7:28:8 | s4 | |
+| set.cpp:22:17:22:18 | ref arg s4 | set.cpp:34:7:34:8 | s4 | |
+| set.cpp:22:17:22:18 | ref arg s4 | set.cpp:126:1:126:1 | s4 | |
+| set.cpp:22:17:22:18 | s4 | set.cpp:22:20:22:24 | call to begin | TAINT |
+| set.cpp:22:20:22:24 | call to begin | set.cpp:22:17:22:26 | call to iterator | TAINT |
+| set.cpp:22:29:22:34 | call to source | set.cpp:22:7:22:8 | ref arg s4 | TAINT |
+| set.cpp:22:29:22:34 | call to source | set.cpp:22:10:22:15 | call to insert | TAINT |
+| set.cpp:23:2:23:3 | ref arg s5 | set.cpp:29:7:29:8 | s5 | |
+| set.cpp:23:2:23:3 | ref arg s5 | set.cpp:35:7:35:8 | s5 | |
+| set.cpp:23:2:23:3 | ref arg s5 | set.cpp:126:1:126:1 | s5 | |
+| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:23:24:23:25 | s1 | |
+| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:25:7:25:8 | s1 | |
+| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:31:7:31:8 | s1 | |
+| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:55:12:55:13 | s1 | |
+| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:55:30:55:31 | s1 | |
+| set.cpp:23:12:23:13 | ref arg s1 | set.cpp:126:1:126:1 | s1 | |
+| set.cpp:23:12:23:13 | s1 | set.cpp:23:15:23:19 | call to begin | TAINT |
+| set.cpp:23:24:23:25 | ref arg s1 | set.cpp:25:7:25:8 | s1 | |
+| set.cpp:23:24:23:25 | ref arg s1 | set.cpp:31:7:31:8 | s1 | |
+| set.cpp:23:24:23:25 | ref arg s1 | set.cpp:55:12:55:13 | s1 | |
+| set.cpp:23:24:23:25 | ref arg s1 | set.cpp:55:30:55:31 | s1 | |
+| set.cpp:23:24:23:25 | ref arg s1 | set.cpp:126:1:126:1 | s1 | |
+| set.cpp:23:24:23:25 | s1 | set.cpp:23:27:23:29 | call to end | TAINT |
+| set.cpp:23:27:23:29 | call to end | set.cpp:23:2:23:3 | ref arg s5 | TAINT |
+| set.cpp:23:27:23:29 | call to end | set.cpp:23:5:23:10 | call to insert | TAINT |
+| set.cpp:24:2:24:3 | ref arg s6 | set.cpp:30:7:30:8 | s6 | |
+| set.cpp:24:2:24:3 | ref arg s6 | set.cpp:36:7:36:8 | s6 | |
+| set.cpp:24:2:24:3 | ref arg s6 | set.cpp:126:1:126:1 | s6 | |
+| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:24:24:24:25 | s2 | |
+| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:26:7:26:8 | s2 | |
+| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:32:7:32:8 | s2 | |
+| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:39:22:39:23 | s2 | |
+| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:40:24:40:25 | s2 | |
+| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:41:22:41:23 | s2 | |
+| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:41:34:41:35 | s2 | |
+| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:43:8:43:9 | s2 | |
+| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:59:12:59:13 | s2 | |
+| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:59:30:59:31 | s2 | |
+| set.cpp:24:12:24:13 | ref arg s2 | set.cpp:126:1:126:1 | s2 | |
+| set.cpp:24:12:24:13 | s2 | set.cpp:24:15:24:19 | call to begin | TAINT |
+| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:26:7:26:8 | s2 | |
+| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:32:7:32:8 | s2 | |
+| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:39:22:39:23 | s2 | |
+| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:40:24:40:25 | s2 | |
+| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:41:22:41:23 | s2 | |
+| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:41:34:41:35 | s2 | |
+| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:43:8:43:9 | s2 | |
+| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:59:12:59:13 | s2 | |
+| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:59:30:59:31 | s2 | |
+| set.cpp:24:24:24:25 | ref arg s2 | set.cpp:126:1:126:1 | s2 | |
+| set.cpp:24:24:24:25 | s2 | set.cpp:24:27:24:29 | call to end | TAINT |
+| set.cpp:24:27:24:29 | call to end | set.cpp:24:2:24:3 | ref arg s6 | TAINT |
+| set.cpp:24:27:24:29 | call to end | set.cpp:24:5:24:10 | call to insert | TAINT |
+| set.cpp:25:7:25:8 | s1 | set.cpp:25:7:25:8 | call to set | |
+| set.cpp:26:7:26:8 | s2 | set.cpp:26:7:26:8 | call to set | |
+| set.cpp:27:7:27:8 | s3 | set.cpp:27:7:27:8 | call to set | |
+| set.cpp:28:7:28:8 | s4 | set.cpp:28:7:28:8 | call to set | |
+| set.cpp:29:7:29:8 | s5 | set.cpp:29:7:29:8 | call to set | |
+| set.cpp:30:7:30:8 | s6 | set.cpp:30:7:30:8 | call to set | |
+| set.cpp:31:7:31:8 | ref arg s1 | set.cpp:55:12:55:13 | s1 | |
+| set.cpp:31:7:31:8 | ref arg s1 | set.cpp:55:30:55:31 | s1 | |
+| set.cpp:31:7:31:8 | ref arg s1 | set.cpp:126:1:126:1 | s1 | |
+| set.cpp:31:7:31:8 | s1 | set.cpp:31:10:31:13 | call to find | TAINT |
+| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:39:22:39:23 | s2 | |
+| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:40:24:40:25 | s2 | |
+| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:41:22:41:23 | s2 | |
+| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:41:34:41:35 | s2 | |
+| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:43:8:43:9 | s2 | |
+| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:59:12:59:13 | s2 | |
+| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:59:30:59:31 | s2 | |
+| set.cpp:32:7:32:8 | ref arg s2 | set.cpp:126:1:126:1 | s2 | |
+| set.cpp:32:7:32:8 | s2 | set.cpp:32:10:32:13 | call to find | TAINT |
+| set.cpp:33:7:33:8 | ref arg s3 | set.cpp:126:1:126:1 | s3 | |
+| set.cpp:33:7:33:8 | s3 | set.cpp:33:10:33:13 | call to find | TAINT |
+| set.cpp:34:7:34:8 | ref arg s4 | set.cpp:126:1:126:1 | s4 | |
+| set.cpp:34:7:34:8 | s4 | set.cpp:34:10:34:13 | call to find | TAINT |
+| set.cpp:35:7:35:8 | ref arg s5 | set.cpp:126:1:126:1 | s5 | |
+| set.cpp:35:7:35:8 | s5 | set.cpp:35:10:35:13 | call to find | TAINT |
+| set.cpp:36:7:36:8 | ref arg s6 | set.cpp:126:1:126:1 | s6 | |
+| set.cpp:36:7:36:8 | s6 | set.cpp:36:10:36:13 | call to find | TAINT |
+| set.cpp:39:22:39:23 | s2 | set.cpp:39:22:39:24 | call to set | |
+| set.cpp:39:22:39:24 | call to set | set.cpp:44:7:44:8 | s7 | |
+| set.cpp:39:22:39:24 | call to set | set.cpp:48:7:48:8 | s7 | |
+| set.cpp:39:22:39:24 | call to set | set.cpp:126:1:126:1 | s7 | |
+| set.cpp:40:23:40:25 | call to set | set.cpp:45:7:45:8 | s8 | |
+| set.cpp:40:23:40:25 | call to set | set.cpp:49:7:49:8 | s8 | |
+| set.cpp:40:23:40:25 | call to set | set.cpp:126:1:126:1 | s8 | |
+| set.cpp:40:24:40:25 | s2 | set.cpp:40:23:40:25 | call to set | |
+| set.cpp:41:22:41:23 | ref arg s2 | set.cpp:41:34:41:35 | s2 | |
+| set.cpp:41:22:41:23 | ref arg s2 | set.cpp:43:8:43:9 | s2 | |
+| set.cpp:41:22:41:23 | ref arg s2 | set.cpp:59:12:59:13 | s2 | |
+| set.cpp:41:22:41:23 | ref arg s2 | set.cpp:59:30:59:31 | s2 | |
+| set.cpp:41:22:41:23 | ref arg s2 | set.cpp:126:1:126:1 | s2 | |
+| set.cpp:41:22:41:23 | s2 | set.cpp:41:25:41:29 | call to begin | TAINT |
+| set.cpp:41:22:41:42 | call to set | set.cpp:46:7:46:8 | s9 | |
+| set.cpp:41:22:41:42 | call to set | set.cpp:50:7:50:8 | s9 | |
+| set.cpp:41:22:41:42 | call to set | set.cpp:126:1:126:1 | s9 | |
+| set.cpp:41:25:41:29 | call to begin | set.cpp:41:22:41:42 | call to set | TAINT |
+| set.cpp:41:34:41:35 | ref arg s2 | set.cpp:43:8:43:9 | s2 | |
+| set.cpp:41:34:41:35 | ref arg s2 | set.cpp:59:12:59:13 | s2 | |
+| set.cpp:41:34:41:35 | ref arg s2 | set.cpp:59:30:59:31 | s2 | |
+| set.cpp:41:34:41:35 | ref arg s2 | set.cpp:126:1:126:1 | s2 | |
+| set.cpp:41:34:41:35 | s2 | set.cpp:41:37:41:39 | call to end | TAINT |
+| set.cpp:41:37:41:39 | call to end | set.cpp:41:22:41:42 | call to set | TAINT |
+| set.cpp:42:19:42:21 | call to set | set.cpp:43:2:43:4 | s10 | |
+| set.cpp:42:19:42:21 | call to set | set.cpp:47:7:47:9 | s10 | |
+| set.cpp:42:19:42:21 | call to set | set.cpp:51:7:51:9 | s10 | |
+| set.cpp:42:19:42:21 | call to set | set.cpp:126:1:126:1 | s10 | |
+| set.cpp:43:2:43:4 | ref arg s10 | set.cpp:47:7:47:9 | s10 | |
+| set.cpp:43:2:43:4 | ref arg s10 | set.cpp:51:7:51:9 | s10 | |
+| set.cpp:43:2:43:4 | ref arg s10 | set.cpp:126:1:126:1 | s10 | |
+| set.cpp:43:8:43:9 | s2 | set.cpp:43:2:43:4 | ref arg s10 | TAINT |
+| set.cpp:43:8:43:9 | s2 | set.cpp:43:6:43:6 | call to operator= | TAINT |
+| set.cpp:44:7:44:8 | s7 | set.cpp:44:7:44:8 | call to set | |
+| set.cpp:45:7:45:8 | s8 | set.cpp:45:7:45:8 | call to set | |
+| set.cpp:46:7:46:8 | s9 | set.cpp:46:7:46:8 | call to set | |
+| set.cpp:47:7:47:9 | s10 | set.cpp:47:7:47:9 | call to set | |
+| set.cpp:48:7:48:8 | ref arg s7 | set.cpp:126:1:126:1 | s7 | |
+| set.cpp:48:7:48:8 | s7 | set.cpp:48:10:48:13 | call to find | TAINT |
+| set.cpp:49:7:49:8 | ref arg s8 | set.cpp:126:1:126:1 | s8 | |
+| set.cpp:49:7:49:8 | s8 | set.cpp:49:10:49:13 | call to find | TAINT |
+| set.cpp:50:7:50:8 | ref arg s9 | set.cpp:126:1:126:1 | s9 | |
+| set.cpp:50:7:50:8 | s9 | set.cpp:50:10:50:13 | call to find | TAINT |
+| set.cpp:51:7:51:9 | ref arg s10 | set.cpp:126:1:126:1 | s10 | |
+| set.cpp:51:7:51:9 | s10 | set.cpp:51:11:51:14 | call to find | TAINT |
+| set.cpp:55:12:55:13 | ref arg s1 | set.cpp:55:30:55:31 | s1 | |
+| set.cpp:55:12:55:13 | ref arg s1 | set.cpp:126:1:126:1 | s1 | |
+| set.cpp:55:12:55:13 | s1 | set.cpp:55:15:55:19 | call to begin | TAINT |
+| set.cpp:55:15:55:19 | call to begin | set.cpp:55:7:55:21 | ... = ... | |
+| set.cpp:55:15:55:19 | call to begin | set.cpp:55:24:55:25 | i1 | |
+| set.cpp:55:15:55:19 | call to begin | set.cpp:55:40:55:41 | i1 | |
+| set.cpp:55:15:55:19 | call to begin | set.cpp:57:9:57:10 | i1 | |
+| set.cpp:55:30:55:31 | ref arg s1 | set.cpp:55:30:55:31 | s1 | |
+| set.cpp:55:30:55:31 | ref arg s1 | set.cpp:126:1:126:1 | s1 | |
+| set.cpp:55:30:55:31 | s1 | set.cpp:55:33:55:35 | call to end | TAINT |
+| set.cpp:55:40:55:41 | i1 | set.cpp:55:42:55:42 | call to operator++ | |
+| set.cpp:55:40:55:41 | ref arg i1 | set.cpp:55:24:55:25 | i1 | |
+| set.cpp:55:40:55:41 | ref arg i1 | set.cpp:55:40:55:41 | i1 | |
+| set.cpp:55:40:55:41 | ref arg i1 | set.cpp:57:9:57:10 | i1 | |
+| set.cpp:57:9:57:10 | i1 | set.cpp:57:8:57:8 | call to operator* | TAINT |
+| set.cpp:59:12:59:13 | ref arg s2 | set.cpp:59:30:59:31 | s2 | |
+| set.cpp:59:12:59:13 | ref arg s2 | set.cpp:126:1:126:1 | s2 | |
+| set.cpp:59:12:59:13 | s2 | set.cpp:59:15:59:19 | call to begin | TAINT |
+| set.cpp:59:15:59:19 | call to begin | set.cpp:59:7:59:21 | ... = ... | |
+| set.cpp:59:15:59:19 | call to begin | set.cpp:59:24:59:25 | i2 | |
+| set.cpp:59:15:59:19 | call to begin | set.cpp:59:40:59:41 | i2 | |
+| set.cpp:59:15:59:19 | call to begin | set.cpp:61:9:61:10 | i2 | |
+| set.cpp:59:30:59:31 | ref arg s2 | set.cpp:59:30:59:31 | s2 | |
+| set.cpp:59:30:59:31 | ref arg s2 | set.cpp:126:1:126:1 | s2 | |
+| set.cpp:59:30:59:31 | s2 | set.cpp:59:33:59:35 | call to end | TAINT |
+| set.cpp:59:40:59:41 | i2 | set.cpp:59:42:59:42 | call to operator++ | |
+| set.cpp:59:40:59:41 | ref arg i2 | set.cpp:59:24:59:25 | i2 | |
+| set.cpp:59:40:59:41 | ref arg i2 | set.cpp:59:40:59:41 | i2 | |
+| set.cpp:59:40:59:41 | ref arg i2 | set.cpp:61:9:61:10 | i2 | |
+| set.cpp:61:9:61:10 | i2 | set.cpp:61:8:61:8 | call to operator* | TAINT |
+| set.cpp:65:19:65:21 | call to set | set.cpp:66:2:66:4 | s11 | |
+| set.cpp:65:19:65:21 | call to set | set.cpp:67:2:67:4 | s11 | |
+| set.cpp:65:19:65:21 | call to set | set.cpp:68:2:68:4 | s11 | |
+| set.cpp:65:19:65:21 | call to set | set.cpp:69:7:69:9 | s11 | |
+| set.cpp:65:19:65:21 | call to set | set.cpp:70:7:70:9 | s11 | |
+| set.cpp:65:19:65:21 | call to set | set.cpp:71:7:71:9 | s11 | |
+| set.cpp:65:19:65:21 | call to set | set.cpp:72:7:72:9 | s11 | |
+| set.cpp:65:19:65:21 | call to set | set.cpp:126:1:126:1 | s11 | |
+| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:67:2:67:4 | s11 | |
+| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:68:2:68:4 | s11 | |
+| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:69:7:69:9 | s11 | |
+| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:70:7:70:9 | s11 | |
+| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:71:7:71:9 | s11 | |
+| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:72:7:72:9 | s11 | |
+| set.cpp:66:2:66:4 | ref arg s11 | set.cpp:126:1:126:1 | s11 | |
+| set.cpp:66:13:66:15 | a | set.cpp:66:2:66:4 | ref arg s11 | TAINT |
+| set.cpp:66:13:66:15 | a | set.cpp:66:6:66:11 | call to insert | TAINT |
+| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:68:2:68:4 | s11 | |
+| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:69:7:69:9 | s11 | |
+| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:70:7:70:9 | s11 | |
+| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:71:7:71:9 | s11 | |
+| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:72:7:72:9 | s11 | |
+| set.cpp:67:2:67:4 | ref arg s11 | set.cpp:126:1:126:1 | s11 | |
+| set.cpp:67:13:67:18 | call to source | set.cpp:67:2:67:4 | ref arg s11 | TAINT |
+| set.cpp:67:13:67:18 | call to source | set.cpp:67:6:67:11 | call to insert | TAINT |
+| set.cpp:68:2:68:4 | ref arg s11 | set.cpp:69:7:69:9 | s11 | |
+| set.cpp:68:2:68:4 | ref arg s11 | set.cpp:70:7:70:9 | s11 | |
+| set.cpp:68:2:68:4 | ref arg s11 | set.cpp:71:7:71:9 | s11 | |
+| set.cpp:68:2:68:4 | ref arg s11 | set.cpp:72:7:72:9 | s11 | |
+| set.cpp:68:2:68:4 | ref arg s11 | set.cpp:126:1:126:1 | s11 | |
+| set.cpp:68:13:68:15 | c | set.cpp:68:2:68:4 | ref arg s11 | TAINT |
+| set.cpp:68:13:68:15 | c | set.cpp:68:6:68:11 | call to insert | TAINT |
+| set.cpp:69:7:69:9 | ref arg s11 | set.cpp:70:7:70:9 | s11 | |
+| set.cpp:69:7:69:9 | ref arg s11 | set.cpp:71:7:71:9 | s11 | |
+| set.cpp:69:7:69:9 | ref arg s11 | set.cpp:72:7:72:9 | s11 | |
+| set.cpp:69:7:69:9 | ref arg s11 | set.cpp:126:1:126:1 | s11 | |
+| set.cpp:69:7:69:9 | s11 | set.cpp:69:11:69:21 | call to lower_bound | TAINT |
+| set.cpp:70:7:70:9 | ref arg s11 | set.cpp:71:7:71:9 | s11 | |
+| set.cpp:70:7:70:9 | ref arg s11 | set.cpp:72:7:72:9 | s11 | |
+| set.cpp:70:7:70:9 | ref arg s11 | set.cpp:126:1:126:1 | s11 | |
+| set.cpp:70:7:70:9 | s11 | set.cpp:70:11:70:21 | call to upper_bound | TAINT |
+| set.cpp:71:7:71:9 | ref arg s11 | set.cpp:72:7:72:9 | s11 | |
+| set.cpp:71:7:71:9 | ref arg s11 | set.cpp:126:1:126:1 | s11 | |
+| set.cpp:71:7:71:9 | s11 | set.cpp:71:11:71:21 | call to equal_range | TAINT |
+| set.cpp:71:28:71:32 | first | set.cpp:71:7:71:32 | call to iterator | |
+| set.cpp:72:7:72:9 | ref arg s11 | set.cpp:126:1:126:1 | s11 | |
+| set.cpp:72:7:72:9 | s11 | set.cpp:72:11:72:21 | call to equal_range | TAINT |
+| set.cpp:72:28:72:33 | second | set.cpp:72:7:72:33 | call to iterator | |
+| set.cpp:75:19:75:21 | call to set | set.cpp:76:2:76:4 | s12 | |
+| set.cpp:75:19:75:21 | call to set | set.cpp:78:7:78:9 | s12 | |
+| set.cpp:75:19:75:21 | call to set | set.cpp:82:2:82:4 | s12 | |
+| set.cpp:75:19:75:21 | call to set | set.cpp:84:7:84:9 | s12 | |
+| set.cpp:75:19:75:21 | call to set | set.cpp:126:1:126:1 | s12 | |
+| set.cpp:75:24:75:26 | call to set | set.cpp:79:7:79:9 | s13 | |
+| set.cpp:75:24:75:26 | call to set | set.cpp:82:11:82:13 | s13 | |
+| set.cpp:75:24:75:26 | call to set | set.cpp:85:7:85:9 | s13 | |
+| set.cpp:75:24:75:26 | call to set | set.cpp:126:1:126:1 | s13 | |
+| set.cpp:75:29:75:31 | call to set | set.cpp:80:7:80:9 | s14 | |
+| set.cpp:75:29:75:31 | call to set | set.cpp:83:2:83:4 | s14 | |
+| set.cpp:75:29:75:31 | call to set | set.cpp:86:7:86:9 | s14 | |
+| set.cpp:75:29:75:31 | call to set | set.cpp:126:1:126:1 | s14 | |
+| set.cpp:75:34:75:36 | call to set | set.cpp:77:2:77:4 | s15 | |
+| set.cpp:75:34:75:36 | call to set | set.cpp:81:7:81:9 | s15 | |
+| set.cpp:75:34:75:36 | call to set | set.cpp:83:11:83:13 | s15 | |
+| set.cpp:75:34:75:36 | call to set | set.cpp:87:7:87:9 | s15 | |
+| set.cpp:75:34:75:36 | call to set | set.cpp:126:1:126:1 | s15 | |
+| set.cpp:76:2:76:4 | ref arg s12 | set.cpp:78:7:78:9 | s12 | |
+| set.cpp:76:2:76:4 | ref arg s12 | set.cpp:82:2:82:4 | s12 | |
+| set.cpp:76:2:76:4 | ref arg s12 | set.cpp:84:7:84:9 | s12 | |
+| set.cpp:76:2:76:4 | ref arg s12 | set.cpp:126:1:126:1 | s12 | |
+| set.cpp:76:13:76:18 | call to source | set.cpp:76:2:76:4 | ref arg s12 | TAINT |
+| set.cpp:76:13:76:18 | call to source | set.cpp:76:6:76:11 | call to insert | TAINT |
+| set.cpp:77:2:77:4 | ref arg s15 | set.cpp:81:7:81:9 | s15 | |
+| set.cpp:77:2:77:4 | ref arg s15 | set.cpp:83:11:83:13 | s15 | |
+| set.cpp:77:2:77:4 | ref arg s15 | set.cpp:87:7:87:9 | s15 | |
+| set.cpp:77:2:77:4 | ref arg s15 | set.cpp:126:1:126:1 | s15 | |
+| set.cpp:77:13:77:18 | call to source | set.cpp:77:2:77:4 | ref arg s15 | TAINT |
+| set.cpp:77:13:77:18 | call to source | set.cpp:77:6:77:11 | call to insert | TAINT |
+| set.cpp:78:7:78:9 | s12 | set.cpp:78:7:78:9 | call to set | |
+| set.cpp:79:7:79:9 | s13 | set.cpp:79:7:79:9 | call to set | |
+| set.cpp:80:7:80:9 | s14 | set.cpp:80:7:80:9 | call to set | |
+| set.cpp:81:7:81:9 | s15 | set.cpp:81:7:81:9 | call to set | |
+| set.cpp:82:2:82:4 | ref arg s12 | set.cpp:84:7:84:9 | s12 | |
+| set.cpp:82:2:82:4 | ref arg s12 | set.cpp:126:1:126:1 | s12 | |
+| set.cpp:82:2:82:4 | s12 | set.cpp:82:11:82:13 | ref arg s13 | TAINT |
+| set.cpp:82:11:82:13 | ref arg s13 | set.cpp:85:7:85:9 | s13 | |
+| set.cpp:82:11:82:13 | ref arg s13 | set.cpp:126:1:126:1 | s13 | |
+| set.cpp:82:11:82:13 | s13 | set.cpp:82:2:82:4 | ref arg s12 | TAINT |
+| set.cpp:83:2:83:4 | ref arg s14 | set.cpp:86:7:86:9 | s14 | |
+| set.cpp:83:2:83:4 | ref arg s14 | set.cpp:126:1:126:1 | s14 | |
+| set.cpp:83:2:83:4 | s14 | set.cpp:83:11:83:13 | ref arg s15 | TAINT |
+| set.cpp:83:11:83:13 | ref arg s15 | set.cpp:87:7:87:9 | s15 | |
+| set.cpp:83:11:83:13 | ref arg s15 | set.cpp:126:1:126:1 | s15 | |
+| set.cpp:83:11:83:13 | s15 | set.cpp:83:2:83:4 | ref arg s14 | TAINT |
+| set.cpp:84:7:84:9 | s12 | set.cpp:84:7:84:9 | call to set | |
+| set.cpp:85:7:85:9 | s13 | set.cpp:85:7:85:9 | call to set | |
+| set.cpp:86:7:86:9 | s14 | set.cpp:86:7:86:9 | call to set | |
+| set.cpp:87:7:87:9 | s15 | set.cpp:87:7:87:9 | call to set | |
+| set.cpp:90:19:90:21 | call to set | set.cpp:91:2:91:4 | s16 | |
+| set.cpp:90:19:90:21 | call to set | set.cpp:95:7:95:9 | s16 | |
+| set.cpp:90:19:90:21 | call to set | set.cpp:99:2:99:4 | s16 | |
+| set.cpp:90:19:90:21 | call to set | set.cpp:101:7:101:9 | s16 | |
+| set.cpp:90:19:90:21 | call to set | set.cpp:126:1:126:1 | s16 | |
+| set.cpp:90:24:90:26 | call to set | set.cpp:92:2:92:4 | s17 | |
+| set.cpp:90:24:90:26 | call to set | set.cpp:96:7:96:9 | s17 | |
+| set.cpp:90:24:90:26 | call to set | set.cpp:99:12:99:14 | s17 | |
+| set.cpp:90:24:90:26 | call to set | set.cpp:102:7:102:9 | s17 | |
+| set.cpp:90:24:90:26 | call to set | set.cpp:126:1:126:1 | s17 | |
+| set.cpp:90:29:90:31 | call to set | set.cpp:93:2:93:4 | s18 | |
+| set.cpp:90:29:90:31 | call to set | set.cpp:97:7:97:9 | s18 | |
+| set.cpp:90:29:90:31 | call to set | set.cpp:100:2:100:4 | s18 | |
+| set.cpp:90:29:90:31 | call to set | set.cpp:103:7:103:9 | s18 | |
+| set.cpp:90:29:90:31 | call to set | set.cpp:126:1:126:1 | s18 | |
+| set.cpp:90:34:90:36 | call to set | set.cpp:94:2:94:4 | s19 | |
+| set.cpp:90:34:90:36 | call to set | set.cpp:98:7:98:9 | s19 | |
+| set.cpp:90:34:90:36 | call to set | set.cpp:100:12:100:14 | s19 | |
+| set.cpp:90:34:90:36 | call to set | set.cpp:104:7:104:9 | s19 | |
+| set.cpp:90:34:90:36 | call to set | set.cpp:126:1:126:1 | s19 | |
+| set.cpp:91:2:91:4 | ref arg s16 | set.cpp:95:7:95:9 | s16 | |
+| set.cpp:91:2:91:4 | ref arg s16 | set.cpp:99:2:99:4 | s16 | |
+| set.cpp:91:2:91:4 | ref arg s16 | set.cpp:101:7:101:9 | s16 | |
+| set.cpp:91:2:91:4 | ref arg s16 | set.cpp:126:1:126:1 | s16 | |
+| set.cpp:91:13:91:18 | call to source | set.cpp:91:2:91:4 | ref arg s16 | TAINT |
+| set.cpp:91:13:91:18 | call to source | set.cpp:91:6:91:11 | call to insert | TAINT |
+| set.cpp:92:2:92:4 | ref arg s17 | set.cpp:96:7:96:9 | s17 | |
+| set.cpp:92:2:92:4 | ref arg s17 | set.cpp:99:12:99:14 | s17 | |
+| set.cpp:92:2:92:4 | ref arg s17 | set.cpp:102:7:102:9 | s17 | |
+| set.cpp:92:2:92:4 | ref arg s17 | set.cpp:126:1:126:1 | s17 | |
+| set.cpp:92:13:92:17 | abc | set.cpp:92:2:92:4 | ref arg s17 | TAINT |
+| set.cpp:92:13:92:17 | abc | set.cpp:92:6:92:11 | call to insert | TAINT |
+| set.cpp:93:2:93:4 | ref arg s18 | set.cpp:97:7:97:9 | s18 | |
+| set.cpp:93:2:93:4 | ref arg s18 | set.cpp:100:2:100:4 | s18 | |
+| set.cpp:93:2:93:4 | ref arg s18 | set.cpp:103:7:103:9 | s18 | |
+| set.cpp:93:2:93:4 | ref arg s18 | set.cpp:126:1:126:1 | s18 | |
+| set.cpp:93:13:93:17 | def | set.cpp:93:2:93:4 | ref arg s18 | TAINT |
+| set.cpp:93:13:93:17 | def | set.cpp:93:6:93:11 | call to insert | TAINT |
+| set.cpp:94:2:94:4 | ref arg s19 | set.cpp:98:7:98:9 | s19 | |
+| set.cpp:94:2:94:4 | ref arg s19 | set.cpp:100:12:100:14 | s19 | |
+| set.cpp:94:2:94:4 | ref arg s19 | set.cpp:104:7:104:9 | s19 | |
+| set.cpp:94:2:94:4 | ref arg s19 | set.cpp:126:1:126:1 | s19 | |
+| set.cpp:94:13:94:18 | call to source | set.cpp:94:2:94:4 | ref arg s19 | TAINT |
+| set.cpp:94:13:94:18 | call to source | set.cpp:94:6:94:11 | call to insert | TAINT |
+| set.cpp:95:7:95:9 | s16 | set.cpp:95:7:95:9 | call to set | |
+| set.cpp:96:7:96:9 | s17 | set.cpp:96:7:96:9 | call to set | |
+| set.cpp:97:7:97:9 | s18 | set.cpp:97:7:97:9 | call to set | |
+| set.cpp:98:7:98:9 | s19 | set.cpp:98:7:98:9 | call to set | |
+| set.cpp:99:2:99:4 | ref arg s16 | set.cpp:101:7:101:9 | s16 | |
+| set.cpp:99:2:99:4 | ref arg s16 | set.cpp:126:1:126:1 | s16 | |
+| set.cpp:99:12:99:14 | ref arg s17 | set.cpp:102:7:102:9 | s17 | |
+| set.cpp:99:12:99:14 | ref arg s17 | set.cpp:126:1:126:1 | s17 | |
+| set.cpp:99:12:99:14 | s17 | set.cpp:99:2:99:4 | ref arg s16 | TAINT |
+| set.cpp:100:2:100:4 | ref arg s18 | set.cpp:103:7:103:9 | s18 | |
+| set.cpp:100:2:100:4 | ref arg s18 | set.cpp:126:1:126:1 | s18 | |
+| set.cpp:100:12:100:14 | ref arg s19 | set.cpp:104:7:104:9 | s19 | |
+| set.cpp:100:12:100:14 | ref arg s19 | set.cpp:126:1:126:1 | s19 | |
+| set.cpp:100:12:100:14 | s19 | set.cpp:100:2:100:4 | ref arg s18 | TAINT |
+| set.cpp:101:7:101:9 | s16 | set.cpp:101:7:101:9 | call to set | |
+| set.cpp:102:7:102:9 | s17 | set.cpp:102:7:102:9 | call to set | |
+| set.cpp:103:7:103:9 | s18 | set.cpp:103:7:103:9 | call to set | |
+| set.cpp:104:7:104:9 | s19 | set.cpp:104:7:104:9 | call to set | |
+| set.cpp:107:19:107:21 | call to set | set.cpp:108:2:108:4 | s20 | |
+| set.cpp:107:19:107:21 | call to set | set.cpp:109:2:109:4 | s20 | |
+| set.cpp:107:19:107:21 | call to set | set.cpp:110:7:110:9 | s20 | |
+| set.cpp:107:19:107:21 | call to set | set.cpp:111:7:111:9 | s20 | |
+| set.cpp:107:19:107:21 | call to set | set.cpp:111:17:111:19 | s20 | |
+| set.cpp:107:19:107:21 | call to set | set.cpp:112:7:112:9 | s20 | |
+| set.cpp:107:19:107:21 | call to set | set.cpp:113:2:113:4 | s20 | |
+| set.cpp:107:19:107:21 | call to set | set.cpp:114:7:114:9 | s20 | |
+| set.cpp:107:19:107:21 | call to set | set.cpp:126:1:126:1 | s20 | |
+| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:109:2:109:4 | s20 | |
+| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:110:7:110:9 | s20 | |
+| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:111:7:111:9 | s20 | |
+| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:111:17:111:19 | s20 | |
+| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:112:7:112:9 | s20 | |
+| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:113:2:113:4 | s20 | |
+| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:114:7:114:9 | s20 | |
+| set.cpp:108:2:108:4 | ref arg s20 | set.cpp:126:1:126:1 | s20 | |
+| set.cpp:108:13:108:18 | call to source | set.cpp:108:2:108:4 | ref arg s20 | TAINT |
+| set.cpp:108:13:108:18 | call to source | set.cpp:108:6:108:11 | call to insert | TAINT |
+| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:110:7:110:9 | s20 | |
+| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:111:7:111:9 | s20 | |
+| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:111:17:111:19 | s20 | |
+| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:112:7:112:9 | s20 | |
+| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:113:2:113:4 | s20 | |
+| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:114:7:114:9 | s20 | |
+| set.cpp:109:2:109:4 | ref arg s20 | set.cpp:126:1:126:1 | s20 | |
+| set.cpp:109:13:109:18 | call to source | set.cpp:109:2:109:4 | ref arg s20 | TAINT |
+| set.cpp:109:13:109:18 | call to source | set.cpp:109:6:109:11 | call to insert | TAINT |
+| set.cpp:110:7:110:9 | s20 | set.cpp:110:7:110:9 | call to set | |
+| set.cpp:111:7:111:9 | ref arg s20 | set.cpp:112:7:112:9 | s20 | |
+| set.cpp:111:7:111:9 | ref arg s20 | set.cpp:113:2:113:4 | s20 | |
+| set.cpp:111:7:111:9 | ref arg s20 | set.cpp:114:7:114:9 | s20 | |
+| set.cpp:111:7:111:9 | ref arg s20 | set.cpp:126:1:126:1 | s20 | |
+| set.cpp:111:7:111:9 | s20 | set.cpp:111:11:111:15 | call to erase | TAINT |
+| set.cpp:111:17:111:19 | ref arg s20 | set.cpp:111:7:111:9 | s20 | |
+| set.cpp:111:17:111:19 | ref arg s20 | set.cpp:112:7:112:9 | s20 | |
+| set.cpp:111:17:111:19 | ref arg s20 | set.cpp:113:2:113:4 | s20 | |
+| set.cpp:111:17:111:19 | ref arg s20 | set.cpp:114:7:114:9 | s20 | |
+| set.cpp:111:17:111:19 | ref arg s20 | set.cpp:126:1:126:1 | s20 | |
+| set.cpp:111:17:111:19 | s20 | set.cpp:111:21:111:25 | call to begin | TAINT |
+| set.cpp:112:7:112:9 | s20 | set.cpp:112:7:112:9 | call to set | |
+| set.cpp:113:2:113:4 | ref arg s20 | set.cpp:114:7:114:9 | s20 | |
+| set.cpp:113:2:113:4 | ref arg s20 | set.cpp:126:1:126:1 | s20 | |
+| set.cpp:114:7:114:9 | s20 | set.cpp:114:7:114:9 | call to set | |
+| set.cpp:117:19:117:21 | call to set | set.cpp:118:7:118:9 | s21 | |
+| set.cpp:117:19:117:21 | call to set | set.cpp:119:7:119:9 | s21 | |
+| set.cpp:117:19:117:21 | call to set | set.cpp:120:7:120:9 | s21 | |
+| set.cpp:117:19:117:21 | call to set | set.cpp:121:7:121:9 | s21 | |
+| set.cpp:117:19:117:21 | call to set | set.cpp:126:1:126:1 | s21 | |
+| set.cpp:117:24:117:26 | call to set | set.cpp:122:7:122:9 | s22 | |
+| set.cpp:117:24:117:26 | call to set | set.cpp:122:24:122:26 | s22 | |
+| set.cpp:117:24:117:26 | call to set | set.cpp:123:7:123:9 | s22 | |
+| set.cpp:117:24:117:26 | call to set | set.cpp:124:7:124:9 | s22 | |
+| set.cpp:117:24:117:26 | call to set | set.cpp:124:24:124:26 | s22 | |
+| set.cpp:117:24:117:26 | call to set | set.cpp:125:7:125:9 | s22 | |
+| set.cpp:117:24:117:26 | call to set | set.cpp:126:1:126:1 | s22 | |
+| set.cpp:118:7:118:9 | ref arg s21 | set.cpp:119:7:119:9 | s21 | |
+| set.cpp:118:7:118:9 | ref arg s21 | set.cpp:120:7:120:9 | s21 | |
+| set.cpp:118:7:118:9 | ref arg s21 | set.cpp:121:7:121:9 | s21 | |
+| set.cpp:118:7:118:9 | ref arg s21 | set.cpp:126:1:126:1 | s21 | |
+| set.cpp:118:7:118:9 | s21 | set.cpp:118:11:118:17 | call to emplace | TAINT |
+| set.cpp:118:19:118:23 | abc | set.cpp:118:7:118:9 | ref arg s21 | TAINT |
+| set.cpp:118:19:118:23 | abc | set.cpp:118:11:118:17 | call to emplace | TAINT |
+| set.cpp:118:26:118:30 | first | set.cpp:118:7:118:30 | call to iterator | |
+| set.cpp:119:7:119:9 | s21 | set.cpp:119:7:119:9 | call to set | |
+| set.cpp:120:7:120:9 | ref arg s21 | set.cpp:121:7:121:9 | s21 | |
+| set.cpp:120:7:120:9 | ref arg s21 | set.cpp:126:1:126:1 | s21 | |
+| set.cpp:120:7:120:9 | s21 | set.cpp:120:11:120:17 | call to emplace | TAINT |
+| set.cpp:120:19:120:24 | call to source | set.cpp:120:7:120:9 | ref arg s21 | TAINT |
+| set.cpp:120:19:120:24 | call to source | set.cpp:120:11:120:17 | call to emplace | TAINT |
+| set.cpp:120:29:120:33 | first | set.cpp:120:7:120:33 | call to iterator | |
+| set.cpp:121:7:121:9 | s21 | set.cpp:121:7:121:9 | call to set | |
+| set.cpp:122:7:122:9 | ref arg s22 | set.cpp:123:7:123:9 | s22 | |
+| set.cpp:122:7:122:9 | ref arg s22 | set.cpp:124:7:124:9 | s22 | |
+| set.cpp:122:7:122:9 | ref arg s22 | set.cpp:124:24:124:26 | s22 | |
+| set.cpp:122:7:122:9 | ref arg s22 | set.cpp:125:7:125:9 | s22 | |
+| set.cpp:122:7:122:9 | ref arg s22 | set.cpp:126:1:126:1 | s22 | |
+| set.cpp:122:7:122:9 | s22 | set.cpp:122:11:122:22 | call to emplace_hint | TAINT |
+| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:122:7:122:9 | s22 | |
+| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:123:7:123:9 | s22 | |
+| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:124:7:124:9 | s22 | |
+| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:124:24:124:26 | s22 | |
+| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:125:7:125:9 | s22 | |
+| set.cpp:122:24:122:26 | ref arg s22 | set.cpp:126:1:126:1 | s22 | |
+| set.cpp:122:24:122:26 | s22 | set.cpp:122:28:122:32 | call to begin | TAINT |
+| set.cpp:122:24:122:34 | call to iterator | set.cpp:122:7:122:9 | ref arg s22 | TAINT |
+| set.cpp:122:24:122:34 | call to iterator | set.cpp:122:11:122:22 | call to emplace_hint | TAINT |
+| set.cpp:122:28:122:32 | call to begin | set.cpp:122:24:122:34 | call to iterator | TAINT |
+| set.cpp:122:37:122:41 | abc | set.cpp:122:7:122:9 | ref arg s22 | TAINT |
+| set.cpp:122:37:122:41 | abc | set.cpp:122:11:122:22 | call to emplace_hint | TAINT |
+| set.cpp:123:7:123:9 | s22 | set.cpp:123:7:123:9 | call to set | |
+| set.cpp:124:7:124:9 | ref arg s22 | set.cpp:125:7:125:9 | s22 | |
+| set.cpp:124:7:124:9 | ref arg s22 | set.cpp:126:1:126:1 | s22 | |
+| set.cpp:124:7:124:9 | s22 | set.cpp:124:11:124:22 | call to emplace_hint | TAINT |
+| set.cpp:124:24:124:26 | ref arg s22 | set.cpp:124:7:124:9 | s22 | |
+| set.cpp:124:24:124:26 | ref arg s22 | set.cpp:125:7:125:9 | s22 | |
+| set.cpp:124:24:124:26 | ref arg s22 | set.cpp:126:1:126:1 | s22 | |
+| set.cpp:124:24:124:26 | s22 | set.cpp:124:28:124:32 | call to begin | TAINT |
+| set.cpp:124:24:124:34 | call to iterator | set.cpp:124:7:124:9 | ref arg s22 | TAINT |
+| set.cpp:124:24:124:34 | call to iterator | set.cpp:124:11:124:22 | call to emplace_hint | TAINT |
+| set.cpp:124:28:124:32 | call to begin | set.cpp:124:24:124:34 | call to iterator | TAINT |
+| set.cpp:124:37:124:42 | call to source | set.cpp:124:7:124:9 | ref arg s22 | TAINT |
+| set.cpp:124:37:124:42 | call to source | set.cpp:124:11:124:22 | call to emplace_hint | TAINT |
+| set.cpp:125:7:125:9 | s22 | set.cpp:125:7:125:9 | call to set | |
+| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:133:7:133:8 | s1 | |
+| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:137:12:137:13 | s1 | |
+| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:137:24:137:25 | s1 | |
+| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:139:7:139:8 | s1 | |
+| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:145:7:145:8 | s1 | |
+| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:169:12:169:13 | s1 | |
+| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:169:30:169:31 | s1 | |
+| set.cpp:131:29:131:30 | call to unordered_set | set.cpp:238:1:238:1 | s1 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:134:7:134:8 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:138:12:138:13 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:138:24:138:25 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:140:7:140:8 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:146:7:146:8 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:153:32:153:33 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:154:34:154:35 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:155:32:155:33 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:155:44:155:45 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:157:8:157:9 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:173:12:173:13 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:173:30:173:31 | s2 | |
+| set.cpp:131:33:131:34 | call to unordered_set | set.cpp:238:1:238:1 | s2 | |
+| set.cpp:131:37:131:38 | call to unordered_set | set.cpp:135:7:135:8 | s3 | |
+| set.cpp:131:37:131:38 | call to unordered_set | set.cpp:135:17:135:18 | s3 | |
+| set.cpp:131:37:131:38 | call to unordered_set | set.cpp:141:7:141:8 | s3 | |
+| set.cpp:131:37:131:38 | call to unordered_set | set.cpp:147:7:147:8 | s3 | |
+| set.cpp:131:37:131:38 | call to unordered_set | set.cpp:238:1:238:1 | s3 | |
+| set.cpp:131:41:131:42 | call to unordered_set | set.cpp:136:7:136:8 | s4 | |
+| set.cpp:131:41:131:42 | call to unordered_set | set.cpp:136:17:136:18 | s4 | |
+| set.cpp:131:41:131:42 | call to unordered_set | set.cpp:142:7:142:8 | s4 | |
+| set.cpp:131:41:131:42 | call to unordered_set | set.cpp:148:7:148:8 | s4 | |
+| set.cpp:131:41:131:42 | call to unordered_set | set.cpp:238:1:238:1 | s4 | |
+| set.cpp:131:45:131:46 | call to unordered_set | set.cpp:137:2:137:3 | s5 | |
+| set.cpp:131:45:131:46 | call to unordered_set | set.cpp:143:7:143:8 | s5 | |
+| set.cpp:131:45:131:46 | call to unordered_set | set.cpp:149:7:149:8 | s5 | |
+| set.cpp:131:45:131:46 | call to unordered_set | set.cpp:238:1:238:1 | s5 | |
+| set.cpp:131:49:131:50 | call to unordered_set | set.cpp:138:2:138:3 | s6 | |
+| set.cpp:131:49:131:50 | call to unordered_set | set.cpp:144:7:144:8 | s6 | |
+| set.cpp:131:49:131:50 | call to unordered_set | set.cpp:150:7:150:8 | s6 | |
+| set.cpp:131:49:131:50 | call to unordered_set | set.cpp:238:1:238:1 | s6 | |
+| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:137:12:137:13 | s1 | |
+| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:137:24:137:25 | s1 | |
+| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:139:7:139:8 | s1 | |
+| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:145:7:145:8 | s1 | |
+| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:169:12:169:13 | s1 | |
+| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:169:30:169:31 | s1 | |
+| set.cpp:133:7:133:8 | ref arg s1 | set.cpp:238:1:238:1 | s1 | |
+| set.cpp:133:17:133:21 | abc | set.cpp:133:7:133:8 | ref arg s1 | TAINT |
+| set.cpp:133:17:133:21 | abc | set.cpp:133:10:133:15 | call to insert | TAINT |
+| set.cpp:133:24:133:28 | first | set.cpp:133:7:133:28 | call to iterator | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:138:12:138:13 | s2 | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:138:24:138:25 | s2 | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:140:7:140:8 | s2 | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:146:7:146:8 | s2 | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:153:32:153:33 | s2 | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:154:34:154:35 | s2 | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:155:32:155:33 | s2 | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:155:44:155:45 | s2 | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:157:8:157:9 | s2 | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:173:12:173:13 | s2 | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:173:30:173:31 | s2 | |
+| set.cpp:134:7:134:8 | ref arg s2 | set.cpp:238:1:238:1 | s2 | |
+| set.cpp:134:17:134:22 | call to source | set.cpp:134:7:134:8 | ref arg s2 | TAINT |
+| set.cpp:134:17:134:22 | call to source | set.cpp:134:10:134:15 | call to insert | TAINT |
+| set.cpp:134:27:134:31 | first | set.cpp:134:7:134:31 | call to iterator | |
+| set.cpp:135:7:135:8 | ref arg s3 | set.cpp:141:7:141:8 | s3 | |
+| set.cpp:135:7:135:8 | ref arg s3 | set.cpp:147:7:147:8 | s3 | |
+| set.cpp:135:7:135:8 | ref arg s3 | set.cpp:238:1:238:1 | s3 | |
+| set.cpp:135:17:135:18 | ref arg s3 | set.cpp:135:7:135:8 | s3 | |
+| set.cpp:135:17:135:18 | ref arg s3 | set.cpp:141:7:141:8 | s3 | |
+| set.cpp:135:17:135:18 | ref arg s3 | set.cpp:147:7:147:8 | s3 | |
+| set.cpp:135:17:135:18 | ref arg s3 | set.cpp:238:1:238:1 | s3 | |
+| set.cpp:135:17:135:18 | s3 | set.cpp:135:20:135:24 | call to begin | TAINT |
+| set.cpp:135:20:135:24 | call to begin | set.cpp:135:17:135:26 | call to iterator | TAINT |
+| set.cpp:135:29:135:33 | abc | set.cpp:135:7:135:8 | ref arg s3 | TAINT |
+| set.cpp:135:29:135:33 | abc | set.cpp:135:10:135:15 | call to insert | TAINT |
+| set.cpp:136:7:136:8 | ref arg s4 | set.cpp:142:7:142:8 | s4 | |
+| set.cpp:136:7:136:8 | ref arg s4 | set.cpp:148:7:148:8 | s4 | |
+| set.cpp:136:7:136:8 | ref arg s4 | set.cpp:238:1:238:1 | s4 | |
+| set.cpp:136:17:136:18 | ref arg s4 | set.cpp:136:7:136:8 | s4 | |
+| set.cpp:136:17:136:18 | ref arg s4 | set.cpp:142:7:142:8 | s4 | |
+| set.cpp:136:17:136:18 | ref arg s4 | set.cpp:148:7:148:8 | s4 | |
+| set.cpp:136:17:136:18 | ref arg s4 | set.cpp:238:1:238:1 | s4 | |
+| set.cpp:136:17:136:18 | s4 | set.cpp:136:20:136:24 | call to begin | TAINT |
+| set.cpp:136:20:136:24 | call to begin | set.cpp:136:17:136:26 | call to iterator | TAINT |
+| set.cpp:136:29:136:34 | call to source | set.cpp:136:7:136:8 | ref arg s4 | TAINT |
+| set.cpp:136:29:136:34 | call to source | set.cpp:136:10:136:15 | call to insert | TAINT |
+| set.cpp:137:2:137:3 | ref arg s5 | set.cpp:143:7:143:8 | s5 | |
+| set.cpp:137:2:137:3 | ref arg s5 | set.cpp:149:7:149:8 | s5 | |
+| set.cpp:137:2:137:3 | ref arg s5 | set.cpp:238:1:238:1 | s5 | |
+| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:137:24:137:25 | s1 | |
+| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:139:7:139:8 | s1 | |
+| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:145:7:145:8 | s1 | |
+| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:169:12:169:13 | s1 | |
+| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:169:30:169:31 | s1 | |
+| set.cpp:137:12:137:13 | ref arg s1 | set.cpp:238:1:238:1 | s1 | |
+| set.cpp:137:12:137:13 | s1 | set.cpp:137:15:137:19 | call to begin | TAINT |
+| set.cpp:137:24:137:25 | ref arg s1 | set.cpp:139:7:139:8 | s1 | |
+| set.cpp:137:24:137:25 | ref arg s1 | set.cpp:145:7:145:8 | s1 | |
+| set.cpp:137:24:137:25 | ref arg s1 | set.cpp:169:12:169:13 | s1 | |
+| set.cpp:137:24:137:25 | ref arg s1 | set.cpp:169:30:169:31 | s1 | |
+| set.cpp:137:24:137:25 | ref arg s1 | set.cpp:238:1:238:1 | s1 | |
+| set.cpp:137:24:137:25 | s1 | set.cpp:137:27:137:29 | call to end | TAINT |
+| set.cpp:137:27:137:29 | call to end | set.cpp:137:2:137:3 | ref arg s5 | TAINT |
+| set.cpp:137:27:137:29 | call to end | set.cpp:137:5:137:10 | call to insert | TAINT |
+| set.cpp:138:2:138:3 | ref arg s6 | set.cpp:144:7:144:8 | s6 | |
+| set.cpp:138:2:138:3 | ref arg s6 | set.cpp:150:7:150:8 | s6 | |
+| set.cpp:138:2:138:3 | ref arg s6 | set.cpp:238:1:238:1 | s6 | |
+| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:138:24:138:25 | s2 | |
+| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:140:7:140:8 | s2 | |
+| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:146:7:146:8 | s2 | |
+| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:153:32:153:33 | s2 | |
+| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:154:34:154:35 | s2 | |
+| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:155:32:155:33 | s2 | |
+| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:155:44:155:45 | s2 | |
+| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:157:8:157:9 | s2 | |
+| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:173:12:173:13 | s2 | |
+| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:173:30:173:31 | s2 | |
+| set.cpp:138:12:138:13 | ref arg s2 | set.cpp:238:1:238:1 | s2 | |
+| set.cpp:138:12:138:13 | s2 | set.cpp:138:15:138:19 | call to begin | TAINT |
+| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:140:7:140:8 | s2 | |
+| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:146:7:146:8 | s2 | |
+| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:153:32:153:33 | s2 | |
+| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:154:34:154:35 | s2 | |
+| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:155:32:155:33 | s2 | |
+| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:155:44:155:45 | s2 | |
+| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:157:8:157:9 | s2 | |
+| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:173:12:173:13 | s2 | |
+| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:173:30:173:31 | s2 | |
+| set.cpp:138:24:138:25 | ref arg s2 | set.cpp:238:1:238:1 | s2 | |
+| set.cpp:138:24:138:25 | s2 | set.cpp:138:27:138:29 | call to end | TAINT |
+| set.cpp:138:27:138:29 | call to end | set.cpp:138:2:138:3 | ref arg s6 | TAINT |
+| set.cpp:138:27:138:29 | call to end | set.cpp:138:5:138:10 | call to insert | TAINT |
+| set.cpp:139:7:139:8 | s1 | set.cpp:139:7:139:8 | call to unordered_set | |
+| set.cpp:140:7:140:8 | s2 | set.cpp:140:7:140:8 | call to unordered_set | |
+| set.cpp:141:7:141:8 | s3 | set.cpp:141:7:141:8 | call to unordered_set | |
+| set.cpp:142:7:142:8 | s4 | set.cpp:142:7:142:8 | call to unordered_set | |
+| set.cpp:143:7:143:8 | s5 | set.cpp:143:7:143:8 | call to unordered_set | |
+| set.cpp:144:7:144:8 | s6 | set.cpp:144:7:144:8 | call to unordered_set | |
+| set.cpp:145:7:145:8 | ref arg s1 | set.cpp:169:12:169:13 | s1 | |
+| set.cpp:145:7:145:8 | ref arg s1 | set.cpp:169:30:169:31 | s1 | |
+| set.cpp:145:7:145:8 | ref arg s1 | set.cpp:238:1:238:1 | s1 | |
+| set.cpp:145:7:145:8 | s1 | set.cpp:145:10:145:13 | call to find | TAINT |
+| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:153:32:153:33 | s2 | |
+| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:154:34:154:35 | s2 | |
+| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:155:32:155:33 | s2 | |
+| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:155:44:155:45 | s2 | |
+| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:157:8:157:9 | s2 | |
+| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:173:12:173:13 | s2 | |
+| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:173:30:173:31 | s2 | |
+| set.cpp:146:7:146:8 | ref arg s2 | set.cpp:238:1:238:1 | s2 | |
+| set.cpp:146:7:146:8 | s2 | set.cpp:146:10:146:13 | call to find | TAINT |
+| set.cpp:147:7:147:8 | ref arg s3 | set.cpp:238:1:238:1 | s3 | |
+| set.cpp:147:7:147:8 | s3 | set.cpp:147:10:147:13 | call to find | TAINT |
+| set.cpp:148:7:148:8 | ref arg s4 | set.cpp:238:1:238:1 | s4 | |
+| set.cpp:148:7:148:8 | s4 | set.cpp:148:10:148:13 | call to find | TAINT |
+| set.cpp:149:7:149:8 | ref arg s5 | set.cpp:238:1:238:1 | s5 | |
+| set.cpp:149:7:149:8 | s5 | set.cpp:149:10:149:13 | call to find | TAINT |
+| set.cpp:150:7:150:8 | ref arg s6 | set.cpp:238:1:238:1 | s6 | |
+| set.cpp:150:7:150:8 | s6 | set.cpp:150:10:150:13 | call to find | TAINT |
+| set.cpp:153:32:153:33 | s2 | set.cpp:153:32:153:34 | call to unordered_set | |
+| set.cpp:153:32:153:34 | call to unordered_set | set.cpp:158:7:158:8 | s7 | |
+| set.cpp:153:32:153:34 | call to unordered_set | set.cpp:162:7:162:8 | s7 | |
+| set.cpp:153:32:153:34 | call to unordered_set | set.cpp:238:1:238:1 | s7 | |
+| set.cpp:154:33:154:35 | call to unordered_set | set.cpp:159:7:159:8 | s8 | |
+| set.cpp:154:33:154:35 | call to unordered_set | set.cpp:163:7:163:8 | s8 | |
+| set.cpp:154:33:154:35 | call to unordered_set | set.cpp:238:1:238:1 | s8 | |
+| set.cpp:154:34:154:35 | s2 | set.cpp:154:33:154:35 | call to unordered_set | |
+| set.cpp:155:32:155:33 | ref arg s2 | set.cpp:155:44:155:45 | s2 | |
+| set.cpp:155:32:155:33 | ref arg s2 | set.cpp:157:8:157:9 | s2 | |
+| set.cpp:155:32:155:33 | ref arg s2 | set.cpp:173:12:173:13 | s2 | |
+| set.cpp:155:32:155:33 | ref arg s2 | set.cpp:173:30:173:31 | s2 | |
+| set.cpp:155:32:155:33 | ref arg s2 | set.cpp:238:1:238:1 | s2 | |
+| set.cpp:155:32:155:33 | s2 | set.cpp:155:35:155:39 | call to begin | TAINT |
+| set.cpp:155:32:155:52 | call to unordered_set | set.cpp:160:7:160:8 | s9 | |
+| set.cpp:155:32:155:52 | call to unordered_set | set.cpp:164:7:164:8 | s9 | |
+| set.cpp:155:32:155:52 | call to unordered_set | set.cpp:238:1:238:1 | s9 | |
+| set.cpp:155:35:155:39 | call to begin | set.cpp:155:32:155:52 | call to unordered_set | TAINT |
+| set.cpp:155:44:155:45 | ref arg s2 | set.cpp:157:8:157:9 | s2 | |
+| set.cpp:155:44:155:45 | ref arg s2 | set.cpp:173:12:173:13 | s2 | |
+| set.cpp:155:44:155:45 | ref arg s2 | set.cpp:173:30:173:31 | s2 | |
+| set.cpp:155:44:155:45 | ref arg s2 | set.cpp:238:1:238:1 | s2 | |
+| set.cpp:155:44:155:45 | s2 | set.cpp:155:47:155:49 | call to end | TAINT |
+| set.cpp:155:47:155:49 | call to end | set.cpp:155:32:155:52 | call to unordered_set | TAINT |
+| set.cpp:156:29:156:31 | call to unordered_set | set.cpp:157:2:157:4 | s10 | |
+| set.cpp:156:29:156:31 | call to unordered_set | set.cpp:161:7:161:9 | s10 | |
+| set.cpp:156:29:156:31 | call to unordered_set | set.cpp:165:7:165:9 | s10 | |
+| set.cpp:156:29:156:31 | call to unordered_set | set.cpp:238:1:238:1 | s10 | |
+| set.cpp:157:2:157:4 | ref arg s10 | set.cpp:161:7:161:9 | s10 | |
+| set.cpp:157:2:157:4 | ref arg s10 | set.cpp:165:7:165:9 | s10 | |
+| set.cpp:157:2:157:4 | ref arg s10 | set.cpp:238:1:238:1 | s10 | |
+| set.cpp:157:8:157:9 | s2 | set.cpp:157:2:157:4 | ref arg s10 | TAINT |
+| set.cpp:157:8:157:9 | s2 | set.cpp:157:6:157:6 | call to operator= | TAINT |
+| set.cpp:158:7:158:8 | s7 | set.cpp:158:7:158:8 | call to unordered_set | |
+| set.cpp:159:7:159:8 | s8 | set.cpp:159:7:159:8 | call to unordered_set | |
+| set.cpp:160:7:160:8 | s9 | set.cpp:160:7:160:8 | call to unordered_set | |
+| set.cpp:161:7:161:9 | s10 | set.cpp:161:7:161:9 | call to unordered_set | |
+| set.cpp:162:7:162:8 | ref arg s7 | set.cpp:238:1:238:1 | s7 | |
+| set.cpp:162:7:162:8 | s7 | set.cpp:162:10:162:13 | call to find | TAINT |
+| set.cpp:163:7:163:8 | ref arg s8 | set.cpp:238:1:238:1 | s8 | |
+| set.cpp:163:7:163:8 | s8 | set.cpp:163:10:163:13 | call to find | TAINT |
+| set.cpp:164:7:164:8 | ref arg s9 | set.cpp:238:1:238:1 | s9 | |
+| set.cpp:164:7:164:8 | s9 | set.cpp:164:10:164:13 | call to find | TAINT |
+| set.cpp:165:7:165:9 | ref arg s10 | set.cpp:238:1:238:1 | s10 | |
+| set.cpp:165:7:165:9 | s10 | set.cpp:165:11:165:14 | call to find | TAINT |
+| set.cpp:169:12:169:13 | ref arg s1 | set.cpp:169:30:169:31 | s1 | |
+| set.cpp:169:12:169:13 | ref arg s1 | set.cpp:238:1:238:1 | s1 | |
+| set.cpp:169:12:169:13 | s1 | set.cpp:169:15:169:19 | call to begin | TAINT |
+| set.cpp:169:15:169:19 | call to begin | set.cpp:169:7:169:21 | ... = ... | |
+| set.cpp:169:15:169:19 | call to begin | set.cpp:169:24:169:25 | i1 | |
+| set.cpp:169:15:169:19 | call to begin | set.cpp:169:40:169:41 | i1 | |
+| set.cpp:169:15:169:19 | call to begin | set.cpp:171:9:171:10 | i1 | |
+| set.cpp:169:30:169:31 | ref arg s1 | set.cpp:169:30:169:31 | s1 | |
+| set.cpp:169:30:169:31 | ref arg s1 | set.cpp:238:1:238:1 | s1 | |
+| set.cpp:169:30:169:31 | s1 | set.cpp:169:33:169:35 | call to end | TAINT |
+| set.cpp:169:40:169:41 | i1 | set.cpp:169:42:169:42 | call to operator++ | |
+| set.cpp:169:40:169:41 | ref arg i1 | set.cpp:169:24:169:25 | i1 | |
+| set.cpp:169:40:169:41 | ref arg i1 | set.cpp:169:40:169:41 | i1 | |
+| set.cpp:169:40:169:41 | ref arg i1 | set.cpp:171:9:171:10 | i1 | |
+| set.cpp:171:9:171:10 | i1 | set.cpp:171:8:171:8 | call to operator* | TAINT |
+| set.cpp:173:12:173:13 | ref arg s2 | set.cpp:173:30:173:31 | s2 | |
+| set.cpp:173:12:173:13 | ref arg s2 | set.cpp:238:1:238:1 | s2 | |
+| set.cpp:173:12:173:13 | s2 | set.cpp:173:15:173:19 | call to begin | TAINT |
+| set.cpp:173:15:173:19 | call to begin | set.cpp:173:7:173:21 | ... = ... | |
+| set.cpp:173:15:173:19 | call to begin | set.cpp:173:24:173:25 | i2 | |
+| set.cpp:173:15:173:19 | call to begin | set.cpp:173:40:173:41 | i2 | |
+| set.cpp:173:15:173:19 | call to begin | set.cpp:175:9:175:10 | i2 | |
+| set.cpp:173:30:173:31 | ref arg s2 | set.cpp:173:30:173:31 | s2 | |
+| set.cpp:173:30:173:31 | ref arg s2 | set.cpp:238:1:238:1 | s2 | |
+| set.cpp:173:30:173:31 | s2 | set.cpp:173:33:173:35 | call to end | TAINT |
+| set.cpp:173:40:173:41 | i2 | set.cpp:173:42:173:42 | call to operator++ | |
+| set.cpp:173:40:173:41 | ref arg i2 | set.cpp:173:24:173:25 | i2 | |
+| set.cpp:173:40:173:41 | ref arg i2 | set.cpp:173:40:173:41 | i2 | |
+| set.cpp:173:40:173:41 | ref arg i2 | set.cpp:175:9:175:10 | i2 | |
+| set.cpp:175:9:175:10 | i2 | set.cpp:175:8:175:8 | call to operator* | TAINT |
+| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:180:2:180:4 | s11 | |
+| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:181:2:181:4 | s11 | |
+| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:182:2:182:4 | s11 | |
+| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:183:7:183:9 | s11 | |
+| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:184:7:184:9 | s11 | |
+| set.cpp:179:29:179:31 | call to unordered_set | set.cpp:238:1:238:1 | s11 | |
+| set.cpp:180:2:180:4 | ref arg s11 | set.cpp:181:2:181:4 | s11 | |
+| set.cpp:180:2:180:4 | ref arg s11 | set.cpp:182:2:182:4 | s11 | |
+| set.cpp:180:2:180:4 | ref arg s11 | set.cpp:183:7:183:9 | s11 | |
+| set.cpp:180:2:180:4 | ref arg s11 | set.cpp:184:7:184:9 | s11 | |
+| set.cpp:180:2:180:4 | ref arg s11 | set.cpp:238:1:238:1 | s11 | |
+| set.cpp:180:13:180:15 | a | set.cpp:180:2:180:4 | ref arg s11 | TAINT |
+| set.cpp:180:13:180:15 | a | set.cpp:180:6:180:11 | call to insert | TAINT |
+| set.cpp:181:2:181:4 | ref arg s11 | set.cpp:182:2:182:4 | s11 | |
+| set.cpp:181:2:181:4 | ref arg s11 | set.cpp:183:7:183:9 | s11 | |
+| set.cpp:181:2:181:4 | ref arg s11 | set.cpp:184:7:184:9 | s11 | |
+| set.cpp:181:2:181:4 | ref arg s11 | set.cpp:238:1:238:1 | s11 | |
+| set.cpp:181:13:181:18 | call to source | set.cpp:181:2:181:4 | ref arg s11 | TAINT |
+| set.cpp:181:13:181:18 | call to source | set.cpp:181:6:181:11 | call to insert | TAINT |
+| set.cpp:182:2:182:4 | ref arg s11 | set.cpp:183:7:183:9 | s11 | |
+| set.cpp:182:2:182:4 | ref arg s11 | set.cpp:184:7:184:9 | s11 | |
+| set.cpp:182:2:182:4 | ref arg s11 | set.cpp:238:1:238:1 | s11 | |
+| set.cpp:182:13:182:15 | c | set.cpp:182:2:182:4 | ref arg s11 | TAINT |
+| set.cpp:182:13:182:15 | c | set.cpp:182:6:182:11 | call to insert | TAINT |
+| set.cpp:183:7:183:9 | ref arg s11 | set.cpp:184:7:184:9 | s11 | |
+| set.cpp:183:7:183:9 | ref arg s11 | set.cpp:238:1:238:1 | s11 | |
+| set.cpp:183:7:183:9 | s11 | set.cpp:183:11:183:21 | call to equal_range | TAINT |
+| set.cpp:183:28:183:32 | first | set.cpp:183:7:183:32 | call to iterator | |
+| set.cpp:184:7:184:9 | ref arg s11 | set.cpp:238:1:238:1 | s11 | |
+| set.cpp:184:7:184:9 | s11 | set.cpp:184:11:184:21 | call to equal_range | TAINT |
+| set.cpp:184:28:184:33 | second | set.cpp:184:7:184:33 | call to iterator | |
+| set.cpp:187:29:187:31 | call to unordered_set | set.cpp:188:2:188:4 | s12 | |
+| set.cpp:187:29:187:31 | call to unordered_set | set.cpp:190:7:190:9 | s12 | |
+| set.cpp:187:29:187:31 | call to unordered_set | set.cpp:194:2:194:4 | s12 | |
+| set.cpp:187:29:187:31 | call to unordered_set | set.cpp:196:7:196:9 | s12 | |
+| set.cpp:187:29:187:31 | call to unordered_set | set.cpp:238:1:238:1 | s12 | |
+| set.cpp:187:34:187:36 | call to unordered_set | set.cpp:191:7:191:9 | s13 | |
+| set.cpp:187:34:187:36 | call to unordered_set | set.cpp:194:11:194:13 | s13 | |
+| set.cpp:187:34:187:36 | call to unordered_set | set.cpp:197:7:197:9 | s13 | |
+| set.cpp:187:34:187:36 | call to unordered_set | set.cpp:238:1:238:1 | s13 | |
+| set.cpp:187:39:187:41 | call to unordered_set | set.cpp:192:7:192:9 | s14 | |
+| set.cpp:187:39:187:41 | call to unordered_set | set.cpp:195:2:195:4 | s14 | |
+| set.cpp:187:39:187:41 | call to unordered_set | set.cpp:198:7:198:9 | s14 | |
+| set.cpp:187:39:187:41 | call to unordered_set | set.cpp:238:1:238:1 | s14 | |
+| set.cpp:187:44:187:46 | call to unordered_set | set.cpp:189:2:189:4 | s15 | |
+| set.cpp:187:44:187:46 | call to unordered_set | set.cpp:193:7:193:9 | s15 | |
+| set.cpp:187:44:187:46 | call to unordered_set | set.cpp:195:11:195:13 | s15 | |
+| set.cpp:187:44:187:46 | call to unordered_set | set.cpp:199:7:199:9 | s15 | |
+| set.cpp:187:44:187:46 | call to unordered_set | set.cpp:238:1:238:1 | s15 | |
+| set.cpp:188:2:188:4 | ref arg s12 | set.cpp:190:7:190:9 | s12 | |
+| set.cpp:188:2:188:4 | ref arg s12 | set.cpp:194:2:194:4 | s12 | |
+| set.cpp:188:2:188:4 | ref arg s12 | set.cpp:196:7:196:9 | s12 | |
+| set.cpp:188:2:188:4 | ref arg s12 | set.cpp:238:1:238:1 | s12 | |
+| set.cpp:188:13:188:18 | call to source | set.cpp:188:2:188:4 | ref arg s12 | TAINT |
+| set.cpp:188:13:188:18 | call to source | set.cpp:188:6:188:11 | call to insert | TAINT |
+| set.cpp:189:2:189:4 | ref arg s15 | set.cpp:193:7:193:9 | s15 | |
+| set.cpp:189:2:189:4 | ref arg s15 | set.cpp:195:11:195:13 | s15 | |
+| set.cpp:189:2:189:4 | ref arg s15 | set.cpp:199:7:199:9 | s15 | |
+| set.cpp:189:2:189:4 | ref arg s15 | set.cpp:238:1:238:1 | s15 | |
+| set.cpp:189:13:189:18 | call to source | set.cpp:189:2:189:4 | ref arg s15 | TAINT |
+| set.cpp:189:13:189:18 | call to source | set.cpp:189:6:189:11 | call to insert | TAINT |
+| set.cpp:190:7:190:9 | s12 | set.cpp:190:7:190:9 | call to unordered_set | |
+| set.cpp:191:7:191:9 | s13 | set.cpp:191:7:191:9 | call to unordered_set | |
+| set.cpp:192:7:192:9 | s14 | set.cpp:192:7:192:9 | call to unordered_set | |
+| set.cpp:193:7:193:9 | s15 | set.cpp:193:7:193:9 | call to unordered_set | |
+| set.cpp:194:2:194:4 | ref arg s12 | set.cpp:196:7:196:9 | s12 | |
+| set.cpp:194:2:194:4 | ref arg s12 | set.cpp:238:1:238:1 | s12 | |
+| set.cpp:194:2:194:4 | s12 | set.cpp:194:11:194:13 | ref arg s13 | TAINT |
+| set.cpp:194:11:194:13 | ref arg s13 | set.cpp:197:7:197:9 | s13 | |
+| set.cpp:194:11:194:13 | ref arg s13 | set.cpp:238:1:238:1 | s13 | |
+| set.cpp:194:11:194:13 | s13 | set.cpp:194:2:194:4 | ref arg s12 | TAINT |
+| set.cpp:195:2:195:4 | ref arg s14 | set.cpp:198:7:198:9 | s14 | |
+| set.cpp:195:2:195:4 | ref arg s14 | set.cpp:238:1:238:1 | s14 | |
+| set.cpp:195:2:195:4 | s14 | set.cpp:195:11:195:13 | ref arg s15 | TAINT |
+| set.cpp:195:11:195:13 | ref arg s15 | set.cpp:199:7:199:9 | s15 | |
+| set.cpp:195:11:195:13 | ref arg s15 | set.cpp:238:1:238:1 | s15 | |
+| set.cpp:195:11:195:13 | s15 | set.cpp:195:2:195:4 | ref arg s14 | TAINT |
+| set.cpp:196:7:196:9 | s12 | set.cpp:196:7:196:9 | call to unordered_set | |
+| set.cpp:197:7:197:9 | s13 | set.cpp:197:7:197:9 | call to unordered_set | |
+| set.cpp:198:7:198:9 | s14 | set.cpp:198:7:198:9 | call to unordered_set | |
+| set.cpp:199:7:199:9 | s15 | set.cpp:199:7:199:9 | call to unordered_set | |
+| set.cpp:202:29:202:31 | call to unordered_set | set.cpp:203:2:203:4 | s16 | |
+| set.cpp:202:29:202:31 | call to unordered_set | set.cpp:207:7:207:9 | s16 | |
+| set.cpp:202:29:202:31 | call to unordered_set | set.cpp:211:2:211:4 | s16 | |
+| set.cpp:202:29:202:31 | call to unordered_set | set.cpp:213:7:213:9 | s16 | |
+| set.cpp:202:29:202:31 | call to unordered_set | set.cpp:238:1:238:1 | s16 | |
+| set.cpp:202:34:202:36 | call to unordered_set | set.cpp:204:2:204:4 | s17 | |
+| set.cpp:202:34:202:36 | call to unordered_set | set.cpp:208:7:208:9 | s17 | |
+| set.cpp:202:34:202:36 | call to unordered_set | set.cpp:211:12:211:14 | s17 | |
+| set.cpp:202:34:202:36 | call to unordered_set | set.cpp:214:7:214:9 | s17 | |
+| set.cpp:202:34:202:36 | call to unordered_set | set.cpp:238:1:238:1 | s17 | |
+| set.cpp:202:39:202:41 | call to unordered_set | set.cpp:205:2:205:4 | s18 | |
+| set.cpp:202:39:202:41 | call to unordered_set | set.cpp:209:7:209:9 | s18 | |
+| set.cpp:202:39:202:41 | call to unordered_set | set.cpp:212:2:212:4 | s18 | |
+| set.cpp:202:39:202:41 | call to unordered_set | set.cpp:215:7:215:9 | s18 | |
+| set.cpp:202:39:202:41 | call to unordered_set | set.cpp:238:1:238:1 | s18 | |
+| set.cpp:202:44:202:46 | call to unordered_set | set.cpp:206:2:206:4 | s19 | |
+| set.cpp:202:44:202:46 | call to unordered_set | set.cpp:210:7:210:9 | s19 | |
+| set.cpp:202:44:202:46 | call to unordered_set | set.cpp:212:12:212:14 | s19 | |
+| set.cpp:202:44:202:46 | call to unordered_set | set.cpp:216:7:216:9 | s19 | |
+| set.cpp:202:44:202:46 | call to unordered_set | set.cpp:238:1:238:1 | s19 | |
+| set.cpp:203:2:203:4 | ref arg s16 | set.cpp:207:7:207:9 | s16 | |
+| set.cpp:203:2:203:4 | ref arg s16 | set.cpp:211:2:211:4 | s16 | |
+| set.cpp:203:2:203:4 | ref arg s16 | set.cpp:213:7:213:9 | s16 | |
+| set.cpp:203:2:203:4 | ref arg s16 | set.cpp:238:1:238:1 | s16 | |
+| set.cpp:203:13:203:18 | call to source | set.cpp:203:2:203:4 | ref arg s16 | TAINT |
+| set.cpp:203:13:203:18 | call to source | set.cpp:203:6:203:11 | call to insert | TAINT |
+| set.cpp:204:2:204:4 | ref arg s17 | set.cpp:208:7:208:9 | s17 | |
+| set.cpp:204:2:204:4 | ref arg s17 | set.cpp:211:12:211:14 | s17 | |
+| set.cpp:204:2:204:4 | ref arg s17 | set.cpp:214:7:214:9 | s17 | |
+| set.cpp:204:2:204:4 | ref arg s17 | set.cpp:238:1:238:1 | s17 | |
+| set.cpp:204:13:204:17 | abc | set.cpp:204:2:204:4 | ref arg s17 | TAINT |
+| set.cpp:204:13:204:17 | abc | set.cpp:204:6:204:11 | call to insert | TAINT |
+| set.cpp:205:2:205:4 | ref arg s18 | set.cpp:209:7:209:9 | s18 | |
+| set.cpp:205:2:205:4 | ref arg s18 | set.cpp:212:2:212:4 | s18 | |
+| set.cpp:205:2:205:4 | ref arg s18 | set.cpp:215:7:215:9 | s18 | |
+| set.cpp:205:2:205:4 | ref arg s18 | set.cpp:238:1:238:1 | s18 | |
+| set.cpp:205:13:205:17 | def | set.cpp:205:2:205:4 | ref arg s18 | TAINT |
+| set.cpp:205:13:205:17 | def | set.cpp:205:6:205:11 | call to insert | TAINT |
+| set.cpp:206:2:206:4 | ref arg s19 | set.cpp:210:7:210:9 | s19 | |
+| set.cpp:206:2:206:4 | ref arg s19 | set.cpp:212:12:212:14 | s19 | |
+| set.cpp:206:2:206:4 | ref arg s19 | set.cpp:216:7:216:9 | s19 | |
+| set.cpp:206:2:206:4 | ref arg s19 | set.cpp:238:1:238:1 | s19 | |
+| set.cpp:206:13:206:18 | call to source | set.cpp:206:2:206:4 | ref arg s19 | TAINT |
+| set.cpp:206:13:206:18 | call to source | set.cpp:206:6:206:11 | call to insert | TAINT |
+| set.cpp:207:7:207:9 | s16 | set.cpp:207:7:207:9 | call to unordered_set | |
+| set.cpp:208:7:208:9 | s17 | set.cpp:208:7:208:9 | call to unordered_set | |
+| set.cpp:209:7:209:9 | s18 | set.cpp:209:7:209:9 | call to unordered_set | |
+| set.cpp:210:7:210:9 | s19 | set.cpp:210:7:210:9 | call to unordered_set | |
+| set.cpp:211:2:211:4 | ref arg s16 | set.cpp:213:7:213:9 | s16 | |
+| set.cpp:211:2:211:4 | ref arg s16 | set.cpp:238:1:238:1 | s16 | |
+| set.cpp:211:12:211:14 | ref arg s17 | set.cpp:214:7:214:9 | s17 | |
+| set.cpp:211:12:211:14 | ref arg s17 | set.cpp:238:1:238:1 | s17 | |
+| set.cpp:211:12:211:14 | s17 | set.cpp:211:2:211:4 | ref arg s16 | TAINT |
+| set.cpp:212:2:212:4 | ref arg s18 | set.cpp:215:7:215:9 | s18 | |
+| set.cpp:212:2:212:4 | ref arg s18 | set.cpp:238:1:238:1 | s18 | |
+| set.cpp:212:12:212:14 | ref arg s19 | set.cpp:216:7:216:9 | s19 | |
+| set.cpp:212:12:212:14 | ref arg s19 | set.cpp:238:1:238:1 | s19 | |
+| set.cpp:212:12:212:14 | s19 | set.cpp:212:2:212:4 | ref arg s18 | TAINT |
+| set.cpp:213:7:213:9 | s16 | set.cpp:213:7:213:9 | call to unordered_set | |
+| set.cpp:214:7:214:9 | s17 | set.cpp:214:7:214:9 | call to unordered_set | |
+| set.cpp:215:7:215:9 | s18 | set.cpp:215:7:215:9 | call to unordered_set | |
+| set.cpp:216:7:216:9 | s19 | set.cpp:216:7:216:9 | call to unordered_set | |
+| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:220:2:220:4 | s20 | |
+| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:221:2:221:4 | s20 | |
+| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:222:7:222:9 | s20 | |
+| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:223:7:223:9 | s20 | |
+| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:223:17:223:19 | s20 | |
+| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:224:7:224:9 | s20 | |
+| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:225:2:225:4 | s20 | |
+| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:226:7:226:9 | s20 | |
+| set.cpp:219:29:219:31 | call to unordered_set | set.cpp:238:1:238:1 | s20 | |
+| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:221:2:221:4 | s20 | |
+| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:222:7:222:9 | s20 | |
+| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:223:7:223:9 | s20 | |
+| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:223:17:223:19 | s20 | |
+| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:224:7:224:9 | s20 | |
+| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:225:2:225:4 | s20 | |
+| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:226:7:226:9 | s20 | |
+| set.cpp:220:2:220:4 | ref arg s20 | set.cpp:238:1:238:1 | s20 | |
+| set.cpp:220:13:220:18 | call to source | set.cpp:220:2:220:4 | ref arg s20 | TAINT |
+| set.cpp:220:13:220:18 | call to source | set.cpp:220:6:220:11 | call to insert | TAINT |
+| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:222:7:222:9 | s20 | |
+| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:223:7:223:9 | s20 | |
+| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:223:17:223:19 | s20 | |
+| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:224:7:224:9 | s20 | |
+| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:225:2:225:4 | s20 | |
+| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:226:7:226:9 | s20 | |
+| set.cpp:221:2:221:4 | ref arg s20 | set.cpp:238:1:238:1 | s20 | |
+| set.cpp:221:13:221:18 | call to source | set.cpp:221:2:221:4 | ref arg s20 | TAINT |
+| set.cpp:221:13:221:18 | call to source | set.cpp:221:6:221:11 | call to insert | TAINT |
+| set.cpp:222:7:222:9 | s20 | set.cpp:222:7:222:9 | call to unordered_set | |
+| set.cpp:223:7:223:9 | ref arg s20 | set.cpp:224:7:224:9 | s20 | |
+| set.cpp:223:7:223:9 | ref arg s20 | set.cpp:225:2:225:4 | s20 | |
+| set.cpp:223:7:223:9 | ref arg s20 | set.cpp:226:7:226:9 | s20 | |
+| set.cpp:223:7:223:9 | ref arg s20 | set.cpp:238:1:238:1 | s20 | |
+| set.cpp:223:7:223:9 | s20 | set.cpp:223:11:223:15 | call to erase | TAINT |
+| set.cpp:223:17:223:19 | ref arg s20 | set.cpp:223:7:223:9 | s20 | |
+| set.cpp:223:17:223:19 | ref arg s20 | set.cpp:224:7:224:9 | s20 | |
+| set.cpp:223:17:223:19 | ref arg s20 | set.cpp:225:2:225:4 | s20 | |
+| set.cpp:223:17:223:19 | ref arg s20 | set.cpp:226:7:226:9 | s20 | |
+| set.cpp:223:17:223:19 | ref arg s20 | set.cpp:238:1:238:1 | s20 | |
+| set.cpp:223:17:223:19 | s20 | set.cpp:223:21:223:25 | call to begin | TAINT |
+| set.cpp:224:7:224:9 | s20 | set.cpp:224:7:224:9 | call to unordered_set | |
+| set.cpp:225:2:225:4 | ref arg s20 | set.cpp:226:7:226:9 | s20 | |
+| set.cpp:225:2:225:4 | ref arg s20 | set.cpp:238:1:238:1 | s20 | |
+| set.cpp:226:7:226:9 | s20 | set.cpp:226:7:226:9 | call to unordered_set | |
+| set.cpp:229:29:229:31 | call to unordered_set | set.cpp:230:7:230:9 | s21 | |
+| set.cpp:229:29:229:31 | call to unordered_set | set.cpp:231:7:231:9 | s21 | |
+| set.cpp:229:29:229:31 | call to unordered_set | set.cpp:232:7:232:9 | s21 | |
+| set.cpp:229:29:229:31 | call to unordered_set | set.cpp:233:7:233:9 | s21 | |
+| set.cpp:229:29:229:31 | call to unordered_set | set.cpp:238:1:238:1 | s21 | |
+| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:234:7:234:9 | s22 | |
+| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:234:24:234:26 | s22 | |
+| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:235:7:235:9 | s22 | |
+| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:236:7:236:9 | s22 | |
+| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:236:24:236:26 | s22 | |
+| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:237:7:237:9 | s22 | |
+| set.cpp:229:34:229:36 | call to unordered_set | set.cpp:238:1:238:1 | s22 | |
+| set.cpp:230:7:230:9 | ref arg s21 | set.cpp:231:7:231:9 | s21 | |
+| set.cpp:230:7:230:9 | ref arg s21 | set.cpp:232:7:232:9 | s21 | |
+| set.cpp:230:7:230:9 | ref arg s21 | set.cpp:233:7:233:9 | s21 | |
+| set.cpp:230:7:230:9 | ref arg s21 | set.cpp:238:1:238:1 | s21 | |
+| set.cpp:230:7:230:9 | s21 | set.cpp:230:11:230:17 | call to emplace | TAINT |
+| set.cpp:230:19:230:23 | abc | set.cpp:230:7:230:9 | ref arg s21 | TAINT |
+| set.cpp:230:19:230:23 | abc | set.cpp:230:11:230:17 | call to emplace | TAINT |
+| set.cpp:230:26:230:30 | first | set.cpp:230:7:230:30 | call to iterator | |
+| set.cpp:231:7:231:9 | s21 | set.cpp:231:7:231:9 | call to unordered_set | |
+| set.cpp:232:7:232:9 | ref arg s21 | set.cpp:233:7:233:9 | s21 | |
+| set.cpp:232:7:232:9 | ref arg s21 | set.cpp:238:1:238:1 | s21 | |
+| set.cpp:232:7:232:9 | s21 | set.cpp:232:11:232:17 | call to emplace | TAINT |
+| set.cpp:232:19:232:24 | call to source | set.cpp:232:7:232:9 | ref arg s21 | TAINT |
+| set.cpp:232:19:232:24 | call to source | set.cpp:232:11:232:17 | call to emplace | TAINT |
+| set.cpp:232:29:232:33 | first | set.cpp:232:7:232:33 | call to iterator | |
+| set.cpp:233:7:233:9 | s21 | set.cpp:233:7:233:9 | call to unordered_set | |
+| set.cpp:234:7:234:9 | ref arg s22 | set.cpp:235:7:235:9 | s22 | |
+| set.cpp:234:7:234:9 | ref arg s22 | set.cpp:236:7:236:9 | s22 | |
+| set.cpp:234:7:234:9 | ref arg s22 | set.cpp:236:24:236:26 | s22 | |
+| set.cpp:234:7:234:9 | ref arg s22 | set.cpp:237:7:237:9 | s22 | |
+| set.cpp:234:7:234:9 | ref arg s22 | set.cpp:238:1:238:1 | s22 | |
+| set.cpp:234:7:234:9 | s22 | set.cpp:234:11:234:22 | call to emplace_hint | TAINT |
+| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:234:7:234:9 | s22 | |
+| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:235:7:235:9 | s22 | |
+| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:236:7:236:9 | s22 | |
+| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:236:24:236:26 | s22 | |
+| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:237:7:237:9 | s22 | |
+| set.cpp:234:24:234:26 | ref arg s22 | set.cpp:238:1:238:1 | s22 | |
+| set.cpp:234:24:234:26 | s22 | set.cpp:234:28:234:32 | call to begin | TAINT |
+| set.cpp:234:24:234:34 | call to iterator | set.cpp:234:7:234:9 | ref arg s22 | TAINT |
+| set.cpp:234:24:234:34 | call to iterator | set.cpp:234:11:234:22 | call to emplace_hint | TAINT |
+| set.cpp:234:28:234:32 | call to begin | set.cpp:234:24:234:34 | call to iterator | TAINT |
+| set.cpp:234:37:234:41 | abc | set.cpp:234:7:234:9 | ref arg s22 | TAINT |
+| set.cpp:234:37:234:41 | abc | set.cpp:234:11:234:22 | call to emplace_hint | TAINT |
+| set.cpp:235:7:235:9 | s22 | set.cpp:235:7:235:9 | call to unordered_set | |
+| set.cpp:236:7:236:9 | ref arg s22 | set.cpp:237:7:237:9 | s22 | |
+| set.cpp:236:7:236:9 | ref arg s22 | set.cpp:238:1:238:1 | s22 | |
+| set.cpp:236:7:236:9 | s22 | set.cpp:236:11:236:22 | call to emplace_hint | TAINT |
+| set.cpp:236:24:236:26 | ref arg s22 | set.cpp:236:7:236:9 | s22 | |
+| set.cpp:236:24:236:26 | ref arg s22 | set.cpp:237:7:237:9 | s22 | |
+| set.cpp:236:24:236:26 | ref arg s22 | set.cpp:238:1:238:1 | s22 | |
+| set.cpp:236:24:236:26 | s22 | set.cpp:236:28:236:32 | call to begin | TAINT |
+| set.cpp:236:24:236:34 | call to iterator | set.cpp:236:7:236:9 | ref arg s22 | TAINT |
+| set.cpp:236:24:236:34 | call to iterator | set.cpp:236:11:236:22 | call to emplace_hint | TAINT |
+| set.cpp:236:28:236:32 | call to begin | set.cpp:236:24:236:34 | call to iterator | TAINT |
+| set.cpp:236:37:236:42 | call to source | set.cpp:236:7:236:9 | ref arg s22 | TAINT |
+| set.cpp:236:37:236:42 | call to source | set.cpp:236:11:236:22 | call to emplace_hint | TAINT |
+| set.cpp:237:7:237:9 | s22 | set.cpp:237:7:237:9 | call to unordered_set | |
+| smart_pointer.cpp:11:30:11:50 | call to make_shared | smart_pointer.cpp:12:11:12:11 | p | |
+| smart_pointer.cpp:11:30:11:50 | call to make_shared | smart_pointer.cpp:13:10:13:10 | p | |
+| smart_pointer.cpp:11:52:11:57 | call to source | smart_pointer.cpp:11:30:11:50 | call to make_shared | TAINT |
+| smart_pointer.cpp:12:11:12:11 | p | smart_pointer.cpp:12:10:12:10 | call to operator* | TAINT |
+| smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:18:11:18:11 | p | |
+| smart_pointer.cpp:17:32:17:54 | call to make_shared | smart_pointer.cpp:19:10:19:10 | p | |
+| smart_pointer.cpp:18:11:18:11 | p | smart_pointer.cpp:18:10:18:10 | call to operator* | TAINT |
+| smart_pointer.cpp:23:30:23:50 | call to make_unique | smart_pointer.cpp:24:11:24:11 | p | |
+| smart_pointer.cpp:23:30:23:50 | call to make_unique | smart_pointer.cpp:25:10:25:10 | p | |
+| smart_pointer.cpp:23:52:23:57 | call to source | smart_pointer.cpp:23:30:23:50 | call to make_unique | TAINT |
+| smart_pointer.cpp:24:11:24:11 | p | smart_pointer.cpp:24:10:24:10 | call to operator* | TAINT |
+| smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:30:11:30:11 | p | |
+| smart_pointer.cpp:29:32:29:54 | call to make_unique | smart_pointer.cpp:31:10:31:10 | p | |
+| smart_pointer.cpp:30:11:30:11 | p | smart_pointer.cpp:30:10:30:10 | call to operator* | TAINT |
+| smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:37:6:37:6 | p | |
+| smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:38:10:38:10 | p | |
+| smart_pointer.cpp:35:30:35:50 | call to make_shared | smart_pointer.cpp:39:11:39:11 | p | |
+| smart_pointer.cpp:37:5:37:17 | ... = ... | smart_pointer.cpp:37:5:37:5 | call to operator* [post update] | |
+| smart_pointer.cpp:37:6:37:6 | p | smart_pointer.cpp:37:5:37:5 | call to operator* | TAINT |
+| smart_pointer.cpp:37:10:37:15 | call to source | smart_pointer.cpp:37:5:37:17 | ... = ... | |
+| smart_pointer.cpp:38:10:38:10 | ref arg p | smart_pointer.cpp:39:11:39:11 | p | |
+| smart_pointer.cpp:39:11:39:11 | p | smart_pointer.cpp:39:10:39:10 | call to operator* | TAINT |
+| smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:45:6:45:6 | p | |
+| smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:46:10:46:10 | p | |
+| smart_pointer.cpp:43:29:43:51 | call to unique_ptr | smart_pointer.cpp:47:11:47:11 | p | |
+| smart_pointer.cpp:45:5:45:17 | ... = ... | smart_pointer.cpp:45:5:45:5 | call to operator* [post update] | |
+| smart_pointer.cpp:45:6:45:6 | p | smart_pointer.cpp:45:5:45:5 | call to operator* | TAINT |
+| smart_pointer.cpp:45:10:45:15 | call to source | smart_pointer.cpp:45:5:45:17 | ... = ... | |
+| smart_pointer.cpp:46:10:46:10 | ref arg p | smart_pointer.cpp:47:11:47:11 | p | |
+| smart_pointer.cpp:47:11:47:11 | p | smart_pointer.cpp:47:10:47:10 | call to operator* | TAINT |
+| smart_pointer.cpp:51:30:51:50 | call to make_shared | smart_pointer.cpp:52:10:52:10 | p | |
+| smart_pointer.cpp:51:52:51:57 | call to source | smart_pointer.cpp:51:30:51:50 | call to make_shared | TAINT |
+| smart_pointer.cpp:52:10:52:10 | p | smart_pointer.cpp:52:12:52:14 | call to get | TAINT |
+| smart_pointer.cpp:56:30:56:50 | call to make_unique | smart_pointer.cpp:57:10:57:10 | p | |
+| smart_pointer.cpp:56:52:56:57 | call to source | smart_pointer.cpp:56:30:56:50 | call to make_unique | TAINT |
+| smart_pointer.cpp:57:10:57:10 | p | smart_pointer.cpp:57:12:57:14 | call to get | TAINT |
+| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:66:10:66:10 | p | |
+| smart_pointer.cpp:65:28:65:46 | call to make_unique | smart_pointer.cpp:67:10:67:10 | p | |
+| smart_pointer.cpp:65:48:65:53 | call to source | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT |
+| smart_pointer.cpp:65:58:65:58 | 0 | smart_pointer.cpp:65:28:65:46 | call to make_unique | TAINT |
+| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:39:45:39:51 | source1 | |
+| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:40:11:40:17 | source1 | |
+| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:41:12:41:18 | source1 | |
+| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:42:14:42:20 | source1 | |
+| standalone_iterators.cpp:40:11:40:17 | source1 | standalone_iterators.cpp:40:10:40:10 | call to operator* | TAINT |
+| standalone_iterators.cpp:41:12:41:18 | ref arg source1 | standalone_iterators.cpp:39:45:39:51 | source1 | |
+| standalone_iterators.cpp:41:12:41:18 | ref arg source1 | standalone_iterators.cpp:42:14:42:20 | source1 | |
+| standalone_iterators.cpp:41:12:41:18 | source1 | standalone_iterators.cpp:41:19:41:19 | call to operator++ | |
+| standalone_iterators.cpp:41:19:41:19 | call to operator++ | standalone_iterators.cpp:41:10:41:10 | call to operator* | TAINT |
+| standalone_iterators.cpp:42:12:42:12 | call to operator++ | standalone_iterators.cpp:42:10:42:10 | call to operator* | TAINT |
+| standalone_iterators.cpp:42:14:42:20 | ref arg source1 | standalone_iterators.cpp:39:45:39:51 | source1 | |
+| standalone_iterators.cpp:42:14:42:20 | source1 | standalone_iterators.cpp:42:12:42:12 | call to operator++ | |
+| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:45:39:45:45 | source1 | |
+| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:46:11:46:17 | source1 | |
+| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:47:12:47:18 | source1 | |
+| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:48:14:48:20 | source1 | |
+| standalone_iterators.cpp:46:11:46:17 | source1 | standalone_iterators.cpp:46:10:46:10 | call to operator* | TAINT |
+| standalone_iterators.cpp:47:12:47:18 | ref arg source1 | standalone_iterators.cpp:45:39:45:45 | source1 | |
+| standalone_iterators.cpp:47:12:47:18 | ref arg source1 | standalone_iterators.cpp:48:14:48:20 | source1 | |
+| standalone_iterators.cpp:47:12:47:18 | source1 | standalone_iterators.cpp:47:19:47:19 | call to operator++ | |
+| standalone_iterators.cpp:47:19:47:19 | call to operator++ | standalone_iterators.cpp:47:10:47:10 | call to operator* | TAINT |
+| standalone_iterators.cpp:48:12:48:12 | call to operator++ | standalone_iterators.cpp:48:10:48:10 | call to operator* | TAINT |
+| standalone_iterators.cpp:48:14:48:20 | ref arg source1 | standalone_iterators.cpp:45:39:45:45 | source1 | |
+| standalone_iterators.cpp:48:14:48:20 | source1 | standalone_iterators.cpp:48:12:48:12 | call to operator++ | |
+| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:52:11:52:17 | source1 | |
+| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:53:12:53:18 | source1 | |
+| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:54:14:54:20 | source1 | |
+| standalone_iterators.cpp:53:12:53:18 | ref arg source1 | standalone_iterators.cpp:54:14:54:20 | source1 | |
+| stl.h:241:30:241:40 | call to allocator | stl.h:241:21:241:41 | noexcept(...) | TAINT |
+| stl.h:241:30:241:40 | call to allocator | stl.h:241:21:241:41 | noexcept(...) | TAINT |
+| stl.h:241:30:241:40 | call to allocator | stl.h:241:21:241:41 | noexcept(...) | TAINT |
+| stl.h:241:30:241:40 | call to allocator | stl.h:241:21:241:41 | noexcept(...) | TAINT |
+| stl.h:241:30:241:40 | call to allocator | stl.h:241:21:241:41 | noexcept(...) | TAINT |
+| stl.h:241:53:241:63 | 0 | stl.h:241:46:241:64 | (no string representation) | TAINT |
+| stl.h:334:9:334:9 | Unknown literal | stl.h:334:9:334:9 | constructor init of field first | TAINT |
+| stl.h:334:9:334:9 | Unknown literal | stl.h:334:9:334:9 | constructor init of field second | TAINT |
+| stl.h:334:9:334:9 | constructor init of field first [post-this] | stl.h:334:9:334:9 | constructor init of field second [pre-this] | |
+| stl.h:334:9:334:9 | constructor init of field first [pre-this] | stl.h:334:9:334:9 | constructor init of field second [pre-this] | |
+| stl.h:334:9:334:9 | this | stl.h:334:9:334:9 | constructor init of field first [pre-this] | |
+| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | |
+| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | |
+| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | |
+| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | |
+| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | |
+| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | |
+| stl.h:341:3:341:3 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | |
+| stl.h:341:3:341:6 | this | stl.h:341:36:341:43 | constructor init of field first [pre-this] | |
+| stl.h:341:18:341:18 | x | stl.h:341:18:341:18 | x | |
+| stl.h:341:18:341:18 | x | stl.h:341:18:341:18 | x | |
+| stl.h:341:18:341:18 | x | stl.h:341:18:341:18 | x | |
+| stl.h:341:18:341:18 | x | stl.h:341:18:341:18 | x | |
+| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | |
+| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | |
+| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | |
+| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | |
+| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | |
+| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | |
+| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | |
+| stl.h:341:18:341:18 | x | stl.h:341:42:341:42 | x | |
+| stl.h:341:31:341:31 | y | stl.h:341:31:341:31 | y | |
+| stl.h:341:31:341:31 | y | stl.h:341:31:341:31 | y | |
+| stl.h:341:31:341:31 | y | stl.h:341:31:341:31 | y | |
+| stl.h:341:31:341:31 | y | stl.h:341:31:341:31 | y | |
+| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | |
+| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | |
+| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | |
+| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | |
+| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | |
+| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | |
+| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | |
+| stl.h:341:31:341:31 | y | stl.h:341:53:341:53 | y | |
+| stl.h:341:36:341:43 | call to unknown function | stl.h:341:36:341:43 | constructor init of field first | TAINT |
+| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [post-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:36:341:43 | constructor init of field first [pre-this] | stl.h:341:46:341:54 | constructor init of field second [pre-this] | |
+| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT |
+| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT |
+| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT |
+| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT |
+| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT |
+| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT |
+| stl.h:341:42:341:42 | x | stl.h:341:36:341:43 | constructor init of field first | TAINT |
+| stl.h:341:46:341:54 | call to unknown function | stl.h:341:46:341:54 | constructor init of field second | TAINT |
+| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT |
+| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT |
+| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT |
+| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT |
+| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT |
+| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT |
+| stl.h:341:53:341:53 | y | stl.h:341:46:341:54 | constructor init of field second | TAINT |
+| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | |
+| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | |
+| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | |
+| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | |
+| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | |
+| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | |
+| stl.h:347:109:347:109 | x | stl.h:347:109:347:109 | x | |
+| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | |
+| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | |
+| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | |
+| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | |
+| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | |
+| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | |
+| stl.h:347:109:347:109 | x | stl.h:348:40:348:40 | x | |
+| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | |
+| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | |
+| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | |
+| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | |
+| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | |
+| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | |
+| stl.h:347:117:347:117 | y | stl.h:347:117:347:117 | y | |
+| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | |
+| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | |
+| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | |
+| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | |
+| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | |
+| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | |
+| stl.h:347:117:347:117 | y | stl.h:348:61:348:61 | y | |
+| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT |
+| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT |
+| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT |
+| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT |
+| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT |
+| stl.h:348:10:348:63 | call to pair | stl.h:348:10:348:63 | call to pair | TAINT |
+| stl.h:348:23:348:38 | ref arg call to forward | stl.h:347:109:347:109 | x | |
+| stl.h:348:23:348:38 | ref arg call to forward | stl.h:347:109:347:109 | x | |
+| stl.h:348:23:348:38 | ref arg call to forward | stl.h:347:109:347:109 | x | |
+| stl.h:348:23:348:38 | ref arg call to forward | stl.h:347:109:347:109 | x | |
+| stl.h:348:23:348:38 | ref arg call to forward | stl.h:348:40:348:40 | x [inner post update] | |
+| stl.h:348:23:348:38 | ref arg call to forward | stl.h:348:40:348:40 | x [inner post update] | |
+| stl.h:348:23:348:38 | ref arg call to forward | stl.h:348:40:348:40 | x [inner post update] | |
+| stl.h:348:23:348:38 | ref arg call to forward | stl.h:348:40:348:40 | x [inner post update] | |
+| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | |
+| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | |
+| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | |
+| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | |
+| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | |
+| stl.h:348:40:348:40 | x | stl.h:348:23:348:38 | call to forward | |
+| stl.h:348:44:348:59 | call to forward | stl.h:348:10:348:63 | call to pair | TAINT |
+| stl.h:348:44:348:59 | call to forward | stl.h:348:10:348:63 | call to pair | TAINT |
+| stl.h:348:44:348:59 | ref arg call to forward | stl.h:347:117:347:117 | y | |
+| stl.h:348:44:348:59 | ref arg call to forward | stl.h:347:117:347:117 | y | |
+| stl.h:348:44:348:59 | ref arg call to forward | stl.h:347:117:347:117 | y | |
+| stl.h:348:44:348:59 | ref arg call to forward | stl.h:347:117:347:117 | y | |
+| stl.h:348:44:348:59 | ref arg call to forward | stl.h:348:61:348:61 | y [inner post update] | |
+| stl.h:348:44:348:59 | ref arg call to forward | stl.h:348:61:348:61 | y [inner post update] | |
+| stl.h:348:44:348:59 | ref arg call to forward | stl.h:348:61:348:61 | y [inner post update] | |
+| stl.h:348:44:348:59 | ref arg call to forward | stl.h:348:61:348:61 | y [inner post update] | |
+| stl.h:348:61:348:61 | y | stl.h:348:10:348:63 | call to pair | TAINT |
+| stl.h:348:61:348:61 | y | stl.h:348:10:348:63 | call to pair | TAINT |
+| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | |
+| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | |
+| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | |
+| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | |
+| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | |
+| stl.h:348:61:348:61 | y | stl.h:348:44:348:59 | call to forward | |
+| string.cpp:25:12:25:17 | call to source | string.cpp:29:7:29:7 | a | |
+| string.cpp:26:16:26:20 | 123 | string.cpp:26:16:26:21 | call to basic_string | TAINT |
+| string.cpp:26:16:26:21 | call to basic_string | string.cpp:30:7:30:7 | b | |
+| string.cpp:26:16:26:21 | call to basic_string | string.cpp:32:7:32:7 | b | |
+| string.cpp:27:16:27:21 | call to source | string.cpp:27:16:27:24 | call to basic_string | TAINT |
+| string.cpp:27:16:27:24 | call to basic_string | string.cpp:31:7:31:7 | c | |
+| string.cpp:27:16:27:24 | call to basic_string | string.cpp:33:7:33:7 | c | |
+| string.cpp:32:7:32:7 | b | string.cpp:32:9:32:13 | call to c_str | TAINT |
+| string.cpp:33:7:33:7 | c | string.cpp:33:9:33:13 | call to c_str | TAINT |
+| string.cpp:38:16:38:28 | call to basic_string | string.cpp:39:7:39:11 | path1 | |
+| string.cpp:38:17:38:26 | call to user_input | string.cpp:38:16:38:28 | call to basic_string | TAINT |
+| string.cpp:39:7:39:11 | path1 | string.cpp:39:13:39:17 | call to c_str | TAINT |
+| string.cpp:42:10:42:19 | call to user_input | string.cpp:42:10:42:21 | call to basic_string | TAINT |
+| string.cpp:42:10:42:21 | call to basic_string | string.cpp:42:2:42:21 | ... = ... | |
+| string.cpp:42:10:42:21 | call to basic_string | string.cpp:43:7:43:11 | path2 | |
+| string.cpp:43:7:43:11 | path2 | string.cpp:43:13:43:17 | call to c_str | TAINT |
+| string.cpp:45:15:45:24 | call to user_input | string.cpp:45:15:45:27 | call to basic_string | TAINT |
+| string.cpp:45:15:45:27 | call to basic_string | string.cpp:46:7:46:11 | path3 | |
+| string.cpp:46:7:46:11 | path3 | string.cpp:46:13:46:17 | call to c_str | TAINT |
+| string.cpp:51:19:51:24 | call to source | string.cpp:54:17:54:18 | cs | |
+| string.cpp:51:19:51:24 | call to source | string.cpp:56:7:56:8 | cs | |
+| string.cpp:54:17:54:18 | cs | string.cpp:54:17:54:19 | call to basic_string | TAINT |
+| string.cpp:54:17:54:19 | call to basic_string | string.cpp:57:7:57:8 | ss | |
+| string.cpp:62:19:62:24 | call to source | string.cpp:65:17:65:18 | cs | |
+| string.cpp:65:17:65:18 | cs | string.cpp:65:17:65:19 | call to basic_string | TAINT |
+| string.cpp:65:17:65:19 | call to basic_string | string.cpp:68:7:68:8 | ss | |
+| string.cpp:65:17:65:19 | call to basic_string | string.cpp:71:7:71:8 | ss | |
+| string.cpp:68:7:68:8 | ss | string.cpp:68:10:68:14 | call to c_str | TAINT |
+| string.cpp:68:10:68:14 | call to c_str | string.cpp:68:2:68:16 | ... = ... | |
+| string.cpp:68:10:68:14 | call to c_str | string.cpp:70:7:70:8 | cs | |
+| string.cpp:77:18:77:24 | hello | string.cpp:77:18:77:25 | call to basic_string | TAINT |
+| string.cpp:77:18:77:25 | call to basic_string | string.cpp:82:8:82:9 | s1 | |
+| string.cpp:78:19:78:26 | call to basic_string | string.cpp:83:8:83:9 | s2 | |
+| string.cpp:78:20:78:26 | hello | string.cpp:78:19:78:26 | call to basic_string | TAINT |
+| string.cpp:80:8:80:14 | call to basic_string | string.cpp:80:3:80:14 | ... = ... | |
+| string.cpp:80:8:80:14 | call to basic_string | string.cpp:84:8:84:9 | s3 | |
+| string.cpp:80:8:80:14 | hello | string.cpp:80:8:80:14 | call to basic_string | TAINT |
+| string.cpp:88:18:88:23 | call to source | string.cpp:88:18:88:26 | call to basic_string | TAINT |
+| string.cpp:88:18:88:26 | call to basic_string | string.cpp:93:8:93:9 | s1 | |
+| string.cpp:89:19:89:27 | call to basic_string | string.cpp:94:8:94:9 | s2 | |
+| string.cpp:89:20:89:25 | call to source | string.cpp:89:19:89:27 | call to basic_string | TAINT |
+| string.cpp:91:8:91:13 | call to source | string.cpp:91:8:91:15 | call to basic_string | TAINT |
+| string.cpp:91:8:91:15 | call to basic_string | string.cpp:91:3:91:15 | ... = ... | |
+| string.cpp:91:8:91:15 | call to basic_string | string.cpp:95:8:95:9 | s3 | |
+| string.cpp:99:15:99:16 | call to basic_string | string.cpp:100:20:100:21 | s1 | |
+| string.cpp:99:15:99:16 | call to basic_string | string.cpp:102:8:102:9 | s1 | |
+| string.cpp:99:15:99:16 | call to basic_string | string.cpp:104:8:104:9 | s1 | |
+| string.cpp:100:20:100:21 | s1 | string.cpp:105:8:105:9 | s2 | |
+| string.cpp:102:8:102:9 | s1 | string.cpp:102:3:102:9 | ... = ... | |
+| string.cpp:102:8:102:9 | s1 | string.cpp:106:8:106:9 | s3 | |
+| string.cpp:110:19:110:40 | call to basic_string | string.cpp:114:8:114:9 | s1 | |
+| string.cpp:110:32:110:37 | call to source | string.cpp:110:19:110:40 | call to basic_string | TAINT |
+| string.cpp:112:8:112:28 | call to basic_string | string.cpp:112:3:112:28 | ... = ... | |
+| string.cpp:112:8:112:28 | call to basic_string | string.cpp:115:8:115:9 | s2 | |
+| string.cpp:112:20:112:25 | call to source | string.cpp:112:8:112:28 | call to basic_string | TAINT |
+| string.cpp:120:16:120:21 | call to source | string.cpp:120:16:120:24 | call to basic_string | TAINT |
+| string.cpp:120:16:120:24 | call to basic_string | string.cpp:121:15:121:15 | s | |
+| string.cpp:120:16:120:24 | call to basic_string | string.cpp:125:33:125:33 | s | |
+| string.cpp:120:16:120:24 | call to basic_string | string.cpp:125:50:125:50 | s | |
+| string.cpp:120:16:120:24 | call to basic_string | string.cpp:129:16:129:16 | s | |
+| string.cpp:121:15:121:15 | (__begin) | string.cpp:121:15:121:15 | call to operator* | TAINT |
+| string.cpp:121:15:121:15 | (__begin) | string.cpp:121:15:121:15 | call to operator++ | |
+| string.cpp:121:15:121:15 | (__end) | string.cpp:121:15:121:15 | call to iterator | |
+| string.cpp:121:15:121:15 | (__range) | string.cpp:121:15:121:15 | call to begin | TAINT |
+| string.cpp:121:15:121:15 | (__range) | string.cpp:121:15:121:15 | call to end | TAINT |
+| string.cpp:121:15:121:15 | call to begin | string.cpp:121:15:121:15 | (__begin) | |
+| string.cpp:121:15:121:15 | call to begin | string.cpp:121:15:121:15 | (__begin) | |
+| string.cpp:121:15:121:15 | call to begin | string.cpp:121:15:121:15 | (__begin) | |
+| string.cpp:121:15:121:15 | call to end | string.cpp:121:15:121:15 | (__end) | |
+| string.cpp:121:15:121:15 | call to operator* | string.cpp:122:8:122:8 | c | |
+| string.cpp:121:15:121:15 | ref arg (__begin) | string.cpp:121:15:121:15 | (__begin) | |
+| string.cpp:121:15:121:15 | ref arg (__begin) | string.cpp:121:15:121:15 | (__begin) | |
+| string.cpp:121:15:121:15 | ref arg (__begin) | string.cpp:121:15:121:15 | (__begin) | |
+| string.cpp:121:15:121:15 | ref arg (__range) | string.cpp:121:15:121:15 | (__range) | |
+| string.cpp:121:15:121:15 | s | string.cpp:121:15:121:15 | (__range) | |
+| string.cpp:121:15:121:15 | s | string.cpp:121:15:121:15 | (__range) | |
+| string.cpp:121:15:121:15 | s | string.cpp:121:15:121:15 | call to operator* | TAINT |
+| string.cpp:125:33:125:33 | ref arg s | string.cpp:125:50:125:50 | s | |
+| string.cpp:125:33:125:33 | ref arg s | string.cpp:129:16:129:16 | s | |
+| string.cpp:125:33:125:33 | s | string.cpp:125:35:125:39 | call to begin | TAINT |
+| string.cpp:125:35:125:39 | call to begin | string.cpp:125:44:125:45 | it | |
+| string.cpp:125:35:125:39 | call to begin | string.cpp:125:61:125:62 | it | |
+| string.cpp:125:35:125:39 | call to begin | string.cpp:126:9:126:10 | it | |
+| string.cpp:125:50:125:50 | ref arg s | string.cpp:125:50:125:50 | s | |
+| string.cpp:125:50:125:50 | ref arg s | string.cpp:129:16:129:16 | s | |
+| string.cpp:125:50:125:50 | s | string.cpp:125:52:125:54 | call to end | TAINT |
+| string.cpp:125:61:125:62 | it | string.cpp:125:59:125:59 | call to operator++ | |
+| string.cpp:125:61:125:62 | ref arg it | string.cpp:125:44:125:45 | it | |
+| string.cpp:125:61:125:62 | ref arg it | string.cpp:125:61:125:62 | it | |
+| string.cpp:125:61:125:62 | ref arg it | string.cpp:126:9:126:10 | it | |
+| string.cpp:126:9:126:10 | it | string.cpp:126:8:126:8 | call to operator* | TAINT |
+| string.cpp:129:16:129:16 | (__begin) | string.cpp:129:16:129:16 | call to operator* | TAINT |
+| string.cpp:129:16:129:16 | (__begin) | string.cpp:129:16:129:16 | call to operator++ | |
+| string.cpp:129:16:129:16 | (__end) | string.cpp:129:16:129:16 | call to iterator | |
+| string.cpp:129:16:129:16 | (__range) | string.cpp:129:16:129:16 | call to begin | TAINT |
+| string.cpp:129:16:129:16 | (__range) | string.cpp:129:16:129:16 | call to end | TAINT |
+| string.cpp:129:16:129:16 | call to begin | string.cpp:129:16:129:16 | (__begin) | |
+| string.cpp:129:16:129:16 | call to begin | string.cpp:129:16:129:16 | (__begin) | |
+| string.cpp:129:16:129:16 | call to begin | string.cpp:129:16:129:16 | (__begin) | |
+| string.cpp:129:16:129:16 | call to end | string.cpp:129:16:129:16 | (__end) | |
+| string.cpp:129:16:129:16 | call to operator* | string.cpp:130:8:130:8 | c | |
+| string.cpp:129:16:129:16 | ref arg (__begin) | string.cpp:129:16:129:16 | (__begin) | |
+| string.cpp:129:16:129:16 | ref arg (__begin) | string.cpp:129:16:129:16 | (__begin) | |
+| string.cpp:129:16:129:16 | ref arg (__begin) | string.cpp:129:16:129:16 | (__begin) | |
+| string.cpp:129:16:129:16 | ref arg (__range) | string.cpp:129:16:129:16 | (__range) | |
+| string.cpp:129:16:129:16 | s | string.cpp:129:16:129:16 | (__range) | |
+| string.cpp:129:16:129:16 | s | string.cpp:129:16:129:16 | (__range) | |
+| string.cpp:129:16:129:16 | s | string.cpp:129:16:129:16 | call to operator* | TAINT |
+| string.cpp:133:28:133:33 | call to source | string.cpp:133:28:133:36 | call to basic_string | TAINT |
+| string.cpp:133:28:133:36 | call to basic_string | string.cpp:134:22:134:28 | const_s | |
+| string.cpp:134:22:134:22 | (__begin) | string.cpp:134:22:134:22 | call to operator* | TAINT |
+| string.cpp:134:22:134:22 | (__begin) | string.cpp:134:22:134:22 | call to operator++ | |
+| string.cpp:134:22:134:22 | (__range) | string.cpp:134:22:134:22 | call to begin | TAINT |
+| string.cpp:134:22:134:22 | (__range) | string.cpp:134:22:134:22 | call to end | TAINT |
+| string.cpp:134:22:134:22 | call to begin | string.cpp:134:22:134:22 | (__begin) | |
+| string.cpp:134:22:134:22 | call to begin | string.cpp:134:22:134:22 | (__begin) | |
+| string.cpp:134:22:134:22 | call to begin | string.cpp:134:22:134:22 | (__begin) | |
+| string.cpp:134:22:134:22 | call to end | string.cpp:134:22:134:22 | (__end) | |
+| string.cpp:134:22:134:22 | call to operator* | string.cpp:135:8:135:8 | c | |
+| string.cpp:134:22:134:22 | ref arg (__begin) | string.cpp:134:22:134:22 | (__begin) | |
+| string.cpp:134:22:134:22 | ref arg (__begin) | string.cpp:134:22:134:22 | (__begin) | |
+| string.cpp:134:22:134:22 | ref arg (__begin) | string.cpp:134:22:134:22 | (__begin) | |
+| string.cpp:134:22:134:28 | const_s | string.cpp:134:22:134:22 | (__range) | |
+| string.cpp:134:22:134:28 | const_s | string.cpp:134:22:134:22 | (__range) | |
+| string.cpp:134:22:134:28 | const_s | string.cpp:134:22:134:22 | call to operator* | TAINT |
+| string.cpp:141:18:141:24 | hello | string.cpp:141:18:141:25 | call to basic_string | TAINT |
+| string.cpp:141:18:141:25 | call to basic_string | string.cpp:144:8:144:9 | s1 | |
+| string.cpp:141:18:141:25 | call to basic_string | string.cpp:144:13:144:14 | s1 | |
+| string.cpp:141:18:141:25 | call to basic_string | string.cpp:145:8:145:9 | s1 | |
+| string.cpp:141:18:141:25 | call to basic_string | string.cpp:146:13:146:14 | s1 | |
+| string.cpp:141:18:141:25 | call to basic_string | string.cpp:149:8:149:9 | s1 | |
+| string.cpp:141:18:141:25 | call to basic_string | string.cpp:150:8:150:9 | s1 | |
+| string.cpp:142:18:142:23 | call to source | string.cpp:142:18:142:26 | call to basic_string | TAINT |
+| string.cpp:142:18:142:26 | call to basic_string | string.cpp:145:13:145:14 | s2 | |
+| string.cpp:142:18:142:26 | call to basic_string | string.cpp:146:8:146:9 | s2 | |
+| string.cpp:142:18:142:26 | call to basic_string | string.cpp:147:8:147:9 | s2 | |
+| string.cpp:142:18:142:26 | call to basic_string | string.cpp:147:13:147:14 | s2 | |
+| string.cpp:144:8:144:9 | s1 | string.cpp:144:11:144:11 | call to operator+ | TAINT |
+| string.cpp:144:13:144:14 | s1 | string.cpp:144:11:144:11 | call to operator+ | TAINT |
+| string.cpp:145:8:145:9 | s1 | string.cpp:145:11:145:11 | call to operator+ | TAINT |
+| string.cpp:145:13:145:14 | s2 | string.cpp:145:11:145:11 | call to operator+ | TAINT |
+| string.cpp:146:8:146:9 | s2 | string.cpp:146:11:146:11 | call to operator+ | TAINT |
+| string.cpp:146:13:146:14 | s1 | string.cpp:146:11:146:11 | call to operator+ | TAINT |
+| string.cpp:147:8:147:9 | s2 | string.cpp:147:11:147:11 | call to operator+ | TAINT |
+| string.cpp:147:13:147:14 | s2 | string.cpp:147:11:147:11 | call to operator+ | TAINT |
+| string.cpp:149:8:149:9 | s1 | string.cpp:149:11:149:11 | call to operator+ | TAINT |
+| string.cpp:149:13:149:20 | world | string.cpp:149:11:149:11 | call to operator+ | TAINT |
+| string.cpp:150:8:150:9 | s1 | string.cpp:150:11:150:11 | call to operator+ | TAINT |
+| string.cpp:150:13:150:18 | call to source | string.cpp:150:11:150:11 | call to operator+ | TAINT |
+| string.cpp:154:18:154:22 | abc | string.cpp:154:18:154:23 | call to basic_string | TAINT |
+| string.cpp:154:18:154:23 | call to basic_string | string.cpp:158:8:158:9 | s3 | |
+| string.cpp:154:18:154:23 | call to basic_string | string.cpp:161:8:161:9 | s3 | |
+| string.cpp:154:18:154:23 | call to basic_string | string.cpp:165:8:165:9 | s3 | |
+| string.cpp:154:18:154:23 | call to basic_string | string.cpp:170:8:170:9 | s3 | |
+| string.cpp:154:18:154:23 | call to basic_string | string.cpp:174:8:174:9 | s3 | |
+| string.cpp:155:18:155:23 | call to source | string.cpp:155:18:155:26 | call to basic_string | TAINT |
+| string.cpp:155:18:155:26 | call to basic_string | string.cpp:158:13:158:14 | s4 | |
+| string.cpp:155:18:155:26 | call to basic_string | string.cpp:162:14:162:15 | s4 | |
+| string.cpp:155:18:155:26 | call to basic_string | string.cpp:171:13:171:14 | s4 | |
+| string.cpp:158:8:158:9 | s3 | string.cpp:158:11:158:11 | call to operator+ | TAINT |
+| string.cpp:158:11:158:11 | call to operator+ | string.cpp:158:3:158:14 | ... = ... | |
+| string.cpp:158:11:158:11 | call to operator+ | string.cpp:159:8:159:9 | s5 | |
+| string.cpp:158:13:158:14 | s4 | string.cpp:158:11:158:11 | call to operator+ | TAINT |
+| string.cpp:161:8:161:9 | s3 | string.cpp:161:3:161:9 | ... = ... | |
+| string.cpp:161:8:161:9 | s3 | string.cpp:162:8:162:9 | s6 | |
+| string.cpp:161:8:161:9 | s3 | string.cpp:163:8:163:9 | s6 | |
+| string.cpp:162:8:162:9 | ref arg s6 | string.cpp:163:8:163:9 | s6 | |
+| string.cpp:162:8:162:9 | s6 | string.cpp:162:11:162:11 | call to operator+= | TAINT |
+| string.cpp:162:14:162:15 | s4 | string.cpp:162:8:162:9 | ref arg s6 | TAINT |
+| string.cpp:162:14:162:15 | s4 | string.cpp:162:11:162:11 | call to operator+= | TAINT |
+| string.cpp:165:8:165:9 | s3 | string.cpp:165:3:165:9 | ... = ... | |
+| string.cpp:165:8:165:9 | s3 | string.cpp:166:8:166:9 | s7 | |
+| string.cpp:165:8:165:9 | s3 | string.cpp:167:8:167:9 | s7 | |
+| string.cpp:165:8:165:9 | s3 | string.cpp:168:8:168:9 | s7 | |
+| string.cpp:166:8:166:9 | ref arg s7 | string.cpp:167:8:167:9 | s7 | |
+| string.cpp:166:8:166:9 | ref arg s7 | string.cpp:168:8:168:9 | s7 | |
+| string.cpp:166:8:166:9 | s7 | string.cpp:166:11:166:11 | call to operator+= | TAINT |
+| string.cpp:166:14:166:19 | call to source | string.cpp:166:8:166:9 | ref arg s7 | TAINT |
+| string.cpp:166:14:166:19 | call to source | string.cpp:166:11:166:11 | call to operator+= | TAINT |
+| string.cpp:167:8:167:9 | ref arg s7 | string.cpp:168:8:168:9 | s7 | |
+| string.cpp:167:8:167:9 | s7 | string.cpp:167:11:167:11 | call to operator+= | TAINT |
+| string.cpp:167:14:167:16 | | string.cpp:167:8:167:9 | ref arg s7 | TAINT |
+| string.cpp:167:14:167:16 | | string.cpp:167:11:167:11 | call to operator+= | TAINT |
+| string.cpp:170:8:170:9 | s3 | string.cpp:170:3:170:9 | ... = ... | |
+| string.cpp:170:8:170:9 | s3 | string.cpp:171:3:171:4 | s8 | |
+| string.cpp:170:8:170:9 | s3 | string.cpp:172:8:172:9 | s8 | |
+| string.cpp:171:3:171:4 | ref arg s8 | string.cpp:172:8:172:9 | s8 | |
+| string.cpp:171:3:171:4 | s8 | string.cpp:171:6:171:11 | call to append | TAINT |
+| string.cpp:171:13:171:14 | s4 | string.cpp:171:3:171:4 | ref arg s8 | TAINT |
+| string.cpp:171:13:171:14 | s4 | string.cpp:171:6:171:11 | call to append | TAINT |
+| string.cpp:174:8:174:9 | s3 | string.cpp:174:3:174:9 | ... = ... | |
+| string.cpp:174:8:174:9 | s3 | string.cpp:175:3:175:4 | s9 | |
+| string.cpp:174:8:174:9 | s3 | string.cpp:176:3:176:4 | s9 | |
+| string.cpp:174:8:174:9 | s3 | string.cpp:177:8:177:9 | s9 | |
+| string.cpp:175:3:175:4 | ref arg s9 | string.cpp:176:3:176:4 | s9 | |
+| string.cpp:175:3:175:4 | ref arg s9 | string.cpp:177:8:177:9 | s9 | |
+| string.cpp:175:3:175:4 | s9 | string.cpp:175:6:175:11 | call to append | TAINT |
+| string.cpp:175:13:175:18 | call to source | string.cpp:175:3:175:4 | ref arg s9 | TAINT |
+| string.cpp:175:13:175:18 | call to source | string.cpp:175:6:175:11 | call to append | TAINT |
+| string.cpp:176:3:176:4 | ref arg s9 | string.cpp:177:8:177:9 | s9 | |
+| string.cpp:176:3:176:4 | s9 | string.cpp:176:6:176:11 | call to append | TAINT |
+| string.cpp:176:13:176:15 | | string.cpp:176:3:176:4 | ref arg s9 | TAINT |
+| string.cpp:176:13:176:15 | | string.cpp:176:6:176:11 | call to append | TAINT |
+| string.cpp:181:19:181:23 | abc | string.cpp:181:19:181:24 | call to basic_string | TAINT |
+| string.cpp:181:19:181:24 | call to basic_string | string.cpp:184:3:184:5 | s10 | |
+| string.cpp:181:19:181:24 | call to basic_string | string.cpp:185:8:185:10 | s10 | |
+| string.cpp:182:12:182:26 | call to source | string.cpp:184:17:184:17 | c | |
+| string.cpp:184:3:184:5 | ref arg s10 | string.cpp:185:8:185:10 | s10 | |
+| string.cpp:184:3:184:5 | s10 | string.cpp:184:7:184:12 | call to append | TAINT |
+| string.cpp:184:17:184:17 | c | string.cpp:184:3:184:5 | ref arg s10 | TAINT |
+| string.cpp:184:17:184:17 | c | string.cpp:184:7:184:12 | call to append | TAINT |
+| string.cpp:190:17:190:23 | hello | string.cpp:190:17:190:24 | call to basic_string | TAINT |
+| string.cpp:190:17:190:24 | call to basic_string | string.cpp:196:17:196:18 | s1 | |
+| string.cpp:190:17:190:24 | call to basic_string | string.cpp:205:17:205:18 | s1 | |
+| string.cpp:191:17:191:22 | call to source | string.cpp:191:17:191:25 | call to basic_string | TAINT |
+| string.cpp:191:17:191:25 | call to basic_string | string.cpp:199:17:199:18 | s2 | |
+| string.cpp:192:11:192:25 | call to source | string.cpp:202:21:202:21 | c | |
+| string.cpp:193:14:193:15 | call to basic_string | string.cpp:196:7:196:8 | s3 | |
+| string.cpp:193:14:193:15 | call to basic_string | string.cpp:197:7:197:8 | s3 | |
+| string.cpp:193:18:193:19 | call to basic_string | string.cpp:199:7:199:8 | s4 | |
+| string.cpp:193:18:193:19 | call to basic_string | string.cpp:200:7:200:8 | s4 | |
+| string.cpp:193:22:193:23 | call to basic_string | string.cpp:202:7:202:8 | s5 | |
+| string.cpp:193:22:193:23 | call to basic_string | string.cpp:203:7:203:8 | s5 | |
+| string.cpp:194:17:194:22 | call to source | string.cpp:194:17:194:25 | call to basic_string | TAINT |
+| string.cpp:194:17:194:25 | call to basic_string | string.cpp:205:7:205:8 | s6 | |
+| string.cpp:194:17:194:25 | call to basic_string | string.cpp:206:7:206:8 | s6 | |
+| string.cpp:196:7:196:8 | ref arg s3 | string.cpp:197:7:197:8 | s3 | |
+| string.cpp:196:17:196:18 | s1 | string.cpp:196:7:196:8 | ref arg s3 | TAINT |
+| string.cpp:196:17:196:18 | s1 | string.cpp:196:10:196:15 | call to assign | TAINT |
+| string.cpp:199:7:199:8 | ref arg s4 | string.cpp:200:7:200:8 | s4 | |
+| string.cpp:199:17:199:18 | s2 | string.cpp:199:7:199:8 | ref arg s4 | TAINT |
+| string.cpp:199:17:199:18 | s2 | string.cpp:199:10:199:15 | call to assign | TAINT |
+| string.cpp:202:7:202:8 | ref arg s5 | string.cpp:203:7:203:8 | s5 | |
+| string.cpp:202:21:202:21 | c | string.cpp:202:7:202:8 | ref arg s5 | TAINT |
+| string.cpp:202:21:202:21 | c | string.cpp:202:10:202:15 | call to assign | TAINT |
+| string.cpp:205:7:205:8 | ref arg s6 | string.cpp:206:7:206:8 | s6 | |
+| string.cpp:205:17:205:18 | s1 | string.cpp:205:7:205:8 | ref arg s6 | TAINT |
+| string.cpp:205:17:205:18 | s1 | string.cpp:205:10:205:15 | call to assign | TAINT |
+| string.cpp:210:17:210:23 | hello | string.cpp:210:17:210:24 | call to basic_string | TAINT |
+| string.cpp:210:17:210:24 | call to basic_string | string.cpp:215:7:215:8 | s1 | |
+| string.cpp:210:17:210:24 | call to basic_string | string.cpp:216:20:216:21 | s1 | |
+| string.cpp:210:17:210:24 | call to basic_string | string.cpp:220:20:220:21 | s1 | |
+| string.cpp:210:17:210:24 | call to basic_string | string.cpp:223:7:223:8 | s1 | |
+| string.cpp:210:17:210:24 | call to basic_string | string.cpp:227:7:227:8 | s1 | |
+| string.cpp:211:17:211:22 | call to source | string.cpp:211:17:211:25 | call to basic_string | TAINT |
+| string.cpp:211:17:211:25 | call to basic_string | string.cpp:219:7:219:8 | s2 | |
+| string.cpp:211:17:211:25 | call to basic_string | string.cpp:224:20:224:21 | s2 | |
+| string.cpp:212:11:212:25 | call to source | string.cpp:228:24:228:24 | c | |
+| string.cpp:215:7:215:8 | s1 | string.cpp:215:2:215:8 | ... = ... | |
+| string.cpp:215:7:215:8 | s1 | string.cpp:216:7:216:8 | s3 | |
+| string.cpp:215:7:215:8 | s1 | string.cpp:217:7:217:8 | s3 | |
+| string.cpp:216:7:216:8 | ref arg s3 | string.cpp:217:7:217:8 | s3 | |
+| string.cpp:216:7:216:8 | s3 | string.cpp:216:10:216:15 | call to insert | TAINT |
+| string.cpp:216:20:216:21 | s1 | string.cpp:216:7:216:8 | ref arg s3 | TAINT |
+| string.cpp:216:20:216:21 | s1 | string.cpp:216:10:216:15 | call to insert | TAINT |
+| string.cpp:219:7:219:8 | s2 | string.cpp:219:2:219:8 | ... = ... | |
+| string.cpp:219:7:219:8 | s2 | string.cpp:220:7:220:8 | s4 | |
+| string.cpp:219:7:219:8 | s2 | string.cpp:221:7:221:8 | s4 | |
+| string.cpp:220:7:220:8 | ref arg s4 | string.cpp:221:7:221:8 | s4 | |
+| string.cpp:220:7:220:8 | s4 | string.cpp:220:10:220:15 | call to insert | TAINT |
+| string.cpp:220:20:220:21 | s1 | string.cpp:220:7:220:8 | ref arg s4 | TAINT |
+| string.cpp:220:20:220:21 | s1 | string.cpp:220:10:220:15 | call to insert | TAINT |
+| string.cpp:223:7:223:8 | s1 | string.cpp:223:2:223:8 | ... = ... | |
+| string.cpp:223:7:223:8 | s1 | string.cpp:224:7:224:8 | s5 | |
+| string.cpp:223:7:223:8 | s1 | string.cpp:225:7:225:8 | s5 | |
+| string.cpp:224:7:224:8 | ref arg s5 | string.cpp:225:7:225:8 | s5 | |
+| string.cpp:224:7:224:8 | s5 | string.cpp:224:10:224:15 | call to insert | TAINT |
+| string.cpp:224:20:224:21 | s2 | string.cpp:224:7:224:8 | ref arg s5 | TAINT |
+| string.cpp:224:20:224:21 | s2 | string.cpp:224:10:224:15 | call to insert | TAINT |
+| string.cpp:227:7:227:8 | s1 | string.cpp:227:2:227:8 | ... = ... | |
+| string.cpp:227:7:227:8 | s1 | string.cpp:228:7:228:8 | s6 | |
+| string.cpp:227:7:227:8 | s1 | string.cpp:229:7:229:8 | s6 | |
+| string.cpp:228:7:228:8 | ref arg s6 | string.cpp:229:7:229:8 | s6 | |
+| string.cpp:228:7:228:8 | s6 | string.cpp:228:10:228:15 | call to insert | TAINT |
+| string.cpp:228:24:228:24 | c | string.cpp:228:7:228:8 | ref arg s6 | TAINT |
+| string.cpp:228:24:228:24 | c | string.cpp:228:10:228:15 | call to insert | TAINT |
+| string.cpp:233:17:233:23 | hello | string.cpp:233:17:233:24 | call to basic_string | TAINT |
+| string.cpp:233:17:233:24 | call to basic_string | string.cpp:238:7:238:8 | s1 | |
+| string.cpp:233:17:233:24 | call to basic_string | string.cpp:239:24:239:25 | s1 | |
+| string.cpp:233:17:233:24 | call to basic_string | string.cpp:243:24:243:25 | s1 | |
+| string.cpp:233:17:233:24 | call to basic_string | string.cpp:246:7:246:8 | s1 | |
+| string.cpp:233:17:233:24 | call to basic_string | string.cpp:250:7:250:8 | s1 | |
+| string.cpp:234:17:234:22 | call to source | string.cpp:234:17:234:25 | call to basic_string | TAINT |
+| string.cpp:234:17:234:25 | call to basic_string | string.cpp:242:7:242:8 | s2 | |
+| string.cpp:234:17:234:25 | call to basic_string | string.cpp:247:24:247:25 | s2 | |
+| string.cpp:235:11:235:25 | call to source | string.cpp:251:28:251:28 | c | |
+| string.cpp:238:7:238:8 | s1 | string.cpp:238:2:238:8 | ... = ... | |
+| string.cpp:238:7:238:8 | s1 | string.cpp:239:7:239:8 | s3 | |
+| string.cpp:238:7:238:8 | s1 | string.cpp:240:7:240:8 | s3 | |
+| string.cpp:239:7:239:8 | ref arg s3 | string.cpp:240:7:240:8 | s3 | |
+| string.cpp:239:7:239:8 | s3 | string.cpp:239:10:239:16 | call to replace | TAINT |
+| string.cpp:239:24:239:25 | s1 | string.cpp:239:7:239:8 | ref arg s3 | TAINT |
+| string.cpp:239:24:239:25 | s1 | string.cpp:239:10:239:16 | call to replace | TAINT |
+| string.cpp:242:7:242:8 | s2 | string.cpp:242:2:242:8 | ... = ... | |
+| string.cpp:242:7:242:8 | s2 | string.cpp:243:7:243:8 | s4 | |
+| string.cpp:242:7:242:8 | s2 | string.cpp:244:7:244:8 | s4 | |
+| string.cpp:243:7:243:8 | ref arg s4 | string.cpp:244:7:244:8 | s4 | |
+| string.cpp:243:7:243:8 | s4 | string.cpp:243:10:243:16 | call to replace | TAINT |
+| string.cpp:243:24:243:25 | s1 | string.cpp:243:7:243:8 | ref arg s4 | TAINT |
+| string.cpp:243:24:243:25 | s1 | string.cpp:243:10:243:16 | call to replace | TAINT |
+| string.cpp:246:7:246:8 | s1 | string.cpp:246:2:246:8 | ... = ... | |
+| string.cpp:246:7:246:8 | s1 | string.cpp:247:7:247:8 | s5 | |
+| string.cpp:246:7:246:8 | s1 | string.cpp:248:7:248:8 | s5 | |
+| string.cpp:247:7:247:8 | ref arg s5 | string.cpp:248:7:248:8 | s5 | |
+| string.cpp:247:7:247:8 | s5 | string.cpp:247:10:247:16 | call to replace | TAINT |
+| string.cpp:247:24:247:25 | s2 | string.cpp:247:7:247:8 | ref arg s5 | TAINT |
+| string.cpp:247:24:247:25 | s2 | string.cpp:247:10:247:16 | call to replace | TAINT |
+| string.cpp:250:7:250:8 | s1 | string.cpp:250:2:250:8 | ... = ... | |
+| string.cpp:250:7:250:8 | s1 | string.cpp:251:7:251:8 | s6 | |
+| string.cpp:250:7:250:8 | s1 | string.cpp:252:7:252:8 | s6 | |
+| string.cpp:251:7:251:8 | ref arg s6 | string.cpp:252:7:252:8 | s6 | |
+| string.cpp:251:7:251:8 | s6 | string.cpp:251:10:251:16 | call to replace | TAINT |
+| string.cpp:251:28:251:28 | c | string.cpp:251:7:251:8 | ref arg s6 | TAINT |
+| string.cpp:251:28:251:28 | c | string.cpp:251:10:251:16 | call to replace | TAINT |
+| string.cpp:256:17:256:20 | {...} | string.cpp:261:10:261:11 | b1 | |
+| string.cpp:256:17:256:20 | {...} | string.cpp:262:7:262:8 | b1 | |
+| string.cpp:256:19:256:19 | 0 | string.cpp:256:17:256:20 | {...} | TAINT |
+| string.cpp:257:17:257:20 | {...} | string.cpp:264:10:264:11 | b2 | |
+| string.cpp:257:17:257:20 | {...} | string.cpp:265:7:265:8 | b2 | |
+| string.cpp:257:19:257:19 | 0 | string.cpp:257:17:257:20 | {...} | TAINT |
+| string.cpp:258:17:258:23 | hello | string.cpp:258:17:258:24 | call to basic_string | TAINT |
+| string.cpp:258:17:258:24 | call to basic_string | string.cpp:261:2:261:3 | s1 | |
+| string.cpp:258:17:258:24 | call to basic_string | string.cpp:261:14:261:15 | s1 | |
+| string.cpp:258:17:258:24 | call to basic_string | string.cpp:264:14:264:15 | s1 | |
+| string.cpp:259:17:259:22 | call to source | string.cpp:259:17:259:25 | call to basic_string | TAINT |
+| string.cpp:259:17:259:25 | call to basic_string | string.cpp:264:2:264:3 | s2 | |
+| string.cpp:261:2:261:3 | s1 | string.cpp:261:10:261:11 | ref arg b1 | TAINT |
+| string.cpp:261:10:261:11 | ref arg b1 | string.cpp:262:7:262:8 | b1 | |
+| string.cpp:264:2:264:3 | s2 | string.cpp:264:10:264:11 | ref arg b2 | TAINT |
+| string.cpp:264:10:264:11 | ref arg b2 | string.cpp:265:7:265:8 | b2 | |
+| string.cpp:269:17:269:23 | hello | string.cpp:269:17:269:24 | call to basic_string | TAINT |
+| string.cpp:269:17:269:24 | call to basic_string | string.cpp:274:7:274:8 | s1 | |
+| string.cpp:269:17:269:24 | call to basic_string | string.cpp:279:2:279:3 | s1 | |
+| string.cpp:269:17:269:24 | call to basic_string | string.cpp:282:7:282:8 | s1 | |
+| string.cpp:270:17:270:22 | call to source | string.cpp:270:17:270:25 | call to basic_string | TAINT |
+| string.cpp:270:17:270:25 | call to basic_string | string.cpp:275:7:275:8 | s2 | |
+| string.cpp:270:17:270:25 | call to basic_string | string.cpp:279:10:279:11 | s2 | |
+| string.cpp:270:17:270:25 | call to basic_string | string.cpp:283:7:283:8 | s2 | |
+| string.cpp:271:17:271:23 | world | string.cpp:271:17:271:24 | call to basic_string | TAINT |
+| string.cpp:271:17:271:24 | call to basic_string | string.cpp:276:7:276:8 | s3 | |
+| string.cpp:271:17:271:24 | call to basic_string | string.cpp:280:10:280:11 | s3 | |
+| string.cpp:271:17:271:24 | call to basic_string | string.cpp:284:7:284:8 | s3 | |
+| string.cpp:272:17:272:22 | call to source | string.cpp:272:17:272:25 | call to basic_string | TAINT |
+| string.cpp:272:17:272:25 | call to basic_string | string.cpp:277:7:277:8 | s4 | |
+| string.cpp:272:17:272:25 | call to basic_string | string.cpp:280:2:280:3 | s4 | |
+| string.cpp:272:17:272:25 | call to basic_string | string.cpp:285:7:285:8 | s4 | |
+| string.cpp:279:2:279:3 | ref arg s1 | string.cpp:282:7:282:8 | s1 | |
+| string.cpp:279:2:279:3 | s1 | string.cpp:279:10:279:11 | ref arg s2 | TAINT |
+| string.cpp:279:10:279:11 | ref arg s2 | string.cpp:283:7:283:8 | s2 | |
+| string.cpp:279:10:279:11 | s2 | string.cpp:279:2:279:3 | ref arg s1 | TAINT |
+| string.cpp:280:2:280:3 | ref arg s4 | string.cpp:285:7:285:8 | s4 | |
+| string.cpp:280:2:280:3 | s4 | string.cpp:280:10:280:11 | ref arg s3 | TAINT |
+| string.cpp:280:10:280:11 | ref arg s3 | string.cpp:284:7:284:8 | s3 | |
+| string.cpp:280:10:280:11 | s3 | string.cpp:280:2:280:3 | ref arg s4 | TAINT |
+| string.cpp:289:17:289:22 | call to source | string.cpp:289:17:289:25 | call to basic_string | TAINT |
+| string.cpp:289:17:289:25 | call to basic_string | string.cpp:293:7:293:8 | s1 | |
+| string.cpp:289:17:289:25 | call to basic_string | string.cpp:297:2:297:3 | s1 | |
+| string.cpp:289:17:289:25 | call to basic_string | string.cpp:301:7:301:8 | s1 | |
+| string.cpp:290:17:290:22 | call to source | string.cpp:290:17:290:25 | call to basic_string | TAINT |
+| string.cpp:290:17:290:25 | call to basic_string | string.cpp:294:7:294:8 | s2 | |
+| string.cpp:291:17:291:22 | call to source | string.cpp:291:17:291:25 | call to basic_string | TAINT |
+| string.cpp:291:17:291:25 | call to basic_string | string.cpp:295:7:295:8 | s3 | |
+| string.cpp:291:17:291:25 | call to basic_string | string.cpp:299:7:299:8 | s3 | |
+| string.cpp:297:2:297:3 | ref arg s1 | string.cpp:301:7:301:8 | s1 | |
+| string.cpp:298:7:298:8 | | string.cpp:298:7:298:8 | call to basic_string | TAINT |
+| string.cpp:298:7:298:8 | call to basic_string | string.cpp:298:2:298:8 | ... = ... | |
+| string.cpp:298:7:298:8 | call to basic_string | string.cpp:302:7:302:8 | s2 | |
+| string.cpp:299:7:299:8 | s3 | string.cpp:299:2:299:8 | ... = ... | |
+| string.cpp:299:7:299:8 | s3 | string.cpp:303:7:303:8 | s3 | |
+| string.cpp:308:16:308:20 | 123 | string.cpp:308:16:308:21 | call to basic_string | TAINT |
+| string.cpp:308:16:308:21 | call to basic_string | string.cpp:311:7:311:7 | a | |
+| string.cpp:308:16:308:21 | call to basic_string | string.cpp:313:7:313:7 | a | |
+| string.cpp:309:16:309:21 | call to source | string.cpp:309:16:309:24 | call to basic_string | TAINT |
+| string.cpp:309:16:309:24 | call to basic_string | string.cpp:312:7:312:7 | b | |
+| string.cpp:309:16:309:24 | call to basic_string | string.cpp:314:7:314:7 | b | |
+| string.cpp:311:7:311:7 | a | string.cpp:311:9:311:12 | call to data | TAINT |
+| string.cpp:311:7:311:7 | ref arg a | string.cpp:313:7:313:7 | a | |
+| string.cpp:312:7:312:7 | b | string.cpp:312:9:312:12 | call to data | TAINT |
+| string.cpp:312:7:312:7 | ref arg b | string.cpp:314:7:314:7 | b | |
+| string.cpp:319:16:319:20 | 123 | string.cpp:319:16:319:21 | call to basic_string | TAINT |
+| string.cpp:319:16:319:21 | call to basic_string | string.cpp:322:7:322:7 | a | |
+| string.cpp:319:16:319:21 | call to basic_string | string.cpp:322:19:322:19 | a | |
+| string.cpp:320:16:320:21 | call to source | string.cpp:320:16:320:24 | call to basic_string | TAINT |
+| string.cpp:320:16:320:24 | call to basic_string | string.cpp:323:7:323:7 | b | |
+| string.cpp:320:16:320:24 | call to basic_string | string.cpp:323:19:323:19 | b | |
+| string.cpp:322:7:322:7 | a | string.cpp:322:9:322:14 | call to substr | TAINT |
+| string.cpp:323:7:323:7 | b | string.cpp:323:9:323:14 | call to substr | TAINT |
+| string.cpp:328:16:328:20 | 123 | string.cpp:328:16:328:21 | call to basic_string | TAINT |
+| string.cpp:328:16:328:21 | call to basic_string | string.cpp:332:7:332:7 | a | |
+| string.cpp:328:16:328:21 | call to basic_string | string.cpp:336:2:336:2 | a | |
+| string.cpp:328:16:328:21 | call to basic_string | string.cpp:338:9:338:9 | a | |
+| string.cpp:328:16:328:21 | call to basic_string | string.cpp:340:7:340:7 | a | |
+| string.cpp:329:16:329:20 | 123 | string.cpp:329:16:329:21 | call to basic_string | TAINT |
+| string.cpp:329:16:329:21 | call to basic_string | string.cpp:333:7:333:7 | b | |
+| string.cpp:329:16:329:21 | call to basic_string | string.cpp:337:2:337:2 | b | |
+| string.cpp:329:16:329:21 | call to basic_string | string.cpp:341:7:341:7 | b | |
+| string.cpp:330:16:330:20 | 123 | string.cpp:330:16:330:21 | call to basic_string | TAINT |
+| string.cpp:330:16:330:21 | call to basic_string | string.cpp:334:7:334:7 | c | |
+| string.cpp:330:16:330:21 | call to basic_string | string.cpp:338:2:338:2 | c | |
+| string.cpp:330:16:330:21 | call to basic_string | string.cpp:342:7:342:7 | c | |
+| string.cpp:336:2:336:2 | a | string.cpp:336:3:336:3 | call to operator[] | TAINT |
+| string.cpp:336:2:336:2 | ref arg a | string.cpp:338:9:338:9 | a | |
+| string.cpp:336:2:336:2 | ref arg a | string.cpp:340:7:340:7 | a | |
+| string.cpp:336:2:336:25 | ... = ... | string.cpp:336:3:336:3 | call to operator[] [post update] | |
+| string.cpp:336:3:336:3 | call to operator[] [post update] | string.cpp:336:2:336:2 | ref arg a | TAINT |
+| string.cpp:336:9:336:23 | call to source | string.cpp:336:2:336:25 | ... = ... | |
+| string.cpp:337:2:337:2 | b | string.cpp:337:4:337:5 | call to at | TAINT |
+| string.cpp:337:2:337:2 | ref arg b | string.cpp:341:7:341:7 | b | |
+| string.cpp:337:2:337:28 | ... = ... | string.cpp:337:4:337:5 | call to at [post update] | |
+| string.cpp:337:4:337:5 | call to at [post update] | string.cpp:337:2:337:2 | ref arg b | TAINT |
+| string.cpp:337:12:337:26 | call to source | string.cpp:337:2:337:28 | ... = ... | |
+| string.cpp:338:2:338:2 | c | string.cpp:338:3:338:3 | call to operator[] | TAINT |
+| string.cpp:338:2:338:2 | ref arg c | string.cpp:342:7:342:7 | c | |
+| string.cpp:338:2:338:12 | ... = ... | string.cpp:338:3:338:3 | call to operator[] [post update] | |
+| string.cpp:338:3:338:3 | call to operator[] [post update] | string.cpp:338:2:338:2 | ref arg c | TAINT |
+| string.cpp:338:9:338:9 | a | string.cpp:338:10:338:10 | call to operator[] | TAINT |
+| string.cpp:338:9:338:9 | ref arg a | string.cpp:340:7:340:7 | a | |
+| string.cpp:338:10:338:10 | call to operator[] | string.cpp:338:2:338:12 | ... = ... | |
+| string.cpp:347:18:347:22 | 123 | string.cpp:347:18:347:23 | call to basic_string | TAINT |
+| string.cpp:347:18:347:23 | call to basic_string | string.cpp:349:2:349:4 | str | |
+| string.cpp:347:18:347:23 | call to basic_string | string.cpp:350:7:350:9 | str | |
+| string.cpp:347:18:347:23 | call to basic_string | string.cpp:351:7:351:9 | str | |
+| string.cpp:349:2:349:4 | ref arg str | string.cpp:350:7:350:9 | str | |
+| string.cpp:349:2:349:4 | ref arg str | string.cpp:351:7:351:9 | str | |
+| string.cpp:349:2:349:4 | str | string.cpp:349:6:349:9 | call to data | TAINT |
+| string.cpp:349:2:349:14 | access to array [post update] | string.cpp:349:6:349:9 | call to data [inner post update] | |
+| string.cpp:349:2:349:34 | ... = ... | string.cpp:349:2:349:14 | access to array [post update] | |
+| string.cpp:349:6:349:9 | call to data | string.cpp:349:2:349:14 | access to array | TAINT |
+| string.cpp:349:6:349:9 | call to data [inner post update] | string.cpp:349:2:349:4 | ref arg str | TAINT |
+| string.cpp:349:13:349:13 | 1 | string.cpp:349:2:349:14 | access to array | TAINT |
+| string.cpp:349:18:349:32 | call to source | string.cpp:349:2:349:34 | ... = ... | |
+| string.cpp:351:7:351:9 | str | string.cpp:351:11:351:14 | call to data | TAINT |
+| string.cpp:357:18:357:24 | hello | string.cpp:357:18:357:25 | call to basic_string | TAINT |
+| string.cpp:357:18:357:25 | call to basic_string | string.cpp:362:8:362:9 | s1 | |
+| string.cpp:357:18:357:25 | call to basic_string | string.cpp:363:8:363:9 | s1 | |
+| string.cpp:357:18:357:25 | call to basic_string | string.cpp:364:8:364:9 | s1 | |
+| string.cpp:358:18:358:23 | call to source | string.cpp:358:18:358:26 | call to basic_string | TAINT |
+| string.cpp:358:18:358:26 | call to basic_string | string.cpp:363:18:363:19 | s2 | |
+| string.cpp:358:18:358:26 | call to basic_string | string.cpp:363:30:363:31 | s2 | |
+| string.cpp:359:18:359:24 | hello | string.cpp:359:18:359:25 | call to basic_string | TAINT |
+| string.cpp:359:18:359:25 | call to basic_string | string.cpp:366:8:366:9 | s3 | |
+| string.cpp:359:18:359:25 | call to basic_string | string.cpp:367:8:367:9 | s3 | |
+| string.cpp:359:18:359:25 | call to basic_string | string.cpp:368:8:368:9 | s3 | |
+| string.cpp:360:18:360:24 | world | string.cpp:360:18:360:25 | call to basic_string | TAINT |
+| string.cpp:360:18:360:25 | call to basic_string | string.cpp:367:18:367:19 | s4 | |
+| string.cpp:360:18:360:25 | call to basic_string | string.cpp:367:30:367:31 | s4 | |
+| string.cpp:363:8:363:9 | ref arg s1 | string.cpp:364:8:364:9 | s1 | |
+| string.cpp:363:8:363:9 | s1 | string.cpp:363:11:363:16 | call to append | TAINT |
+| string.cpp:363:18:363:19 | ref arg s2 | string.cpp:363:30:363:31 | s2 | |
+| string.cpp:363:18:363:19 | s2 | string.cpp:363:21:363:25 | call to begin | TAINT |
+| string.cpp:363:21:363:25 | call to begin | string.cpp:363:8:363:9 | ref arg s1 | TAINT |
+| string.cpp:363:21:363:25 | call to begin | string.cpp:363:11:363:16 | call to append | TAINT |
+| string.cpp:363:30:363:31 | s2 | string.cpp:363:33:363:35 | call to end | TAINT |
+| string.cpp:363:33:363:35 | call to end | string.cpp:363:8:363:9 | ref arg s1 | TAINT |
+| string.cpp:363:33:363:35 | call to end | string.cpp:363:11:363:16 | call to append | TAINT |
+| string.cpp:367:8:367:9 | ref arg s3 | string.cpp:368:8:368:9 | s3 | |
+| string.cpp:367:8:367:9 | s3 | string.cpp:367:11:367:16 | call to append | TAINT |
+| string.cpp:367:18:367:19 | ref arg s4 | string.cpp:367:30:367:31 | s4 | |
+| string.cpp:367:18:367:19 | s4 | string.cpp:367:21:367:25 | call to begin | TAINT |
+| string.cpp:367:21:367:25 | call to begin | string.cpp:367:8:367:9 | ref arg s3 | TAINT |
+| string.cpp:367:21:367:25 | call to begin | string.cpp:367:11:367:16 | call to append | TAINT |
+| string.cpp:367:30:367:31 | s4 | string.cpp:367:33:367:35 | call to end | TAINT |
+| string.cpp:367:33:367:35 | call to end | string.cpp:367:8:367:9 | ref arg s3 | TAINT |
+| string.cpp:367:33:367:35 | call to end | string.cpp:367:11:367:16 | call to append | TAINT |
+| string.cpp:373:18:373:24 | hello | string.cpp:373:18:373:25 | call to basic_string | TAINT |
+| string.cpp:373:18:373:25 | call to basic_string | string.cpp:376:28:376:29 | s1 | |
+| string.cpp:374:18:374:23 | call to source | string.cpp:374:18:374:26 | call to basic_string | TAINT |
+| string.cpp:374:18:374:26 | call to basic_string | string.cpp:380:28:380:29 | s2 | |
+| string.cpp:376:28:376:29 | s1 | string.cpp:376:31:376:35 | call to begin | TAINT |
+| string.cpp:376:31:376:35 | call to begin | string.cpp:378:9:378:13 | iter1 | |
+| string.cpp:376:31:376:35 | call to begin | string.cpp:379:8:379:12 | iter1 | |
+| string.cpp:378:9:378:13 | iter1 | string.cpp:378:8:378:8 | call to operator* | TAINT |
+| string.cpp:379:8:379:12 | iter1 | string.cpp:379:13:379:13 | call to operator[] | TAINT |
+| string.cpp:380:28:380:29 | s2 | string.cpp:380:31:380:35 | call to begin | TAINT |
+| string.cpp:380:31:380:35 | call to begin | string.cpp:382:9:382:13 | iter2 | |
+| string.cpp:380:31:380:35 | call to begin | string.cpp:383:8:383:12 | iter2 | |
+| string.cpp:382:9:382:13 | iter2 | string.cpp:382:8:382:8 | call to operator* | TAINT |
+| string.cpp:383:8:383:12 | iter2 | string.cpp:383:13:383:13 | call to operator[] | TAINT |
+| string.cpp:388:18:388:24 | hello | string.cpp:388:18:388:25 | call to basic_string | TAINT |
+| string.cpp:388:18:388:25 | call to basic_string | string.cpp:391:25:391:26 | s1 | |
+| string.cpp:389:18:389:23 | call to source | string.cpp:389:18:389:26 | call to basic_string | TAINT |
+| string.cpp:389:18:389:26 | call to basic_string | string.cpp:393:25:393:26 | s2 | |
+| string.cpp:389:18:389:26 | call to basic_string | string.cpp:413:8:413:9 | s2 | |
+| string.cpp:391:25:391:26 | s1 | string.cpp:391:28:391:32 | call to begin | TAINT |
+| string.cpp:393:25:393:26 | ref arg s2 | string.cpp:413:8:413:9 | s2 | |
+| string.cpp:393:25:393:26 | s2 | string.cpp:393:28:393:32 | call to begin | TAINT |
+| string.cpp:393:28:393:32 | call to begin | string.cpp:396:10:396:11 | i2 | |
+| string.cpp:393:28:393:32 | call to begin | string.cpp:397:10:397:11 | i2 | |
+| string.cpp:393:28:393:32 | call to begin | string.cpp:398:8:398:9 | i2 | |
+| string.cpp:393:28:393:32 | call to begin | string.cpp:400:8:400:9 | i2 | |
+| string.cpp:393:28:393:32 | call to begin | string.cpp:402:8:402:9 | i2 | |
+| string.cpp:393:28:393:32 | call to begin | string.cpp:405:8:405:9 | i2 | |
+| string.cpp:393:28:393:32 | call to begin | string.cpp:408:8:408:9 | i2 | |
+| string.cpp:393:28:393:32 | call to begin | string.cpp:410:8:410:9 | i2 | |
+| string.cpp:393:28:393:32 | call to begin | string.cpp:417:9:417:10 | i2 | |
+| string.cpp:393:28:393:32 | call to begin | string.cpp:420:9:420:10 | i2 | |
+| string.cpp:396:10:396:11 | i2 | string.cpp:396:12:396:12 | call to operator+ | TAINT |
+| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:397:10:397:11 | i2 | |
+| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:398:8:398:9 | i2 | |
+| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:400:8:400:9 | i2 | |
+| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:402:8:402:9 | i2 | |
+| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:405:8:405:9 | i2 | |
+| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:408:8:408:9 | i2 | |
+| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:410:8:410:9 | i2 | |
+| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:417:9:417:10 | i2 | |
+| string.cpp:396:10:396:11 | ref arg i2 | string.cpp:420:9:420:10 | i2 | |
+| string.cpp:396:12:396:12 | call to operator+ | string.cpp:396:8:396:8 | call to operator* | TAINT |
+| string.cpp:397:10:397:11 | i2 | string.cpp:397:12:397:12 | call to operator- | TAINT |
+| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:398:8:398:9 | i2 | |
+| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:400:8:400:9 | i2 | |
+| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:402:8:402:9 | i2 | |
+| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:405:8:405:9 | i2 | |
+| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:408:8:408:9 | i2 | |
+| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:410:8:410:9 | i2 | |
+| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:417:9:417:10 | i2 | |
+| string.cpp:397:10:397:11 | ref arg i2 | string.cpp:420:9:420:10 | i2 | |
+| string.cpp:397:12:397:12 | call to operator- | string.cpp:397:8:397:8 | call to operator* | TAINT |
+| string.cpp:398:8:398:9 | i2 | string.cpp:398:3:398:9 | ... = ... | |
+| string.cpp:398:8:398:9 | i2 | string.cpp:399:12:399:13 | i3 | |
+| string.cpp:399:10:399:10 | call to operator++ | string.cpp:399:8:399:8 | call to operator* | TAINT |
+| string.cpp:399:12:399:13 | i3 | string.cpp:399:10:399:10 | call to operator++ | |
+| string.cpp:400:8:400:9 | i2 | string.cpp:400:3:400:9 | ... = ... | |
+| string.cpp:400:8:400:9 | i2 | string.cpp:401:12:401:13 | i4 | |
+| string.cpp:401:10:401:10 | call to operator-- | string.cpp:401:8:401:8 | call to operator* | TAINT |
+| string.cpp:401:12:401:13 | i4 | string.cpp:401:10:401:10 | call to operator-- | |
+| string.cpp:402:8:402:9 | i2 | string.cpp:402:3:402:9 | ... = ... | |
+| string.cpp:402:8:402:9 | i2 | string.cpp:403:3:403:4 | i5 | |
+| string.cpp:402:8:402:9 | i2 | string.cpp:404:9:404:10 | i5 | |
+| string.cpp:403:3:403:4 | i5 | string.cpp:403:5:403:5 | call to operator++ | |
+| string.cpp:403:3:403:4 | ref arg i5 | string.cpp:404:9:404:10 | i5 | |
+| string.cpp:404:9:404:10 | i5 | string.cpp:404:8:404:8 | call to operator* | TAINT |
+| string.cpp:405:8:405:9 | i2 | string.cpp:405:3:405:9 | ... = ... | |
+| string.cpp:405:8:405:9 | i2 | string.cpp:406:3:406:4 | i6 | |
+| string.cpp:405:8:405:9 | i2 | string.cpp:407:9:407:10 | i6 | |
+| string.cpp:406:3:406:4 | i6 | string.cpp:406:5:406:5 | call to operator-- | |
+| string.cpp:406:3:406:4 | ref arg i6 | string.cpp:407:9:407:10 | i6 | |
+| string.cpp:407:9:407:10 | i6 | string.cpp:407:8:407:8 | call to operator* | TAINT |
+| string.cpp:408:8:408:9 | i2 | string.cpp:408:3:408:9 | ... = ... | |
+| string.cpp:408:8:408:9 | i2 | string.cpp:409:10:409:11 | i7 | |
+| string.cpp:409:10:409:11 | i7 | string.cpp:409:12:409:12 | call to operator+= | |
+| string.cpp:409:12:409:12 | call to operator+= | string.cpp:409:8:409:8 | call to operator* | TAINT |
+| string.cpp:409:14:409:14 | 1 | string.cpp:409:12:409:12 | call to operator+= | |
+| string.cpp:410:8:410:9 | i2 | string.cpp:410:3:410:9 | ... = ... | |
+| string.cpp:410:8:410:9 | i2 | string.cpp:411:10:411:11 | i8 | |
+| string.cpp:411:10:411:11 | i8 | string.cpp:411:12:411:12 | call to operator-= | |
+| string.cpp:411:12:411:12 | call to operator-= | string.cpp:411:8:411:8 | call to operator* | TAINT |
+| string.cpp:411:14:411:14 | 1 | string.cpp:411:12:411:12 | call to operator-= | |
+| string.cpp:413:8:413:9 | s2 | string.cpp:413:11:413:13 | call to end | TAINT |
+| string.cpp:413:11:413:13 | call to end | string.cpp:413:3:413:15 | ... = ... | |
+| string.cpp:413:11:413:13 | call to end | string.cpp:414:5:414:6 | i9 | |
+| string.cpp:413:11:413:13 | call to end | string.cpp:415:9:415:10 | i9 | |
+| string.cpp:414:5:414:6 | i9 | string.cpp:414:3:414:3 | call to operator-- | |
+| string.cpp:414:5:414:6 | ref arg i9 | string.cpp:415:9:415:10 | i9 | |
+| string.cpp:415:9:415:10 | i9 | string.cpp:415:8:415:8 | call to operator* | TAINT |
+| string.cpp:417:9:417:10 | i2 | string.cpp:417:3:417:10 | ... = ... | |
+| string.cpp:417:9:417:10 | i2 | string.cpp:418:10:418:12 | i10 | |
+| string.cpp:417:9:417:10 | i2 | string.cpp:419:8:419:10 | i10 | |
+| string.cpp:418:10:418:12 | i10 | string.cpp:418:13:418:13 | call to operator++ | |
+| string.cpp:418:10:418:12 | ref arg i10 | string.cpp:419:8:419:10 | i10 | |
+| string.cpp:418:13:418:13 | call to operator++ | string.cpp:418:8:418:8 | call to operator* | TAINT |
+| string.cpp:419:8:419:10 | i10 | string.cpp:419:8:419:10 | call to iterator | |
+| string.cpp:420:9:420:10 | i2 | string.cpp:420:3:420:10 | ... = ... | |
+| string.cpp:420:9:420:10 | i2 | string.cpp:421:10:421:12 | i11 | |
+| string.cpp:420:9:420:10 | i2 | string.cpp:422:8:422:10 | i11 | |
+| string.cpp:421:10:421:12 | i11 | string.cpp:421:13:421:13 | call to operator-- | |
+| string.cpp:421:10:421:12 | ref arg i11 | string.cpp:422:8:422:10 | i11 | |
+| string.cpp:421:13:421:13 | call to operator-- | string.cpp:421:8:421:8 | call to operator* | TAINT |
+| string.cpp:422:8:422:10 | i11 | string.cpp:422:8:422:10 | call to iterator | |
+| string.cpp:428:17:428:20 | aa | string.cpp:428:17:428:21 | call to basic_string | TAINT |
+| string.cpp:428:17:428:21 | call to basic_string | string.cpp:433:7:433:8 | s1 | |
+| string.cpp:428:17:428:21 | call to basic_string | string.cpp:434:7:434:8 | s1 | |
+| string.cpp:429:17:429:20 | bb | string.cpp:429:17:429:21 | call to basic_string | TAINT |
+| string.cpp:429:17:429:21 | call to basic_string | string.cpp:436:7:436:8 | s2 | |
+| string.cpp:429:17:429:21 | call to basic_string | string.cpp:437:7:437:8 | s2 | |
+| string.cpp:430:14:430:17 | cc | string.cpp:433:20:433:22 | cs1 | |
+| string.cpp:431:14:431:19 | call to source | string.cpp:436:20:436:22 | cs2 | |
+| string.cpp:433:7:433:8 | ref arg s1 | string.cpp:434:7:434:8 | s1 | |
+| string.cpp:433:7:433:8 | s1 | string.cpp:433:10:433:15 | call to insert | TAINT |
+| string.cpp:433:20:433:22 | cs1 | string.cpp:433:7:433:8 | ref arg s1 | TAINT |
+| string.cpp:433:20:433:22 | cs1 | string.cpp:433:10:433:15 | call to insert | TAINT |
+| string.cpp:436:7:436:8 | ref arg s2 | string.cpp:437:7:437:8 | s2 | |
+| string.cpp:436:7:436:8 | s2 | string.cpp:436:10:436:15 | call to insert | TAINT |
+| string.cpp:436:20:436:22 | cs2 | string.cpp:436:7:436:8 | ref arg s2 | TAINT |
+| string.cpp:436:20:436:22 | cs2 | string.cpp:436:10:436:15 | call to insert | TAINT |
+| string.cpp:443:17:443:20 | aa | string.cpp:443:17:443:21 | call to basic_string | TAINT |
+| string.cpp:443:17:443:21 | call to basic_string | string.cpp:446:8:446:8 | a | |
+| string.cpp:443:17:443:21 | call to basic_string | string.cpp:446:17:446:17 | a | |
+| string.cpp:443:17:443:21 | call to basic_string | string.cpp:447:8:447:8 | a | |
+| string.cpp:444:17:444:20 | bb | string.cpp:444:17:444:21 | call to basic_string | TAINT |
+| string.cpp:444:17:444:21 | call to basic_string | string.cpp:449:8:449:8 | b | |
+| string.cpp:444:17:444:21 | call to basic_string | string.cpp:449:17:449:17 | b | |
+| string.cpp:444:17:444:21 | call to basic_string | string.cpp:450:8:450:8 | b | |
+| string.cpp:446:8:446:8 | a | string.cpp:446:10:446:15 | call to insert | TAINT |
+| string.cpp:446:8:446:8 | ref arg a | string.cpp:447:8:447:8 | a | |
+| string.cpp:446:17:446:17 | a | string.cpp:446:19:446:23 | call to begin | TAINT |
+| string.cpp:446:17:446:17 | ref arg a | string.cpp:446:8:446:8 | a | |
+| string.cpp:446:17:446:17 | ref arg a | string.cpp:447:8:447:8 | a | |
+| string.cpp:446:19:446:23 | call to begin | string.cpp:446:17:446:25 | call to iterator | TAINT |
+| string.cpp:446:32:446:34 | 120 | string.cpp:446:8:446:8 | ref arg a | TAINT |
+| string.cpp:446:32:446:34 | 120 | string.cpp:446:10:446:15 | call to insert | TAINT |
+| string.cpp:449:8:449:8 | b | string.cpp:449:10:449:15 | call to insert | TAINT |
+| string.cpp:449:8:449:8 | ref arg b | string.cpp:450:8:450:8 | b | |
+| string.cpp:449:17:449:17 | b | string.cpp:449:19:449:23 | call to begin | TAINT |
+| string.cpp:449:17:449:17 | ref arg b | string.cpp:449:8:449:8 | b | |
+| string.cpp:449:17:449:17 | ref arg b | string.cpp:450:8:450:8 | b | |
+| string.cpp:449:19:449:23 | call to begin | string.cpp:449:17:449:25 | call to iterator | TAINT |
+| string.cpp:449:32:449:46 | call to source | string.cpp:449:8:449:8 | ref arg b | TAINT |
+| string.cpp:449:32:449:46 | call to source | string.cpp:449:10:449:15 | call to insert | TAINT |
+| string.cpp:454:17:454:20 | cc | string.cpp:454:17:454:21 | call to basic_string | TAINT |
+| string.cpp:454:17:454:21 | call to basic_string | string.cpp:459:8:459:8 | c | |
+| string.cpp:454:17:454:21 | call to basic_string | string.cpp:459:17:459:17 | c | |
+| string.cpp:454:17:454:21 | call to basic_string | string.cpp:460:8:460:8 | c | |
+| string.cpp:455:17:455:20 | dd | string.cpp:455:17:455:21 | call to basic_string | TAINT |
+| string.cpp:455:17:455:21 | call to basic_string | string.cpp:462:8:462:8 | d | |
+| string.cpp:455:17:455:21 | call to basic_string | string.cpp:462:17:462:17 | d | |
+| string.cpp:455:17:455:21 | call to basic_string | string.cpp:463:8:463:8 | d | |
+| string.cpp:456:18:456:21 | 11 | string.cpp:456:18:456:22 | call to basic_string | TAINT |
+| string.cpp:456:18:456:22 | call to basic_string | string.cpp:459:26:459:27 | s1 | |
+| string.cpp:456:18:456:22 | call to basic_string | string.cpp:459:38:459:39 | s1 | |
+| string.cpp:456:18:456:22 | call to basic_string | string.cpp:465:28:465:29 | s1 | |
+| string.cpp:456:18:456:22 | call to basic_string | string.cpp:465:40:465:41 | s1 | |
+| string.cpp:457:18:457:23 | call to source | string.cpp:457:18:457:26 | call to basic_string | TAINT |
+| string.cpp:457:18:457:26 | call to basic_string | string.cpp:462:26:462:27 | s2 | |
+| string.cpp:457:18:457:26 | call to basic_string | string.cpp:462:38:462:39 | s2 | |
+| string.cpp:457:18:457:26 | call to basic_string | string.cpp:465:8:465:9 | s2 | |
+| string.cpp:457:18:457:26 | call to basic_string | string.cpp:465:18:465:19 | s2 | |
+| string.cpp:457:18:457:26 | call to basic_string | string.cpp:466:8:466:9 | s2 | |
+| string.cpp:459:8:459:8 | c | string.cpp:459:10:459:15 | call to insert | TAINT |
+| string.cpp:459:8:459:8 | ref arg c | string.cpp:460:8:460:8 | c | |
+| string.cpp:459:17:459:17 | c | string.cpp:459:19:459:21 | call to end | TAINT |
+| string.cpp:459:17:459:17 | ref arg c | string.cpp:459:8:459:8 | c | |
+| string.cpp:459:17:459:17 | ref arg c | string.cpp:460:8:460:8 | c | |
+| string.cpp:459:19:459:21 | call to end | string.cpp:459:17:459:23 | call to iterator | TAINT |
+| string.cpp:459:26:459:27 | ref arg s1 | string.cpp:459:38:459:39 | s1 | |
+| string.cpp:459:26:459:27 | ref arg s1 | string.cpp:465:28:465:29 | s1 | |
+| string.cpp:459:26:459:27 | ref arg s1 | string.cpp:465:40:465:41 | s1 | |
+| string.cpp:459:26:459:27 | s1 | string.cpp:459:29:459:33 | call to begin | TAINT |
+| string.cpp:459:29:459:33 | call to begin | string.cpp:459:8:459:8 | ref arg c | TAINT |
+| string.cpp:459:29:459:33 | call to begin | string.cpp:459:10:459:15 | call to insert | TAINT |
+| string.cpp:459:38:459:39 | ref arg s1 | string.cpp:465:28:465:29 | s1 | |
+| string.cpp:459:38:459:39 | ref arg s1 | string.cpp:465:40:465:41 | s1 | |
+| string.cpp:459:38:459:39 | s1 | string.cpp:459:41:459:43 | call to end | TAINT |
+| string.cpp:459:41:459:43 | call to end | string.cpp:459:8:459:8 | ref arg c | TAINT |
+| string.cpp:459:41:459:43 | call to end | string.cpp:459:10:459:15 | call to insert | TAINT |
+| string.cpp:462:8:462:8 | d | string.cpp:462:10:462:15 | call to insert | TAINT |
+| string.cpp:462:8:462:8 | ref arg d | string.cpp:463:8:463:8 | d | |
+| string.cpp:462:17:462:17 | d | string.cpp:462:19:462:21 | call to end | TAINT |
+| string.cpp:462:17:462:17 | ref arg d | string.cpp:462:8:462:8 | d | |
+| string.cpp:462:17:462:17 | ref arg d | string.cpp:463:8:463:8 | d | |
+| string.cpp:462:19:462:21 | call to end | string.cpp:462:17:462:23 | call to iterator | TAINT |
+| string.cpp:462:26:462:27 | ref arg s2 | string.cpp:462:38:462:39 | s2 | |
+| string.cpp:462:26:462:27 | ref arg s2 | string.cpp:465:8:465:9 | s2 | |
+| string.cpp:462:26:462:27 | ref arg s2 | string.cpp:465:18:465:19 | s2 | |
+| string.cpp:462:26:462:27 | ref arg s2 | string.cpp:466:8:466:9 | s2 | |
+| string.cpp:462:26:462:27 | s2 | string.cpp:462:29:462:33 | call to begin | TAINT |
+| string.cpp:462:29:462:33 | call to begin | string.cpp:462:8:462:8 | ref arg d | TAINT |
+| string.cpp:462:29:462:33 | call to begin | string.cpp:462:10:462:15 | call to insert | TAINT |
+| string.cpp:462:38:462:39 | ref arg s2 | string.cpp:465:8:465:9 | s2 | |
+| string.cpp:462:38:462:39 | ref arg s2 | string.cpp:465:18:465:19 | s2 | |
+| string.cpp:462:38:462:39 | ref arg s2 | string.cpp:466:8:466:9 | s2 | |
+| string.cpp:462:38:462:39 | s2 | string.cpp:462:41:462:43 | call to end | TAINT |
+| string.cpp:462:41:462:43 | call to end | string.cpp:462:8:462:8 | ref arg d | TAINT |
+| string.cpp:462:41:462:43 | call to end | string.cpp:462:10:462:15 | call to insert | TAINT |
+| string.cpp:465:8:465:9 | ref arg s2 | string.cpp:466:8:466:9 | s2 | |
+| string.cpp:465:8:465:9 | s2 | string.cpp:465:11:465:16 | call to insert | TAINT |
+| string.cpp:465:18:465:19 | ref arg s2 | string.cpp:465:8:465:9 | s2 | |
+| string.cpp:465:18:465:19 | ref arg s2 | string.cpp:466:8:466:9 | s2 | |
+| string.cpp:465:18:465:19 | s2 | string.cpp:465:21:465:23 | call to end | TAINT |
+| string.cpp:465:21:465:23 | call to end | string.cpp:465:18:465:25 | call to iterator | TAINT |
+| string.cpp:465:28:465:29 | ref arg s1 | string.cpp:465:40:465:41 | s1 | |
+| string.cpp:465:28:465:29 | s1 | string.cpp:465:31:465:35 | call to begin | TAINT |
+| string.cpp:465:31:465:35 | call to begin | string.cpp:465:8:465:9 | ref arg s2 | TAINT |
+| string.cpp:465:31:465:35 | call to begin | string.cpp:465:11:465:16 | call to insert | TAINT |
+| string.cpp:465:40:465:41 | s1 | string.cpp:465:43:465:45 | call to end | TAINT |
+| string.cpp:465:43:465:45 | call to end | string.cpp:465:8:465:9 | ref arg s2 | TAINT |
+| string.cpp:465:43:465:45 | call to end | string.cpp:465:11:465:16 | call to insert | TAINT |
+| string.cpp:470:17:470:20 | ee | string.cpp:470:17:470:21 | call to basic_string | TAINT |
+| string.cpp:470:17:470:21 | call to basic_string | string.cpp:475:8:475:8 | e | |
+| string.cpp:470:17:470:21 | call to basic_string | string.cpp:476:8:476:8 | e | |
+| string.cpp:471:17:471:20 | ff | string.cpp:471:17:471:21 | call to basic_string | TAINT |
+| string.cpp:471:17:471:21 | call to basic_string | string.cpp:478:8:478:8 | f | |
+| string.cpp:471:17:471:21 | call to basic_string | string.cpp:479:8:479:8 | f | |
+| string.cpp:472:18:472:21 | 33 | string.cpp:472:18:472:22 | call to basic_string | TAINT |
+| string.cpp:472:18:472:22 | call to basic_string | string.cpp:475:17:475:18 | s3 | |
+| string.cpp:472:18:472:22 | call to basic_string | string.cpp:475:29:475:30 | s3 | |
+| string.cpp:472:18:472:22 | call to basic_string | string.cpp:481:18:481:19 | s3 | |
+| string.cpp:472:18:472:22 | call to basic_string | string.cpp:481:30:481:31 | s3 | |
+| string.cpp:473:18:473:23 | call to source | string.cpp:473:18:473:26 | call to basic_string | TAINT |
+| string.cpp:473:18:473:26 | call to basic_string | string.cpp:478:17:478:18 | s4 | |
+| string.cpp:473:18:473:26 | call to basic_string | string.cpp:478:29:478:30 | s4 | |
+| string.cpp:473:18:473:26 | call to basic_string | string.cpp:481:8:481:9 | s4 | |
+| string.cpp:473:18:473:26 | call to basic_string | string.cpp:482:8:482:9 | s4 | |
+| string.cpp:475:8:475:8 | e | string.cpp:475:10:475:15 | call to append | TAINT |
+| string.cpp:475:8:475:8 | ref arg e | string.cpp:476:8:476:8 | e | |
+| string.cpp:475:17:475:18 | ref arg s3 | string.cpp:475:29:475:30 | s3 | |
+| string.cpp:475:17:475:18 | ref arg s3 | string.cpp:481:18:481:19 | s3 | |
+| string.cpp:475:17:475:18 | ref arg s3 | string.cpp:481:30:481:31 | s3 | |
+| string.cpp:475:17:475:18 | s3 | string.cpp:475:20:475:24 | call to begin | TAINT |
+| string.cpp:475:20:475:24 | call to begin | string.cpp:475:8:475:8 | ref arg e | TAINT |
+| string.cpp:475:20:475:24 | call to begin | string.cpp:475:10:475:15 | call to append | TAINT |
+| string.cpp:475:29:475:30 | ref arg s3 | string.cpp:481:18:481:19 | s3 | |
+| string.cpp:475:29:475:30 | ref arg s3 | string.cpp:481:30:481:31 | s3 | |
+| string.cpp:475:29:475:30 | s3 | string.cpp:475:32:475:34 | call to end | TAINT |
+| string.cpp:475:32:475:34 | call to end | string.cpp:475:8:475:8 | ref arg e | TAINT |
+| string.cpp:475:32:475:34 | call to end | string.cpp:475:10:475:15 | call to append | TAINT |
+| string.cpp:478:8:478:8 | f | string.cpp:478:10:478:15 | call to append | TAINT |
+| string.cpp:478:8:478:8 | ref arg f | string.cpp:479:8:479:8 | f | |
+| string.cpp:478:17:478:18 | ref arg s4 | string.cpp:478:29:478:30 | s4 | |
+| string.cpp:478:17:478:18 | ref arg s4 | string.cpp:481:8:481:9 | s4 | |
+| string.cpp:478:17:478:18 | ref arg s4 | string.cpp:482:8:482:9 | s4 | |
+| string.cpp:478:17:478:18 | s4 | string.cpp:478:20:478:24 | call to begin | TAINT |
+| string.cpp:478:20:478:24 | call to begin | string.cpp:478:8:478:8 | ref arg f | TAINT |
+| string.cpp:478:20:478:24 | call to begin | string.cpp:478:10:478:15 | call to append | TAINT |
+| string.cpp:478:29:478:30 | ref arg s4 | string.cpp:481:8:481:9 | s4 | |
+| string.cpp:478:29:478:30 | ref arg s4 | string.cpp:482:8:482:9 | s4 | |
+| string.cpp:478:29:478:30 | s4 | string.cpp:478:32:478:34 | call to end | TAINT |
+| string.cpp:478:32:478:34 | call to end | string.cpp:478:8:478:8 | ref arg f | TAINT |
+| string.cpp:478:32:478:34 | call to end | string.cpp:478:10:478:15 | call to append | TAINT |
+| string.cpp:481:8:481:9 | ref arg s4 | string.cpp:482:8:482:9 | s4 | |
+| string.cpp:481:8:481:9 | s4 | string.cpp:481:11:481:16 | call to append | TAINT |
+| string.cpp:481:18:481:19 | ref arg s3 | string.cpp:481:30:481:31 | s3 | |
+| string.cpp:481:18:481:19 | s3 | string.cpp:481:21:481:25 | call to begin | TAINT |
+| string.cpp:481:21:481:25 | call to begin | string.cpp:481:8:481:9 | ref arg s4 | TAINT |
+| string.cpp:481:21:481:25 | call to begin | string.cpp:481:11:481:16 | call to append | TAINT |
+| string.cpp:481:30:481:31 | s3 | string.cpp:481:33:481:35 | call to end | TAINT |
+| string.cpp:481:33:481:35 | call to end | string.cpp:481:8:481:9 | ref arg s4 | TAINT |
+| string.cpp:481:33:481:35 | call to end | string.cpp:481:11:481:16 | call to append | TAINT |
+| string.cpp:486:17:486:20 | gg | string.cpp:486:17:486:21 | call to basic_string | TAINT |
+| string.cpp:486:17:486:21 | call to basic_string | string.cpp:491:8:491:8 | g | |
+| string.cpp:486:17:486:21 | call to basic_string | string.cpp:492:8:492:8 | g | |
+| string.cpp:487:17:487:20 | hh | string.cpp:487:17:487:21 | call to basic_string | TAINT |
+| string.cpp:487:17:487:21 | call to basic_string | string.cpp:494:8:494:8 | h | |
+| string.cpp:487:17:487:21 | call to basic_string | string.cpp:495:8:495:8 | h | |
+| string.cpp:488:18:488:21 | 55 | string.cpp:488:18:488:22 | call to basic_string | TAINT |
+| string.cpp:488:18:488:22 | call to basic_string | string.cpp:491:17:491:18 | s5 | |
+| string.cpp:488:18:488:22 | call to basic_string | string.cpp:491:30:491:31 | s5 | |
+| string.cpp:488:18:488:22 | call to basic_string | string.cpp:497:18:497:19 | s5 | |
+| string.cpp:488:18:488:22 | call to basic_string | string.cpp:497:31:497:32 | s5 | |
+| string.cpp:489:18:489:23 | call to source | string.cpp:489:18:489:26 | call to basic_string | TAINT |
+| string.cpp:489:18:489:26 | call to basic_string | string.cpp:494:17:494:18 | s6 | |
+| string.cpp:489:18:489:26 | call to basic_string | string.cpp:494:30:494:31 | s6 | |
+| string.cpp:489:18:489:26 | call to basic_string | string.cpp:497:8:497:9 | s6 | |
+| string.cpp:489:18:489:26 | call to basic_string | string.cpp:498:8:498:9 | s6 | |
+| string.cpp:491:8:491:8 | ref arg g | string.cpp:492:8:492:8 | g | |
+| string.cpp:491:17:491:18 | s5 | string.cpp:491:20:491:25 | call to cbegin | TAINT |
+| string.cpp:491:20:491:25 | call to cbegin | string.cpp:491:8:491:8 | ref arg g | TAINT |
+| string.cpp:491:20:491:25 | call to cbegin | string.cpp:491:10:491:15 | call to assign | TAINT |
+| string.cpp:491:30:491:31 | s5 | string.cpp:491:33:491:36 | call to cend | TAINT |
+| string.cpp:491:33:491:36 | call to cend | string.cpp:491:8:491:8 | ref arg g | TAINT |
+| string.cpp:491:33:491:36 | call to cend | string.cpp:491:10:491:15 | call to assign | TAINT |
+| string.cpp:494:8:494:8 | ref arg h | string.cpp:495:8:495:8 | h | |
+| string.cpp:494:17:494:18 | s6 | string.cpp:494:20:494:25 | call to cbegin | TAINT |
+| string.cpp:494:20:494:25 | call to cbegin | string.cpp:494:8:494:8 | ref arg h | TAINT |
+| string.cpp:494:20:494:25 | call to cbegin | string.cpp:494:10:494:15 | call to assign | TAINT |
+| string.cpp:494:30:494:31 | s6 | string.cpp:494:33:494:36 | call to cend | TAINT |
+| string.cpp:494:33:494:36 | call to cend | string.cpp:494:8:494:8 | ref arg h | TAINT |
+| string.cpp:494:33:494:36 | call to cend | string.cpp:494:10:494:15 | call to assign | TAINT |
+| string.cpp:497:8:497:9 | ref arg s6 | string.cpp:498:8:498:9 | s6 | |
+| string.cpp:497:18:497:19 | s5 | string.cpp:497:21:497:26 | call to cbegin | TAINT |
+| string.cpp:497:21:497:26 | call to cbegin | string.cpp:497:8:497:9 | ref arg s6 | TAINT |
+| string.cpp:497:21:497:26 | call to cbegin | string.cpp:497:11:497:16 | call to assign | TAINT |
+| string.cpp:497:31:497:32 | s5 | string.cpp:497:34:497:37 | call to cend | TAINT |
+| string.cpp:497:34:497:37 | call to cend | string.cpp:497:8:497:9 | ref arg s6 | TAINT |
+| string.cpp:497:34:497:37 | call to cend | string.cpp:497:11:497:16 | call to assign | TAINT |
+| string.cpp:503:14:503:18 | abc | string.cpp:505:17:505:19 | cs1 | |
+| string.cpp:504:14:504:19 | call to source | string.cpp:506:17:506:19 | cs2 | |
+| string.cpp:505:17:505:19 | cs1 | string.cpp:505:17:505:20 | call to basic_string | TAINT |
+| string.cpp:505:17:505:20 | call to basic_string | string.cpp:507:17:507:18 | s1 | |
+| string.cpp:505:17:505:20 | call to basic_string | string.cpp:507:29:507:30 | s1 | |
+| string.cpp:505:17:505:20 | call to basic_string | string.cpp:510:7:510:8 | s1 | |
+| string.cpp:506:17:506:19 | cs2 | string.cpp:506:17:506:20 | call to basic_string | TAINT |
+| string.cpp:506:17:506:20 | call to basic_string | string.cpp:508:17:508:18 | s2 | |
+| string.cpp:506:17:506:20 | call to basic_string | string.cpp:508:29:508:30 | s2 | |
+| string.cpp:506:17:506:20 | call to basic_string | string.cpp:511:7:511:8 | s2 | |
+| string.cpp:507:17:507:18 | ref arg s1 | string.cpp:507:29:507:30 | s1 | |
+| string.cpp:507:17:507:18 | ref arg s1 | string.cpp:510:7:510:8 | s1 | |
+| string.cpp:507:17:507:18 | s1 | string.cpp:507:20:507:24 | call to begin | TAINT |
+| string.cpp:507:17:507:37 | call to basic_string | string.cpp:512:7:512:8 | s3 | |
+| string.cpp:507:20:507:24 | call to begin | string.cpp:507:17:507:37 | call to basic_string | TAINT |
+| string.cpp:507:29:507:30 | ref arg s1 | string.cpp:510:7:510:8 | s1 | |
+| string.cpp:507:29:507:30 | s1 | string.cpp:507:32:507:34 | call to end | TAINT |
+| string.cpp:507:32:507:34 | call to end | string.cpp:507:17:507:37 | call to basic_string | TAINT |
+| string.cpp:508:17:508:18 | ref arg s2 | string.cpp:508:29:508:30 | s2 | |
+| string.cpp:508:17:508:18 | ref arg s2 | string.cpp:511:7:511:8 | s2 | |
+| string.cpp:508:17:508:18 | s2 | string.cpp:508:20:508:24 | call to begin | TAINT |
+| string.cpp:508:17:508:37 | call to basic_string | string.cpp:513:7:513:8 | s4 | |
+| string.cpp:508:20:508:24 | call to begin | string.cpp:508:17:508:37 | call to basic_string | TAINT |
+| string.cpp:508:29:508:30 | ref arg s2 | string.cpp:511:7:511:8 | s2 | |
+| string.cpp:508:29:508:30 | s2 | string.cpp:508:32:508:34 | call to end | TAINT |
+| string.cpp:508:32:508:34 | call to end | string.cpp:508:17:508:37 | call to basic_string | TAINT |
+| string.cpp:517:16:517:19 | aa | string.cpp:517:16:517:20 | call to basic_string | TAINT |
+| string.cpp:517:16:517:20 | call to basic_string | string.cpp:519:7:519:7 | a | |
+| string.cpp:517:16:517:20 | call to basic_string | string.cpp:520:7:520:7 | a | |
+| string.cpp:517:16:517:20 | call to basic_string | string.cpp:521:2:521:2 | a | |
+| string.cpp:517:16:517:20 | call to basic_string | string.cpp:522:7:522:7 | a | |
+| string.cpp:517:16:517:20 | call to basic_string | string.cpp:523:7:523:7 | a | |
+| string.cpp:519:7:519:7 | a | string.cpp:519:9:519:13 | call to front | TAINT |
+| string.cpp:519:7:519:7 | ref arg a | string.cpp:520:7:520:7 | a | |
+| string.cpp:519:7:519:7 | ref arg a | string.cpp:521:2:521:2 | a | |
+| string.cpp:519:7:519:7 | ref arg a | string.cpp:522:7:522:7 | a | |
+| string.cpp:519:7:519:7 | ref arg a | string.cpp:523:7:523:7 | a | |
+| string.cpp:520:7:520:7 | a | string.cpp:520:9:520:12 | call to back | TAINT |
+| string.cpp:520:7:520:7 | ref arg a | string.cpp:521:2:521:2 | a | |
+| string.cpp:520:7:520:7 | ref arg a | string.cpp:522:7:522:7 | a | |
+| string.cpp:520:7:520:7 | ref arg a | string.cpp:523:7:523:7 | a | |
+| string.cpp:521:2:521:2 | ref arg a | string.cpp:522:7:522:7 | a | |
+| string.cpp:521:2:521:2 | ref arg a | string.cpp:523:7:523:7 | a | |
+| string.cpp:521:14:521:28 | call to source | string.cpp:521:2:521:2 | ref arg a | TAINT |
+| string.cpp:522:7:522:7 | a | string.cpp:522:9:522:13 | call to front | TAINT |
+| string.cpp:522:7:522:7 | ref arg a | string.cpp:523:7:523:7 | a | |
+| string.cpp:523:7:523:7 | a | string.cpp:523:9:523:12 | call to back | TAINT |
+| string.cpp:528:17:528:20 | aa | string.cpp:528:17:528:21 | call to basic_string | TAINT |
+| string.cpp:528:17:528:21 | call to basic_string | string.cpp:535:9:535:9 | a | |
+| string.cpp:528:17:528:21 | call to basic_string | string.cpp:539:8:539:8 | a | |
+| string.cpp:529:17:529:20 | bb | string.cpp:529:17:529:21 | call to basic_string | TAINT |
+| string.cpp:529:17:529:21 | call to basic_string | string.cpp:535:15:535:15 | b | |
+| string.cpp:529:17:529:21 | call to basic_string | string.cpp:540:8:540:8 | b | |
+| string.cpp:530:17:530:20 | cc | string.cpp:530:17:530:21 | call to basic_string | TAINT |
+| string.cpp:530:17:530:21 | call to basic_string | string.cpp:536:9:536:9 | c | |
+| string.cpp:530:17:530:21 | call to basic_string | string.cpp:541:8:541:8 | c | |
+| string.cpp:531:17:531:20 | dd | string.cpp:531:17:531:21 | call to basic_string | TAINT |
+| string.cpp:531:17:531:21 | call to basic_string | string.cpp:536:15:536:15 | d | |
+| string.cpp:531:17:531:21 | call to basic_string | string.cpp:542:8:542:8 | d | |
+| string.cpp:532:17:532:20 | ee | string.cpp:532:17:532:21 | call to basic_string | TAINT |
+| string.cpp:532:17:532:21 | call to basic_string | string.cpp:537:10:537:10 | e | |
+| string.cpp:532:17:532:21 | call to basic_string | string.cpp:543:8:543:8 | e | |
+| string.cpp:533:17:533:20 | ff | string.cpp:533:17:533:21 | call to basic_string | TAINT |
+| string.cpp:533:17:533:21 | call to basic_string | string.cpp:538:10:538:10 | f | |
+| string.cpp:533:17:533:21 | call to basic_string | string.cpp:544:8:544:8 | f | |
+| string.cpp:535:9:535:9 | a | string.cpp:535:11:535:11 | call to operator+= | TAINT |
+| string.cpp:535:9:535:9 | ref arg a | string.cpp:539:8:539:8 | a | |
+| string.cpp:535:15:535:15 | b | string.cpp:535:17:535:17 | call to operator+= | TAINT |
+| string.cpp:535:15:535:15 | ref arg b | string.cpp:540:8:540:8 | b | |
+| string.cpp:535:17:535:17 | call to operator+= | string.cpp:535:9:535:9 | ref arg a | TAINT |
+| string.cpp:535:17:535:17 | call to operator+= | string.cpp:535:11:535:11 | call to operator+= | TAINT |
+| string.cpp:535:20:535:23 | bb | string.cpp:535:15:535:15 | ref arg b | TAINT |
+| string.cpp:535:20:535:23 | bb | string.cpp:535:17:535:17 | call to operator+= | TAINT |
+| string.cpp:536:9:536:9 | c | string.cpp:536:11:536:11 | call to operator+= | TAINT |
+| string.cpp:536:9:536:9 | ref arg c | string.cpp:541:8:541:8 | c | |
+| string.cpp:536:15:536:15 | d | string.cpp:536:17:536:17 | call to operator+= | TAINT |
+| string.cpp:536:15:536:15 | ref arg d | string.cpp:542:8:542:8 | d | |
+| string.cpp:536:17:536:17 | call to operator+= | string.cpp:536:9:536:9 | ref arg c | TAINT |
+| string.cpp:536:17:536:17 | call to operator+= | string.cpp:536:11:536:11 | call to operator+= | TAINT |
+| string.cpp:536:20:536:25 | call to source | string.cpp:536:15:536:15 | ref arg d | TAINT |
+| string.cpp:536:20:536:25 | call to source | string.cpp:536:17:536:17 | call to operator+= | TAINT |
+| string.cpp:537:10:537:10 | e | string.cpp:537:12:537:12 | call to operator+= | TAINT |
+| string.cpp:537:10:537:10 | ref arg e | string.cpp:543:8:543:8 | e | |
+| string.cpp:537:12:537:12 | call to operator+= | string.cpp:537:21:537:21 | call to operator+= | TAINT |
+| string.cpp:537:12:537:12 | ref arg call to operator+= | string.cpp:537:10:537:10 | ref arg e | TAINT |
+| string.cpp:537:15:537:18 | ee | string.cpp:537:10:537:10 | ref arg e | TAINT |
+| string.cpp:537:15:537:18 | ee | string.cpp:537:12:537:12 | call to operator+= | TAINT |
+| string.cpp:537:24:537:29 | call to source | string.cpp:537:12:537:12 | ref arg call to operator+= | TAINT |
+| string.cpp:537:24:537:29 | call to source | string.cpp:537:21:537:21 | call to operator+= | TAINT |
+| string.cpp:538:10:538:10 | f | string.cpp:538:12:538:12 | call to operator+= | TAINT |
+| string.cpp:538:10:538:10 | ref arg f | string.cpp:544:8:544:8 | f | |
+| string.cpp:538:12:538:12 | call to operator+= | string.cpp:538:25:538:25 | call to operator+= | TAINT |
+| string.cpp:538:12:538:12 | ref arg call to operator+= | string.cpp:538:10:538:10 | ref arg f | TAINT |
+| string.cpp:538:15:538:20 | call to source | string.cpp:538:10:538:10 | ref arg f | TAINT |
+| string.cpp:538:15:538:20 | call to source | string.cpp:538:12:538:12 | call to operator+= | TAINT |
+| string.cpp:538:28:538:31 | ff | string.cpp:538:12:538:12 | ref arg call to operator+= | TAINT |
+| string.cpp:538:28:538:31 | ff | string.cpp:538:25:538:25 | call to operator+= | TAINT |
+| string.cpp:548:17:548:20 | aa | string.cpp:548:17:548:21 | call to basic_string | TAINT |
+| string.cpp:548:17:548:21 | call to basic_string | string.cpp:555:9:555:9 | a | |
+| string.cpp:548:17:548:21 | call to basic_string | string.cpp:559:8:559:8 | a | |
+| string.cpp:549:17:549:20 | bb | string.cpp:549:17:549:21 | call to basic_string | TAINT |
+| string.cpp:549:17:549:21 | call to basic_string | string.cpp:555:18:555:18 | b | |
+| string.cpp:549:17:549:21 | call to basic_string | string.cpp:560:8:560:8 | b | |
+| string.cpp:550:17:550:20 | cc | string.cpp:550:17:550:21 | call to basic_string | TAINT |
+| string.cpp:550:17:550:21 | call to basic_string | string.cpp:556:9:556:9 | c | |
+| string.cpp:550:17:550:21 | call to basic_string | string.cpp:561:8:561:8 | c | |
+| string.cpp:551:17:551:20 | dd | string.cpp:551:17:551:21 | call to basic_string | TAINT |
+| string.cpp:551:17:551:21 | call to basic_string | string.cpp:556:18:556:18 | d | |
+| string.cpp:551:17:551:21 | call to basic_string | string.cpp:562:8:562:8 | d | |
+| string.cpp:552:17:552:20 | ee | string.cpp:552:17:552:21 | call to basic_string | TAINT |
+| string.cpp:552:17:552:21 | call to basic_string | string.cpp:557:9:557:9 | e | |
+| string.cpp:552:17:552:21 | call to basic_string | string.cpp:563:8:563:8 | e | |
+| string.cpp:553:17:553:20 | ff | string.cpp:553:17:553:21 | call to basic_string | TAINT |
+| string.cpp:553:17:553:21 | call to basic_string | string.cpp:558:9:558:9 | f | |
+| string.cpp:553:17:553:21 | call to basic_string | string.cpp:564:8:564:8 | f | |
+| string.cpp:555:9:555:9 | ref arg a | string.cpp:559:8:559:8 | a | |
+| string.cpp:555:18:555:18 | ref arg b | string.cpp:560:8:560:8 | b | |
+| string.cpp:555:20:555:25 | call to assign | string.cpp:555:9:555:9 | ref arg a | TAINT |
+| string.cpp:555:20:555:25 | call to assign | string.cpp:555:11:555:16 | call to assign | TAINT |
+| string.cpp:555:27:555:30 | bb | string.cpp:555:27:555:30 | call to basic_string | TAINT |
+| string.cpp:555:27:555:30 | call to basic_string | string.cpp:555:18:555:18 | ref arg b | TAINT |
+| string.cpp:555:27:555:30 | call to basic_string | string.cpp:555:20:555:25 | call to assign | TAINT |
+| string.cpp:556:9:556:9 | ref arg c | string.cpp:561:8:561:8 | c | |
+| string.cpp:556:18:556:18 | ref arg d | string.cpp:562:8:562:8 | d | |
+| string.cpp:556:20:556:25 | call to assign | string.cpp:556:9:556:9 | ref arg c | TAINT |
+| string.cpp:556:20:556:25 | call to assign | string.cpp:556:11:556:16 | call to assign | TAINT |
+| string.cpp:556:27:556:32 | call to source | string.cpp:556:27:556:34 | call to basic_string | TAINT |
+| string.cpp:556:27:556:34 | call to basic_string | string.cpp:556:18:556:18 | ref arg d | TAINT |
+| string.cpp:556:27:556:34 | call to basic_string | string.cpp:556:20:556:25 | call to assign | TAINT |
+| string.cpp:557:9:557:9 | ref arg e | string.cpp:563:8:563:8 | e | |
+| string.cpp:557:11:557:16 | ref arg call to assign | string.cpp:557:9:557:9 | ref arg e | TAINT |
+| string.cpp:557:18:557:21 | call to basic_string | string.cpp:557:9:557:9 | ref arg e | TAINT |
+| string.cpp:557:18:557:21 | call to basic_string | string.cpp:557:11:557:16 | call to assign | TAINT |
+| string.cpp:557:18:557:21 | ee | string.cpp:557:18:557:21 | call to basic_string | TAINT |
+| string.cpp:557:31:557:36 | call to source | string.cpp:557:31:557:38 | call to basic_string | TAINT |
+| string.cpp:557:31:557:38 | call to basic_string | string.cpp:557:11:557:16 | ref arg call to assign | TAINT |
+| string.cpp:557:31:557:38 | call to basic_string | string.cpp:557:24:557:29 | call to assign | TAINT |
+| string.cpp:558:9:558:9 | ref arg f | string.cpp:564:8:564:8 | f | |
+| string.cpp:558:11:558:16 | ref arg call to assign | string.cpp:558:9:558:9 | ref arg f | TAINT |
+| string.cpp:558:18:558:23 | call to source | string.cpp:558:18:558:25 | call to basic_string | TAINT |
+| string.cpp:558:18:558:25 | call to basic_string | string.cpp:558:9:558:9 | ref arg f | TAINT |
+| string.cpp:558:18:558:25 | call to basic_string | string.cpp:558:11:558:16 | call to assign | TAINT |
+| string.cpp:558:35:558:38 | call to basic_string | string.cpp:558:11:558:16 | ref arg call to assign | TAINT |
+| string.cpp:558:35:558:38 | call to basic_string | string.cpp:558:28:558:33 | call to assign | TAINT |
+| string.cpp:558:35:558:38 | ff | string.cpp:558:35:558:38 | call to basic_string | TAINT |
+| stringstream.cpp:26:35:26:40 | amount | stringstream.cpp:64:46:64:51 | amount | |
+| stringstream.cpp:28:20:28:22 | call to basic_stringstream | stringstream.cpp:31:7:31:9 | ss1 | |
+| stringstream.cpp:28:20:28:22 | call to basic_stringstream | stringstream.cpp:37:7:37:9 | ss1 | |
+| stringstream.cpp:28:20:28:22 | call to basic_stringstream | stringstream.cpp:42:7:42:9 | ss1 | |
+| stringstream.cpp:28:25:28:27 | call to basic_stringstream | stringstream.cpp:32:7:32:9 | ss2 | |
+| stringstream.cpp:28:25:28:27 | call to basic_stringstream | stringstream.cpp:38:7:38:9 | ss2 | |
+| stringstream.cpp:28:25:28:27 | call to basic_stringstream | stringstream.cpp:43:7:43:9 | ss2 | |
+| stringstream.cpp:28:30:28:32 | call to basic_stringstream | stringstream.cpp:33:7:33:9 | ss3 | |
+| stringstream.cpp:28:30:28:32 | call to basic_stringstream | stringstream.cpp:39:7:39:9 | ss3 | |
+| stringstream.cpp:28:30:28:32 | call to basic_stringstream | stringstream.cpp:44:7:44:9 | ss3 | |
+| stringstream.cpp:28:35:28:37 | call to basic_stringstream | stringstream.cpp:34:7:34:9 | ss4 | |
+| stringstream.cpp:28:35:28:37 | call to basic_stringstream | stringstream.cpp:40:7:40:9 | ss4 | |
+| stringstream.cpp:28:35:28:37 | call to basic_stringstream | stringstream.cpp:45:7:45:9 | ss4 | |
+| stringstream.cpp:28:40:28:42 | call to basic_stringstream | stringstream.cpp:35:7:35:9 | ss5 | |
+| stringstream.cpp:28:40:28:42 | call to basic_stringstream | stringstream.cpp:41:7:41:9 | ss5 | |
+| stringstream.cpp:28:40:28:42 | call to basic_stringstream | stringstream.cpp:46:7:46:9 | ss5 | |
+| stringstream.cpp:28:45:28:47 | call to basic_stringstream | stringstream.cpp:48:2:48:4 | ss6 | |
+| stringstream.cpp:28:45:28:47 | call to basic_stringstream | stringstream.cpp:49:2:49:4 | ss6 | |
+| stringstream.cpp:28:45:28:47 | call to basic_stringstream | stringstream.cpp:52:7:52:9 | ss6 | |
+| stringstream.cpp:28:50:28:52 | call to basic_stringstream | stringstream.cpp:50:2:50:4 | ss7 | |
+| stringstream.cpp:28:50:28:52 | call to basic_stringstream | stringstream.cpp:51:2:51:4 | ss7 | |
+| stringstream.cpp:28:50:28:52 | call to basic_stringstream | stringstream.cpp:53:7:53:9 | ss7 | |
+| stringstream.cpp:28:55:28:57 | call to basic_stringstream | stringstream.cpp:55:7:55:9 | ss8 | |
+| stringstream.cpp:28:55:28:57 | call to basic_stringstream | stringstream.cpp:58:7:58:9 | ss8 | |
+| stringstream.cpp:28:60:28:62 | call to basic_stringstream | stringstream.cpp:56:7:56:9 | ss9 | |
+| stringstream.cpp:28:60:28:62 | call to basic_stringstream | stringstream.cpp:59:7:59:9 | ss9 | |
+| stringstream.cpp:28:65:28:68 | call to basic_stringstream | stringstream.cpp:57:7:57:10 | ss10 | |
+| stringstream.cpp:28:65:28:68 | call to basic_stringstream | stringstream.cpp:60:7:60:10 | ss10 | |
+| stringstream.cpp:28:71:28:74 | call to basic_stringstream | stringstream.cpp:62:7:62:10 | ss11 | |
+| stringstream.cpp:28:71:28:74 | call to basic_stringstream | stringstream.cpp:65:7:65:10 | ss11 | |
+| stringstream.cpp:28:77:28:80 | call to basic_stringstream | stringstream.cpp:63:7:63:10 | ss12 | |
+| stringstream.cpp:28:77:28:80 | call to basic_stringstream | stringstream.cpp:66:7:66:10 | ss12 | |
+| stringstream.cpp:28:83:28:86 | call to basic_stringstream | stringstream.cpp:64:7:64:10 | ss13 | |
+| stringstream.cpp:28:83:28:86 | call to basic_stringstream | stringstream.cpp:67:7:67:10 | ss13 | |
+| stringstream.cpp:29:16:29:21 | call to source | stringstream.cpp:29:16:29:24 | call to basic_string | TAINT |
+| stringstream.cpp:29:16:29:24 | call to basic_string | stringstream.cpp:35:14:35:14 | t | |
+| stringstream.cpp:31:7:31:9 | ref arg ss1 | stringstream.cpp:37:7:37:9 | ss1 | |
+| stringstream.cpp:31:7:31:9 | ref arg ss1 | stringstream.cpp:42:7:42:9 | ss1 | |
+| stringstream.cpp:31:7:31:9 | ss1 | stringstream.cpp:31:11:31:11 | call to operator<< | |
+| stringstream.cpp:31:14:31:19 | 1234 | stringstream.cpp:31:7:31:9 | ref arg ss1 | TAINT |
+| stringstream.cpp:31:14:31:19 | 1234 | stringstream.cpp:31:11:31:11 | call to operator<< | TAINT |
+| stringstream.cpp:32:7:32:9 | ref arg ss2 | stringstream.cpp:38:7:38:9 | ss2 | |
+| stringstream.cpp:32:7:32:9 | ref arg ss2 | stringstream.cpp:43:7:43:9 | ss2 | |
+| stringstream.cpp:32:7:32:9 | ss2 | stringstream.cpp:32:11:32:11 | call to operator<< | |
+| stringstream.cpp:32:14:32:19 | call to source | stringstream.cpp:32:7:32:9 | ref arg ss2 | TAINT |
+| stringstream.cpp:32:14:32:19 | call to source | stringstream.cpp:32:11:32:11 | call to operator<< | TAINT |
+| stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:39:7:39:9 | ss3 | |
+| stringstream.cpp:33:7:33:9 | ref arg ss3 | stringstream.cpp:44:7:44:9 | ss3 | |
+| stringstream.cpp:33:7:33:9 | ss3 | stringstream.cpp:33:11:33:11 | call to operator<< | |
+| stringstream.cpp:33:11:33:11 | call to operator<< | stringstream.cpp:33:20:33:20 | call to operator<< | |
+| stringstream.cpp:33:11:33:11 | ref arg call to operator<< | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT |
+| stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:7:33:9 | ref arg ss3 | TAINT |
+| stringstream.cpp:33:14:33:18 | 123 | stringstream.cpp:33:11:33:11 | call to operator<< | TAINT |
+| stringstream.cpp:33:23:33:28 | call to source | stringstream.cpp:33:11:33:11 | ref arg call to operator<< | TAINT |
+| stringstream.cpp:33:23:33:28 | call to source | stringstream.cpp:33:20:33:20 | call to operator<< | TAINT |
+| stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:40:7:40:9 | ss4 | |
+| stringstream.cpp:34:7:34:9 | ref arg ss4 | stringstream.cpp:45:7:45:9 | ss4 | |
+| stringstream.cpp:34:7:34:9 | ss4 | stringstream.cpp:34:11:34:11 | call to operator<< | |
+| stringstream.cpp:34:11:34:11 | call to operator<< | stringstream.cpp:34:23:34:23 | call to operator<< | |
+| stringstream.cpp:34:11:34:11 | ref arg call to operator<< | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT |
+| stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:7:34:9 | ref arg ss4 | TAINT |
+| stringstream.cpp:34:14:34:19 | call to source | stringstream.cpp:34:11:34:11 | call to operator<< | TAINT |
+| stringstream.cpp:34:26:34:30 | 456 | stringstream.cpp:34:11:34:11 | ref arg call to operator<< | TAINT |
+| stringstream.cpp:34:26:34:30 | 456 | stringstream.cpp:34:23:34:23 | call to operator<< | TAINT |
+| stringstream.cpp:35:7:35:9 | ref arg ss5 | stringstream.cpp:41:7:41:9 | ss5 | |
+| stringstream.cpp:35:7:35:9 | ref arg ss5 | stringstream.cpp:46:7:46:9 | ss5 | |
+| stringstream.cpp:35:7:35:9 | ss5 | stringstream.cpp:35:11:35:11 | call to operator<< | |
+| stringstream.cpp:35:14:35:14 | t | stringstream.cpp:35:7:35:9 | ref arg ss5 | TAINT |
+| stringstream.cpp:35:14:35:14 | t | stringstream.cpp:35:11:35:11 | call to operator<< | TAINT |
+| stringstream.cpp:42:7:42:9 | ss1 | stringstream.cpp:42:11:42:13 | call to str | TAINT |
+| stringstream.cpp:43:7:43:9 | ss2 | stringstream.cpp:43:11:43:13 | call to str | TAINT |
+| stringstream.cpp:44:7:44:9 | ss3 | stringstream.cpp:44:11:44:13 | call to str | TAINT |
+| stringstream.cpp:45:7:45:9 | ss4 | stringstream.cpp:45:11:45:13 | call to str | TAINT |
+| stringstream.cpp:46:7:46:9 | ss5 | stringstream.cpp:46:11:46:13 | call to str | TAINT |
+| stringstream.cpp:48:2:48:4 | ref arg ss6 | stringstream.cpp:49:2:49:4 | ss6 | |
+| stringstream.cpp:48:2:48:4 | ref arg ss6 | stringstream.cpp:52:7:52:9 | ss6 | |
+| stringstream.cpp:48:2:48:4 | ss6 | stringstream.cpp:48:6:48:8 | call to str | TAINT |
+| stringstream.cpp:48:10:48:14 | abc | stringstream.cpp:48:10:48:14 | call to basic_string | TAINT |
+| stringstream.cpp:48:10:48:14 | call to basic_string | stringstream.cpp:48:2:48:4 | ref arg ss6 | TAINT |
+| stringstream.cpp:49:2:49:4 | ref arg ss6 | stringstream.cpp:52:7:52:9 | ss6 | |
+| stringstream.cpp:49:2:49:4 | ss6 | stringstream.cpp:49:6:49:8 | call to str | TAINT |
+| stringstream.cpp:49:10:49:15 | call to source | stringstream.cpp:49:10:49:17 | call to basic_string | TAINT |
+| stringstream.cpp:49:10:49:17 | call to basic_string | stringstream.cpp:49:2:49:4 | ref arg ss6 | TAINT |
+| stringstream.cpp:50:2:50:4 | ref arg ss7 | stringstream.cpp:51:2:51:4 | ss7 | |
+| stringstream.cpp:50:2:50:4 | ref arg ss7 | stringstream.cpp:53:7:53:9 | ss7 | |
+| stringstream.cpp:50:2:50:4 | ss7 | stringstream.cpp:50:6:50:8 | call to str | TAINT |
+| stringstream.cpp:50:10:50:15 | call to source | stringstream.cpp:50:10:50:17 | call to basic_string | TAINT |
+| stringstream.cpp:50:10:50:17 | call to basic_string | stringstream.cpp:50:2:50:4 | ref arg ss7 | TAINT |
+| stringstream.cpp:51:2:51:4 | ref arg ss7 | stringstream.cpp:53:7:53:9 | ss7 | |
+| stringstream.cpp:51:2:51:4 | ss7 | stringstream.cpp:51:6:51:8 | call to str | TAINT |
+| stringstream.cpp:51:10:51:14 | abc | stringstream.cpp:51:10:51:14 | call to basic_string | TAINT |
+| stringstream.cpp:51:10:51:14 | call to basic_string | stringstream.cpp:51:2:51:4 | ref arg ss7 | TAINT |
+| stringstream.cpp:55:7:55:9 | ref arg ss8 | stringstream.cpp:58:7:58:9 | ss8 | |
+| stringstream.cpp:55:7:55:9 | ss8 | stringstream.cpp:55:11:55:13 | call to put | |
+| stringstream.cpp:55:15:55:17 | 97 | stringstream.cpp:55:7:55:9 | ref arg ss8 | TAINT |
+| stringstream.cpp:55:15:55:17 | 97 | stringstream.cpp:55:11:55:13 | call to put | TAINT |
+| stringstream.cpp:56:7:56:9 | ref arg ss9 | stringstream.cpp:59:7:59:9 | ss9 | |
+| stringstream.cpp:56:7:56:9 | ss9 | stringstream.cpp:56:11:56:13 | call to put | |
+| stringstream.cpp:56:15:56:29 | call to source | stringstream.cpp:56:7:56:9 | ref arg ss9 | TAINT |
+| stringstream.cpp:56:15:56:29 | call to source | stringstream.cpp:56:11:56:13 | call to put | TAINT |
+| stringstream.cpp:57:7:57:10 | ref arg ss10 | stringstream.cpp:60:7:60:10 | ss10 | |
+| stringstream.cpp:57:7:57:10 | ss10 | stringstream.cpp:57:12:57:14 | call to put | |
+| stringstream.cpp:57:12:57:14 | call to put | stringstream.cpp:57:21:57:23 | call to put | |
+| stringstream.cpp:57:12:57:14 | ref arg call to put | stringstream.cpp:57:7:57:10 | ref arg ss10 | TAINT |
+| stringstream.cpp:57:16:57:18 | 97 | stringstream.cpp:57:7:57:10 | ref arg ss10 | TAINT |
+| stringstream.cpp:57:16:57:18 | 97 | stringstream.cpp:57:12:57:14 | call to put | TAINT |
+| stringstream.cpp:57:21:57:23 | call to put | stringstream.cpp:57:44:57:46 | call to put | |
+| stringstream.cpp:57:21:57:23 | ref arg call to put | stringstream.cpp:57:12:57:14 | ref arg call to put | TAINT |
+| stringstream.cpp:57:25:57:39 | call to source | stringstream.cpp:57:12:57:14 | ref arg call to put | TAINT |
+| stringstream.cpp:57:25:57:39 | call to source | stringstream.cpp:57:21:57:23 | call to put | TAINT |
+| stringstream.cpp:57:48:57:50 | 122 | stringstream.cpp:57:21:57:23 | ref arg call to put | TAINT |
+| stringstream.cpp:57:48:57:50 | 122 | stringstream.cpp:57:44:57:46 | call to put | TAINT |
+| stringstream.cpp:62:7:62:10 | ref arg ss11 | stringstream.cpp:65:7:65:10 | ss11 | |
+| stringstream.cpp:62:7:62:10 | ss11 | stringstream.cpp:62:12:62:16 | call to write | |
+| stringstream.cpp:62:18:62:24 | begin | stringstream.cpp:62:7:62:10 | ref arg ss11 | TAINT |
+| stringstream.cpp:62:18:62:24 | begin | stringstream.cpp:62:12:62:16 | call to write | TAINT |
+| stringstream.cpp:63:7:63:10 | ref arg ss12 | stringstream.cpp:66:7:66:10 | ss12 | |
+| stringstream.cpp:63:7:63:10 | ss12 | stringstream.cpp:63:12:63:16 | call to write | |
+| stringstream.cpp:63:18:63:23 | call to source | stringstream.cpp:63:7:63:10 | ref arg ss12 | TAINT |
+| stringstream.cpp:63:18:63:23 | call to source | stringstream.cpp:63:12:63:16 | call to write | TAINT |
+| stringstream.cpp:64:7:64:10 | ref arg ss13 | stringstream.cpp:67:7:67:10 | ss13 | |
+| stringstream.cpp:64:7:64:10 | ss13 | stringstream.cpp:64:12:64:16 | call to write | |
+| stringstream.cpp:64:12:64:16 | call to write | stringstream.cpp:64:30:64:34 | call to write | |
+| stringstream.cpp:64:12:64:16 | ref arg call to write | stringstream.cpp:64:7:64:10 | ref arg ss13 | TAINT |
+| stringstream.cpp:64:18:64:24 | begin | stringstream.cpp:64:7:64:10 | ref arg ss13 | TAINT |
+| stringstream.cpp:64:18:64:24 | begin | stringstream.cpp:64:12:64:16 | call to write | TAINT |
+| stringstream.cpp:64:30:64:34 | call to write | stringstream.cpp:64:54:64:58 | call to write | |
+| stringstream.cpp:64:30:64:34 | ref arg call to write | stringstream.cpp:64:12:64:16 | ref arg call to write | TAINT |
+| stringstream.cpp:64:36:64:41 | call to source | stringstream.cpp:64:12:64:16 | ref arg call to write | TAINT |
+| stringstream.cpp:64:36:64:41 | call to source | stringstream.cpp:64:30:64:34 | call to write | TAINT |
+| stringstream.cpp:64:60:64:64 | end | stringstream.cpp:64:30:64:34 | ref arg call to write | TAINT |
+| stringstream.cpp:64:60:64:64 | end | stringstream.cpp:64:54:64:58 | call to write | TAINT |
+| stringstream.cpp:70:32:70:37 | source | stringstream.cpp:76:14:76:19 | source | |
+| stringstream.cpp:72:20:72:22 | call to basic_stringstream | stringstream.cpp:75:7:75:9 | ss1 | |
+| stringstream.cpp:72:20:72:22 | call to basic_stringstream | stringstream.cpp:77:7:77:9 | ss1 | |
+| stringstream.cpp:72:20:72:22 | call to basic_stringstream | stringstream.cpp:80:7:80:9 | ss1 | |
+| stringstream.cpp:72:20:72:22 | call to basic_stringstream | stringstream.cpp:82:7:82:9 | ss1 | |
+| stringstream.cpp:72:25:72:27 | call to basic_stringstream | stringstream.cpp:76:7:76:9 | ss2 | |
+| stringstream.cpp:72:25:72:27 | call to basic_stringstream | stringstream.cpp:78:7:78:9 | ss2 | |
+| stringstream.cpp:72:25:72:27 | call to basic_stringstream | stringstream.cpp:81:7:81:9 | ss2 | |
+| stringstream.cpp:72:25:72:27 | call to basic_stringstream | stringstream.cpp:83:7:83:9 | ss2 | |
+| stringstream.cpp:73:10:73:11 | 0 | stringstream.cpp:77:14:77:15 | v1 | |
+| stringstream.cpp:73:10:73:11 | 0 | stringstream.cpp:84:7:84:8 | v1 | |
+| stringstream.cpp:73:18:73:19 | 0 | stringstream.cpp:78:14:78:15 | v2 | |
+| stringstream.cpp:73:18:73:19 | 0 | stringstream.cpp:85:7:85:8 | v2 | |
+| stringstream.cpp:75:7:75:9 | ref arg ss1 | stringstream.cpp:77:7:77:9 | ss1 | |
+| stringstream.cpp:75:7:75:9 | ref arg ss1 | stringstream.cpp:80:7:80:9 | ss1 | |
+| stringstream.cpp:75:7:75:9 | ref arg ss1 | stringstream.cpp:82:7:82:9 | ss1 | |
+| stringstream.cpp:75:7:75:9 | ss1 | stringstream.cpp:75:11:75:11 | call to operator<< | |
+| stringstream.cpp:75:14:75:17 | 1234 | stringstream.cpp:75:7:75:9 | ref arg ss1 | TAINT |
+| stringstream.cpp:75:14:75:17 | 1234 | stringstream.cpp:75:11:75:11 | call to operator<< | TAINT |
+| stringstream.cpp:76:7:76:9 | ref arg ss2 | stringstream.cpp:78:7:78:9 | ss2 | |
+| stringstream.cpp:76:7:76:9 | ref arg ss2 | stringstream.cpp:81:7:81:9 | ss2 | |
+| stringstream.cpp:76:7:76:9 | ref arg ss2 | stringstream.cpp:83:7:83:9 | ss2 | |
+| stringstream.cpp:76:7:76:9 | ss2 | stringstream.cpp:76:11:76:11 | call to operator<< | |
+| stringstream.cpp:76:14:76:19 | source | stringstream.cpp:76:7:76:9 | ref arg ss2 | TAINT |
+| stringstream.cpp:76:14:76:19 | source | stringstream.cpp:76:11:76:11 | call to operator<< | TAINT |
+| stringstream.cpp:77:7:77:9 | ref arg ss1 | stringstream.cpp:80:7:80:9 | ss1 | |
+| stringstream.cpp:77:7:77:9 | ref arg ss1 | stringstream.cpp:82:7:82:9 | ss1 | |
+| stringstream.cpp:77:7:77:9 | ss1 | stringstream.cpp:77:11:77:11 | call to operator>> | |
+| stringstream.cpp:77:7:77:9 | ss1 | stringstream.cpp:77:14:77:15 | ref arg v1 | TAINT |
+| stringstream.cpp:77:14:77:15 | ref arg v1 | stringstream.cpp:84:7:84:8 | v1 | |
+| stringstream.cpp:78:7:78:9 | ref arg ss2 | stringstream.cpp:81:7:81:9 | ss2 | |
+| stringstream.cpp:78:7:78:9 | ref arg ss2 | stringstream.cpp:83:7:83:9 | ss2 | |
+| stringstream.cpp:78:7:78:9 | ss2 | stringstream.cpp:78:11:78:11 | call to operator>> | |
+| stringstream.cpp:78:7:78:9 | ss2 | stringstream.cpp:78:14:78:15 | ref arg v2 | TAINT |
+| stringstream.cpp:78:14:78:15 | ref arg v2 | stringstream.cpp:85:7:85:8 | v2 | |
+| stringstream.cpp:82:7:82:9 | ss1 | stringstream.cpp:82:11:82:13 | call to str | TAINT |
+| stringstream.cpp:83:7:83:9 | ss2 | stringstream.cpp:83:11:83:13 | call to str | TAINT |
+| stringstream.cpp:90:18:90:23 | call to basic_string | stringstream.cpp:92:24:92:25 | s1 | |
+| stringstream.cpp:90:19:90:23 | abc | stringstream.cpp:90:18:90:23 | call to basic_string | TAINT |
+| stringstream.cpp:91:18:91:26 | call to basic_string | stringstream.cpp:93:24:93:25 | s2 | |
+| stringstream.cpp:91:19:91:24 | call to source | stringstream.cpp:91:18:91:26 | call to basic_string | TAINT |
+| stringstream.cpp:92:24:92:25 | s1 | stringstream.cpp:92:24:92:26 | call to basic_stringstream | TAINT |
+| stringstream.cpp:92:24:92:26 | call to basic_stringstream | stringstream.cpp:102:7:102:9 | ss1 | |
+| stringstream.cpp:93:24:93:25 | s2 | stringstream.cpp:93:24:93:26 | call to basic_stringstream | TAINT |
+| stringstream.cpp:93:24:93:26 | call to basic_stringstream | stringstream.cpp:103:7:103:9 | ss2 | |
+| stringstream.cpp:94:25:94:49 | call to basic_stringstream | stringstream.cpp:104:7:104:9 | ss3 | |
+| stringstream.cpp:94:44:94:48 | abc | stringstream.cpp:94:44:94:48 | call to basic_string | TAINT |
+| stringstream.cpp:94:44:94:48 | call to basic_string | stringstream.cpp:94:25:94:49 | call to basic_stringstream | TAINT |
+| stringstream.cpp:95:25:95:52 | call to basic_stringstream | stringstream.cpp:105:7:105:9 | ss4 | |
+| stringstream.cpp:95:44:95:49 | call to source | stringstream.cpp:95:44:95:51 | call to basic_string | TAINT |
+| stringstream.cpp:95:44:95:51 | call to basic_string | stringstream.cpp:95:25:95:52 | call to basic_stringstream | TAINT |
+| stringstream.cpp:96:20:96:22 | call to basic_stringstream | stringstream.cpp:99:7:99:9 | ss5 | |
+| stringstream.cpp:96:20:96:22 | call to basic_stringstream | stringstream.cpp:106:7:106:9 | ss5 | |
+| stringstream.cpp:97:20:97:22 | call to basic_stringstream | stringstream.cpp:100:7:100:9 | ss6 | |
+| stringstream.cpp:97:20:97:22 | call to basic_stringstream | stringstream.cpp:107:7:107:9 | ss6 | |
+| stringstream.cpp:99:7:99:9 | ref arg ss5 | stringstream.cpp:106:7:106:9 | ss5 | |
+| stringstream.cpp:99:13:99:36 | call to basic_stringstream | stringstream.cpp:99:7:99:9 | ref arg ss5 | TAINT |
+| stringstream.cpp:99:13:99:36 | call to basic_stringstream | stringstream.cpp:99:11:99:11 | call to operator= | TAINT |
+| stringstream.cpp:99:31:99:35 | abc | stringstream.cpp:99:31:99:35 | call to basic_string | TAINT |
+| stringstream.cpp:99:31:99:35 | call to basic_string | stringstream.cpp:99:13:99:36 | call to basic_stringstream | TAINT |
+| stringstream.cpp:100:7:100:9 | ref arg ss6 | stringstream.cpp:107:7:107:9 | ss6 | |
+| stringstream.cpp:100:13:100:39 | call to basic_stringstream | stringstream.cpp:100:7:100:9 | ref arg ss6 | TAINT |
+| stringstream.cpp:100:13:100:39 | call to basic_stringstream | stringstream.cpp:100:11:100:11 | call to operator= | TAINT |
+| stringstream.cpp:100:31:100:36 | call to source | stringstream.cpp:100:31:100:38 | call to basic_string | TAINT |
+| stringstream.cpp:100:31:100:38 | call to basic_string | stringstream.cpp:100:13:100:39 | call to basic_stringstream | TAINT |
+| stringstream.cpp:112:24:112:28 | abc | stringstream.cpp:112:24:112:28 | call to basic_string | TAINT |
+| stringstream.cpp:112:24:112:28 | call to basic_string | stringstream.cpp:112:24:112:29 | call to basic_stringstream | TAINT |
+| stringstream.cpp:112:24:112:29 | call to basic_stringstream | stringstream.cpp:117:2:117:4 | ss1 | |
+| stringstream.cpp:112:24:112:29 | call to basic_stringstream | stringstream.cpp:120:7:120:9 | ss1 | |
+| stringstream.cpp:113:24:113:29 | call to source | stringstream.cpp:113:24:113:31 | call to basic_string | TAINT |
+| stringstream.cpp:113:24:113:31 | call to basic_string | stringstream.cpp:113:24:113:32 | call to basic_stringstream | TAINT |
+| stringstream.cpp:113:24:113:32 | call to basic_stringstream | stringstream.cpp:117:11:117:13 | ss2 | |
+| stringstream.cpp:113:24:113:32 | call to basic_stringstream | stringstream.cpp:121:7:121:9 | ss2 | |
+| stringstream.cpp:114:24:114:28 | abc | stringstream.cpp:114:24:114:28 | call to basic_string | TAINT |
+| stringstream.cpp:114:24:114:28 | call to basic_string | stringstream.cpp:114:24:114:29 | call to basic_stringstream | TAINT |
+| stringstream.cpp:114:24:114:29 | call to basic_stringstream | stringstream.cpp:118:11:118:13 | ss3 | |
+| stringstream.cpp:114:24:114:29 | call to basic_stringstream | stringstream.cpp:122:7:122:9 | ss3 | |
+| stringstream.cpp:115:24:115:29 | call to source | stringstream.cpp:115:24:115:31 | call to basic_string | TAINT |
+| stringstream.cpp:115:24:115:31 | call to basic_string | stringstream.cpp:115:24:115:32 | call to basic_stringstream | TAINT |
+| stringstream.cpp:115:24:115:32 | call to basic_stringstream | stringstream.cpp:118:2:118:4 | ss4 | |
+| stringstream.cpp:115:24:115:32 | call to basic_stringstream | stringstream.cpp:123:7:123:9 | ss4 | |
+| stringstream.cpp:117:2:117:4 | ref arg ss1 | stringstream.cpp:120:7:120:9 | ss1 | |
+| stringstream.cpp:117:2:117:4 | ss1 | stringstream.cpp:117:11:117:13 | ref arg ss2 | TAINT |
+| stringstream.cpp:117:11:117:13 | ref arg ss2 | stringstream.cpp:121:7:121:9 | ss2 | |
+| stringstream.cpp:117:11:117:13 | ss2 | stringstream.cpp:117:2:117:4 | ref arg ss1 | TAINT |
+| stringstream.cpp:118:2:118:4 | ref arg ss4 | stringstream.cpp:123:7:123:9 | ss4 | |
+| stringstream.cpp:118:2:118:4 | ss4 | stringstream.cpp:118:11:118:13 | ref arg ss3 | TAINT |
+| stringstream.cpp:118:11:118:13 | ref arg ss3 | stringstream.cpp:122:7:122:9 | ss3 | |
+| stringstream.cpp:118:11:118:13 | ss3 | stringstream.cpp:118:2:118:4 | ref arg ss4 | TAINT |
+| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:142:7:142:9 | ss1 | |
+| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:145:7:145:9 | ss1 | |
+| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:153:7:153:9 | ss1 | |
+| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:161:7:161:9 | ss1 | |
+| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:163:7:163:9 | ss1 | |
+| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:165:7:165:9 | ss1 | |
+| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:174:12:174:14 | ss1 | |
+| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:176:12:176:14 | ss1 | |
+| stringstream.cpp:128:20:128:22 | call to basic_stringstream | stringstream.cpp:178:7:178:9 | ss1 | |
+| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:143:7:143:9 | ss2 | |
+| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:146:7:146:9 | ss2 | |
+| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:147:7:147:9 | ss2 | |
+| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:154:7:154:9 | ss2 | |
+| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:155:7:155:9 | ss2 | |
+| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:162:7:162:9 | ss2 | |
+| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:164:7:164:9 | ss2 | |
+| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:166:7:166:9 | ss2 | |
+| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:175:12:175:14 | ss2 | |
+| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:177:12:177:14 | ss2 | |
+| stringstream.cpp:128:25:128:27 | call to basic_stringstream | stringstream.cpp:179:7:179:9 | ss2 | |
+| stringstream.cpp:129:14:129:15 | call to basic_string | stringstream.cpp:145:14:145:15 | s1 | |
+| stringstream.cpp:129:14:129:15 | call to basic_string | stringstream.cpp:148:7:148:8 | s1 | |
+| stringstream.cpp:129:18:129:19 | call to basic_string | stringstream.cpp:146:14:146:15 | s2 | |
+| stringstream.cpp:129:18:129:19 | call to basic_string | stringstream.cpp:149:7:149:8 | s2 | |
+| stringstream.cpp:129:22:129:23 | call to basic_string | stringstream.cpp:147:14:147:15 | s3 | |
+| stringstream.cpp:129:22:129:23 | call to basic_string | stringstream.cpp:150:7:150:8 | s3 | |
+| stringstream.cpp:129:26:129:27 | call to basic_string | stringstream.cpp:147:20:147:21 | s4 | |
+| stringstream.cpp:129:26:129:27 | call to basic_string | stringstream.cpp:151:7:151:8 | s4 | |
+| stringstream.cpp:130:16:130:19 | {...} | stringstream.cpp:153:14:153:15 | b1 | |
+| stringstream.cpp:130:16:130:19 | {...} | stringstream.cpp:156:7:156:8 | b1 | |
+| stringstream.cpp:130:18:130:18 | 0 | stringstream.cpp:130:16:130:19 | {...} | TAINT |
+| stringstream.cpp:131:16:131:19 | {...} | stringstream.cpp:154:14:154:15 | b2 | |
+| stringstream.cpp:131:16:131:19 | {...} | stringstream.cpp:157:7:157:8 | b2 | |
+| stringstream.cpp:131:18:131:18 | 0 | stringstream.cpp:131:16:131:19 | {...} | TAINT |
+| stringstream.cpp:132:16:132:19 | {...} | stringstream.cpp:155:14:155:15 | b3 | |
+| stringstream.cpp:132:16:132:19 | {...} | stringstream.cpp:158:7:158:8 | b3 | |
+| stringstream.cpp:132:18:132:18 | 0 | stringstream.cpp:132:16:132:19 | {...} | TAINT |
+| stringstream.cpp:133:16:133:19 | {...} | stringstream.cpp:155:20:155:21 | b4 | |
+| stringstream.cpp:133:16:133:19 | {...} | stringstream.cpp:159:7:159:8 | b4 | |
+| stringstream.cpp:133:18:133:18 | 0 | stringstream.cpp:133:16:133:19 | {...} | TAINT |
+| stringstream.cpp:134:16:134:19 | {...} | stringstream.cpp:161:16:161:17 | b5 | |
+| stringstream.cpp:134:16:134:19 | {...} | stringstream.cpp:167:7:167:8 | b5 | |
+| stringstream.cpp:134:18:134:18 | 0 | stringstream.cpp:134:16:134:19 | {...} | TAINT |
+| stringstream.cpp:135:16:135:19 | {...} | stringstream.cpp:162:16:162:17 | b6 | |
+| stringstream.cpp:135:16:135:19 | {...} | stringstream.cpp:168:7:168:8 | b6 | |
+| stringstream.cpp:135:18:135:18 | 0 | stringstream.cpp:135:16:135:19 | {...} | TAINT |
+| stringstream.cpp:136:16:136:19 | {...} | stringstream.cpp:163:20:163:21 | b7 | |
+| stringstream.cpp:136:16:136:19 | {...} | stringstream.cpp:169:7:169:8 | b7 | |
+| stringstream.cpp:136:18:136:18 | 0 | stringstream.cpp:136:16:136:19 | {...} | TAINT |
+| stringstream.cpp:137:16:137:19 | {...} | stringstream.cpp:164:20:164:21 | b8 | |
+| stringstream.cpp:137:16:137:19 | {...} | stringstream.cpp:170:7:170:8 | b8 | |
+| stringstream.cpp:137:18:137:18 | 0 | stringstream.cpp:137:16:137:19 | {...} | TAINT |
+| stringstream.cpp:138:16:138:19 | {...} | stringstream.cpp:165:15:165:16 | b9 | |
+| stringstream.cpp:138:16:138:19 | {...} | stringstream.cpp:171:7:171:8 | b9 | |
+| stringstream.cpp:138:18:138:18 | 0 | stringstream.cpp:138:16:138:19 | {...} | TAINT |
+| stringstream.cpp:139:17:139:20 | {...} | stringstream.cpp:166:15:166:17 | b10 | |
+| stringstream.cpp:139:17:139:20 | {...} | stringstream.cpp:172:7:172:9 | b10 | |
+| stringstream.cpp:139:19:139:19 | 0 | stringstream.cpp:139:17:139:20 | {...} | TAINT |
+| stringstream.cpp:140:44:140:44 | 0 | stringstream.cpp:178:15:178:16 | c5 | |
+| stringstream.cpp:140:44:140:44 | 0 | stringstream.cpp:184:7:184:8 | c5 | |
+| stringstream.cpp:140:52:140:52 | 0 | stringstream.cpp:179:15:179:16 | c6 | |
+| stringstream.cpp:140:52:140:52 | 0 | stringstream.cpp:185:7:185:8 | c6 | |
+| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:145:7:145:9 | ss1 | |
+| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:153:7:153:9 | ss1 | |
+| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:161:7:161:9 | ss1 | |
+| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:163:7:163:9 | ss1 | |
+| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:165:7:165:9 | ss1 | |
+| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | |
+| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | |
+| stringstream.cpp:142:7:142:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | |
+| stringstream.cpp:142:7:142:9 | ss1 | stringstream.cpp:142:11:142:11 | call to operator<< | |
+| stringstream.cpp:142:14:142:18 | abc | stringstream.cpp:142:7:142:9 | ref arg ss1 | TAINT |
+| stringstream.cpp:142:14:142:18 | abc | stringstream.cpp:142:11:142:11 | call to operator<< | TAINT |
+| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:146:7:146:9 | ss2 | |
+| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:147:7:147:9 | ss2 | |
+| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:154:7:154:9 | ss2 | |
+| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:155:7:155:9 | ss2 | |
+| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:162:7:162:9 | ss2 | |
+| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | |
+| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | |
+| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | |
+| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | |
+| stringstream.cpp:143:7:143:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
+| stringstream.cpp:143:7:143:9 | ss2 | stringstream.cpp:143:11:143:11 | call to operator<< | |
+| stringstream.cpp:143:14:143:19 | call to source | stringstream.cpp:143:7:143:9 | ref arg ss2 | TAINT |
+| stringstream.cpp:143:14:143:19 | call to source | stringstream.cpp:143:11:143:11 | call to operator<< | TAINT |
+| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:153:7:153:9 | ss1 | |
+| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:161:7:161:9 | ss1 | |
+| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:163:7:163:9 | ss1 | |
+| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:165:7:165:9 | ss1 | |
+| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | |
+| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | |
+| stringstream.cpp:145:7:145:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | |
+| stringstream.cpp:145:7:145:9 | ss1 | stringstream.cpp:145:11:145:11 | call to operator>> | |
+| stringstream.cpp:145:7:145:9 | ss1 | stringstream.cpp:145:14:145:15 | ref arg s1 | TAINT |
+| stringstream.cpp:145:14:145:15 | ref arg s1 | stringstream.cpp:148:7:148:8 | s1 | |
+| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:147:7:147:9 | ss2 | |
+| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:154:7:154:9 | ss2 | |
+| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:155:7:155:9 | ss2 | |
+| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:162:7:162:9 | ss2 | |
+| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | |
+| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | |
+| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | |
+| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | |
+| stringstream.cpp:146:7:146:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
+| stringstream.cpp:146:7:146:9 | ss2 | stringstream.cpp:146:11:146:11 | call to operator>> | |
+| stringstream.cpp:146:7:146:9 | ss2 | stringstream.cpp:146:14:146:15 | ref arg s2 | TAINT |
+| stringstream.cpp:146:14:146:15 | ref arg s2 | stringstream.cpp:149:7:149:8 | s2 | |
+| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:154:7:154:9 | ss2 | |
+| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:155:7:155:9 | ss2 | |
+| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:162:7:162:9 | ss2 | |
+| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | |
+| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | |
+| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | |
+| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | |
+| stringstream.cpp:147:7:147:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
+| stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:11:147:11 | call to operator>> | |
+| stringstream.cpp:147:7:147:9 | ss2 | stringstream.cpp:147:14:147:15 | ref arg s3 | TAINT |
+| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:17:147:17 | call to operator>> | |
+| stringstream.cpp:147:11:147:11 | call to operator>> | stringstream.cpp:147:20:147:21 | ref arg s4 | TAINT |
+| stringstream.cpp:147:11:147:11 | ref arg call to operator>> | stringstream.cpp:147:7:147:9 | ref arg ss2 | TAINT |
+| stringstream.cpp:147:14:147:15 | ref arg s3 | stringstream.cpp:150:7:150:8 | s3 | |
+| stringstream.cpp:147:20:147:21 | ref arg s4 | stringstream.cpp:151:7:151:8 | s4 | |
+| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:161:7:161:9 | ss1 | |
+| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:163:7:163:9 | ss1 | |
+| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:165:7:165:9 | ss1 | |
+| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | |
+| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | |
+| stringstream.cpp:153:7:153:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | |
+| stringstream.cpp:153:7:153:9 | ss1 | stringstream.cpp:153:11:153:11 | call to operator>> | |
+| stringstream.cpp:153:7:153:9 | ss1 | stringstream.cpp:153:14:153:15 | ref arg b1 | TAINT |
+| stringstream.cpp:153:14:153:15 | ref arg b1 | stringstream.cpp:156:7:156:8 | b1 | |
+| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:155:7:155:9 | ss2 | |
+| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:162:7:162:9 | ss2 | |
+| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | |
+| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | |
+| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | |
+| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | |
+| stringstream.cpp:154:7:154:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
+| stringstream.cpp:154:7:154:9 | ss2 | stringstream.cpp:154:11:154:11 | call to operator>> | |
+| stringstream.cpp:154:7:154:9 | ss2 | stringstream.cpp:154:14:154:15 | ref arg b2 | TAINT |
+| stringstream.cpp:154:14:154:15 | ref arg b2 | stringstream.cpp:157:7:157:8 | b2 | |
+| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:162:7:162:9 | ss2 | |
+| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | |
+| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | |
+| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | |
+| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | |
+| stringstream.cpp:155:7:155:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
+| stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:11:155:11 | call to operator>> | |
+| stringstream.cpp:155:7:155:9 | ss2 | stringstream.cpp:155:14:155:15 | ref arg b3 | TAINT |
+| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:17:155:17 | call to operator>> | |
+| stringstream.cpp:155:11:155:11 | call to operator>> | stringstream.cpp:155:20:155:21 | ref arg b4 | TAINT |
+| stringstream.cpp:155:11:155:11 | ref arg call to operator>> | stringstream.cpp:155:7:155:9 | ref arg ss2 | TAINT |
+| stringstream.cpp:155:14:155:15 | ref arg b3 | stringstream.cpp:158:7:158:8 | b3 | |
+| stringstream.cpp:155:20:155:21 | ref arg b4 | stringstream.cpp:159:7:159:8 | b4 | |
+| stringstream.cpp:156:7:156:8 | b1 | stringstream.cpp:156:7:156:8 | call to basic_string | TAINT |
+| stringstream.cpp:157:7:157:8 | b2 | stringstream.cpp:157:7:157:8 | call to basic_string | TAINT |
+| stringstream.cpp:158:7:158:8 | b3 | stringstream.cpp:158:7:158:8 | call to basic_string | TAINT |
+| stringstream.cpp:159:7:159:8 | b4 | stringstream.cpp:159:7:159:8 | call to basic_string | TAINT |
+| stringstream.cpp:161:7:161:9 | ref arg ss1 | stringstream.cpp:163:7:163:9 | ss1 | |
+| stringstream.cpp:161:7:161:9 | ref arg ss1 | stringstream.cpp:165:7:165:9 | ss1 | |
+| stringstream.cpp:161:7:161:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | |
+| stringstream.cpp:161:7:161:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | |
+| stringstream.cpp:161:7:161:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | |
+| stringstream.cpp:161:7:161:9 | ss1 | stringstream.cpp:161:11:161:14 | call to read | |
+| stringstream.cpp:161:7:161:9 | ss1 | stringstream.cpp:161:16:161:17 | ref arg b5 | TAINT |
+| stringstream.cpp:161:16:161:17 | ref arg b5 | stringstream.cpp:167:7:167:8 | b5 | |
+| stringstream.cpp:162:7:162:9 | ref arg ss2 | stringstream.cpp:164:7:164:9 | ss2 | |
+| stringstream.cpp:162:7:162:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | |
+| stringstream.cpp:162:7:162:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | |
+| stringstream.cpp:162:7:162:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | |
+| stringstream.cpp:162:7:162:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
+| stringstream.cpp:162:7:162:9 | ss2 | stringstream.cpp:162:11:162:14 | call to read | |
+| stringstream.cpp:162:7:162:9 | ss2 | stringstream.cpp:162:16:162:17 | ref arg b6 | TAINT |
+| stringstream.cpp:162:16:162:17 | ref arg b6 | stringstream.cpp:168:7:168:8 | b6 | |
+| stringstream.cpp:163:7:163:9 | ref arg ss1 | stringstream.cpp:165:7:165:9 | ss1 | |
+| stringstream.cpp:163:7:163:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | |
+| stringstream.cpp:163:7:163:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | |
+| stringstream.cpp:163:7:163:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | |
+| stringstream.cpp:163:7:163:9 | ss1 | stringstream.cpp:163:20:163:21 | ref arg b7 | TAINT |
+| stringstream.cpp:163:20:163:21 | ref arg b7 | stringstream.cpp:169:7:169:8 | b7 | |
+| stringstream.cpp:164:7:164:9 | ref arg ss2 | stringstream.cpp:166:7:166:9 | ss2 | |
+| stringstream.cpp:164:7:164:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | |
+| stringstream.cpp:164:7:164:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | |
+| stringstream.cpp:164:7:164:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
+| stringstream.cpp:164:7:164:9 | ss2 | stringstream.cpp:164:20:164:21 | ref arg b8 | TAINT |
+| stringstream.cpp:164:20:164:21 | ref arg b8 | stringstream.cpp:170:7:170:8 | b8 | |
+| stringstream.cpp:165:7:165:9 | ref arg ss1 | stringstream.cpp:174:12:174:14 | ss1 | |
+| stringstream.cpp:165:7:165:9 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | |
+| stringstream.cpp:165:7:165:9 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | |
+| stringstream.cpp:165:7:165:9 | ss1 | stringstream.cpp:165:11:165:13 | call to get | |
+| stringstream.cpp:165:7:165:9 | ss1 | stringstream.cpp:165:15:165:16 | ref arg b9 | TAINT |
+| stringstream.cpp:165:15:165:16 | ref arg b9 | stringstream.cpp:171:7:171:8 | b9 | |
+| stringstream.cpp:166:7:166:9 | ref arg ss2 | stringstream.cpp:175:12:175:14 | ss2 | |
+| stringstream.cpp:166:7:166:9 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | |
+| stringstream.cpp:166:7:166:9 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
+| stringstream.cpp:166:7:166:9 | ss2 | stringstream.cpp:166:11:166:13 | call to get | |
+| stringstream.cpp:166:7:166:9 | ss2 | stringstream.cpp:166:15:166:17 | ref arg b10 | TAINT |
+| stringstream.cpp:166:15:166:17 | ref arg b10 | stringstream.cpp:172:7:172:9 | b10 | |
+| stringstream.cpp:167:7:167:8 | b5 | stringstream.cpp:167:7:167:8 | call to basic_string | TAINT |
+| stringstream.cpp:168:7:168:8 | b6 | stringstream.cpp:168:7:168:8 | call to basic_string | TAINT |
+| stringstream.cpp:169:7:169:8 | b7 | stringstream.cpp:169:7:169:8 | call to basic_string | TAINT |
+| stringstream.cpp:170:7:170:8 | b8 | stringstream.cpp:170:7:170:8 | call to basic_string | TAINT |
+| stringstream.cpp:171:7:171:8 | b9 | stringstream.cpp:171:7:171:8 | call to basic_string | TAINT |
+| stringstream.cpp:172:7:172:9 | b10 | stringstream.cpp:172:7:172:9 | call to basic_string | TAINT |
+| stringstream.cpp:174:7:174:8 | c1 | stringstream.cpp:174:7:174:20 | ... = ... | |
+| stringstream.cpp:174:12:174:14 | ref arg ss1 | stringstream.cpp:176:12:176:14 | ss1 | |
+| stringstream.cpp:174:12:174:14 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | |
+| stringstream.cpp:174:12:174:14 | ss1 | stringstream.cpp:174:16:174:18 | call to get | TAINT |
+| stringstream.cpp:174:16:174:18 | call to get | stringstream.cpp:174:7:174:20 | ... = ... | |
+| stringstream.cpp:174:16:174:18 | call to get | stringstream.cpp:180:7:180:8 | c1 | |
+| stringstream.cpp:175:7:175:8 | c2 | stringstream.cpp:175:7:175:20 | ... = ... | |
+| stringstream.cpp:175:12:175:14 | ref arg ss2 | stringstream.cpp:177:12:177:14 | ss2 | |
+| stringstream.cpp:175:12:175:14 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
+| stringstream.cpp:175:12:175:14 | ss2 | stringstream.cpp:175:16:175:18 | call to get | TAINT |
+| stringstream.cpp:175:16:175:18 | call to get | stringstream.cpp:175:7:175:20 | ... = ... | |
+| stringstream.cpp:175:16:175:18 | call to get | stringstream.cpp:181:7:181:8 | c2 | |
+| stringstream.cpp:176:7:176:8 | c3 | stringstream.cpp:176:7:176:21 | ... = ... | |
+| stringstream.cpp:176:12:176:14 | ref arg ss1 | stringstream.cpp:178:7:178:9 | ss1 | |
+| stringstream.cpp:176:12:176:14 | ss1 | stringstream.cpp:176:16:176:19 | call to peek | TAINT |
+| stringstream.cpp:176:16:176:19 | call to peek | stringstream.cpp:176:7:176:21 | ... = ... | |
+| stringstream.cpp:176:16:176:19 | call to peek | stringstream.cpp:182:7:182:8 | c3 | |
+| stringstream.cpp:177:7:177:8 | c4 | stringstream.cpp:177:7:177:21 | ... = ... | |
+| stringstream.cpp:177:12:177:14 | ref arg ss2 | stringstream.cpp:179:7:179:9 | ss2 | |
+| stringstream.cpp:177:12:177:14 | ss2 | stringstream.cpp:177:16:177:19 | call to peek | TAINT |
+| stringstream.cpp:177:16:177:19 | call to peek | stringstream.cpp:177:7:177:21 | ... = ... | |
+| stringstream.cpp:177:16:177:19 | call to peek | stringstream.cpp:183:7:183:8 | c4 | |
+| stringstream.cpp:178:7:178:9 | ss1 | stringstream.cpp:178:11:178:13 | call to get | |
+| stringstream.cpp:178:7:178:9 | ss1 | stringstream.cpp:178:15:178:16 | ref arg c5 | TAINT |
+| stringstream.cpp:178:15:178:16 | ref arg c5 | stringstream.cpp:184:7:184:8 | c5 | |
+| stringstream.cpp:179:7:179:9 | ss2 | stringstream.cpp:179:11:179:13 | call to get | |
+| stringstream.cpp:179:7:179:9 | ss2 | stringstream.cpp:179:15:179:16 | ref arg c6 | TAINT |
+| stringstream.cpp:179:15:179:16 | ref arg c6 | stringstream.cpp:185:7:185:8 | c6 | |
+| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:192:7:192:8 | ss | |
+| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:193:7:193:8 | ss | |
+| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:194:7:194:8 | ss | |
+| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:195:7:195:8 | ss | |
+| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:196:7:196:8 | ss | |
+| stringstream.cpp:190:20:190:21 | call to basic_stringstream | stringstream.cpp:197:7:197:8 | ss | |
+| stringstream.cpp:192:7:192:8 | ref arg ss | stringstream.cpp:193:7:193:8 | ss | |
+| stringstream.cpp:192:7:192:8 | ref arg ss | stringstream.cpp:194:7:194:8 | ss | |
+| stringstream.cpp:192:7:192:8 | ref arg ss | stringstream.cpp:195:7:195:8 | ss | |
+| stringstream.cpp:192:7:192:8 | ref arg ss | stringstream.cpp:196:7:196:8 | ss | |
+| stringstream.cpp:192:7:192:8 | ref arg ss | stringstream.cpp:197:7:197:8 | ss | |
+| stringstream.cpp:192:7:192:8 | ss | stringstream.cpp:192:10:192:12 | call to put | |
+| stringstream.cpp:192:14:192:16 | 97 | stringstream.cpp:192:7:192:8 | ref arg ss | TAINT |
+| stringstream.cpp:192:14:192:16 | 97 | stringstream.cpp:192:10:192:12 | call to put | TAINT |
+| stringstream.cpp:193:7:193:8 | ref arg ss | stringstream.cpp:194:7:194:8 | ss | |
+| stringstream.cpp:193:7:193:8 | ref arg ss | stringstream.cpp:195:7:195:8 | ss | |
+| stringstream.cpp:193:7:193:8 | ref arg ss | stringstream.cpp:196:7:196:8 | ss | |
+| stringstream.cpp:193:7:193:8 | ref arg ss | stringstream.cpp:197:7:197:8 | ss | |
+| stringstream.cpp:193:7:193:8 | ss | stringstream.cpp:193:10:193:12 | call to get | TAINT |
+| stringstream.cpp:194:7:194:8 | ref arg ss | stringstream.cpp:195:7:195:8 | ss | |
+| stringstream.cpp:194:7:194:8 | ref arg ss | stringstream.cpp:196:7:196:8 | ss | |
+| stringstream.cpp:194:7:194:8 | ref arg ss | stringstream.cpp:197:7:197:8 | ss | |
+| stringstream.cpp:194:7:194:8 | ss | stringstream.cpp:194:10:194:16 | call to putback | |
+| stringstream.cpp:194:18:194:20 | 98 | stringstream.cpp:194:7:194:8 | ref arg ss | TAINT |
+| stringstream.cpp:194:18:194:20 | 98 | stringstream.cpp:194:10:194:16 | call to putback | TAINT |
+| stringstream.cpp:195:7:195:8 | ref arg ss | stringstream.cpp:196:7:196:8 | ss | |
+| stringstream.cpp:195:7:195:8 | ref arg ss | stringstream.cpp:197:7:197:8 | ss | |
+| stringstream.cpp:195:7:195:8 | ss | stringstream.cpp:195:10:195:12 | call to get | TAINT |
+| stringstream.cpp:196:7:196:8 | ref arg ss | stringstream.cpp:197:7:197:8 | ss | |
+| stringstream.cpp:196:7:196:8 | ss | stringstream.cpp:196:10:196:16 | call to putback | |
+| stringstream.cpp:196:18:196:32 | call to source | stringstream.cpp:196:7:196:8 | ref arg ss | TAINT |
+| stringstream.cpp:196:18:196:32 | call to source | stringstream.cpp:196:10:196:16 | call to putback | TAINT |
+| stringstream.cpp:197:7:197:8 | ss | stringstream.cpp:197:10:197:12 | call to get | TAINT |
+| stringstream.cpp:202:24:202:28 | abc | stringstream.cpp:202:24:202:28 | call to basic_string | TAINT |
+| stringstream.cpp:202:24:202:28 | call to basic_string | stringstream.cpp:202:24:202:29 | call to basic_stringstream | TAINT |
+| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:214:7:214:9 | ss1 | |
+| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:217:7:217:9 | ss1 | |
+| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:222:7:222:9 | ss1 | |
+| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:225:7:225:9 | ss1 | |
+| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:234:15:234:17 | ss1 | |
+| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:237:15:237:17 | ss1 | |
+| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:242:15:242:17 | ss1 | |
+| stringstream.cpp:202:24:202:29 | call to basic_stringstream | stringstream.cpp:245:15:245:17 | ss1 | |
+| stringstream.cpp:203:24:203:29 | call to source | stringstream.cpp:203:24:203:31 | call to basic_string | TAINT |
+| stringstream.cpp:203:24:203:31 | call to basic_string | stringstream.cpp:203:24:203:32 | call to basic_stringstream | TAINT |
+| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:215:7:215:9 | ss2 | |
+| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:216:7:216:9 | ss2 | |
+| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:223:7:223:9 | ss2 | |
+| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:224:7:224:9 | ss2 | |
+| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:230:7:230:9 | ss2 | |
+| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:235:15:235:17 | ss2 | |
+| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:236:15:236:17 | ss2 | |
+| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:243:15:243:17 | ss2 | |
+| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:244:15:244:17 | ss2 | |
+| stringstream.cpp:203:24:203:32 | call to basic_stringstream | stringstream.cpp:250:23:250:25 | ss2 | |
+| stringstream.cpp:204:17:204:20 | {...} | stringstream.cpp:214:19:214:20 | b1 | |
+| stringstream.cpp:204:17:204:20 | {...} | stringstream.cpp:218:7:218:8 | b1 | |
+| stringstream.cpp:204:19:204:19 | 0 | stringstream.cpp:204:17:204:20 | {...} | TAINT |
+| stringstream.cpp:205:17:205:20 | {...} | stringstream.cpp:215:19:215:20 | b2 | |
+| stringstream.cpp:205:17:205:20 | {...} | stringstream.cpp:219:7:219:8 | b2 | |
+| stringstream.cpp:205:19:205:19 | 0 | stringstream.cpp:205:17:205:20 | {...} | TAINT |
+| stringstream.cpp:206:17:206:20 | {...} | stringstream.cpp:216:19:216:20 | b3 | |
+| stringstream.cpp:206:17:206:20 | {...} | stringstream.cpp:217:19:217:20 | b3 | |
+| stringstream.cpp:206:17:206:20 | {...} | stringstream.cpp:220:7:220:8 | b3 | |
+| stringstream.cpp:206:19:206:19 | 0 | stringstream.cpp:206:17:206:20 | {...} | TAINT |
+| stringstream.cpp:207:17:207:20 | {...} | stringstream.cpp:222:19:222:20 | b4 | |
+| stringstream.cpp:207:17:207:20 | {...} | stringstream.cpp:226:7:226:8 | b4 | |
+| stringstream.cpp:207:19:207:19 | 0 | stringstream.cpp:207:17:207:20 | {...} | TAINT |
+| stringstream.cpp:208:17:208:20 | {...} | stringstream.cpp:223:19:223:20 | b5 | |
+| stringstream.cpp:208:17:208:20 | {...} | stringstream.cpp:227:7:227:8 | b5 | |
+| stringstream.cpp:208:19:208:19 | 0 | stringstream.cpp:208:17:208:20 | {...} | TAINT |
+| stringstream.cpp:209:17:209:20 | {...} | stringstream.cpp:224:19:224:20 | b6 | |
+| stringstream.cpp:209:17:209:20 | {...} | stringstream.cpp:225:19:225:20 | b6 | |
+| stringstream.cpp:209:17:209:20 | {...} | stringstream.cpp:228:7:228:8 | b6 | |
+| stringstream.cpp:209:19:209:19 | 0 | stringstream.cpp:209:17:209:20 | {...} | TAINT |
+| stringstream.cpp:210:17:210:20 | {...} | stringstream.cpp:230:19:230:20 | b7 | |
+| stringstream.cpp:210:17:210:20 | {...} | stringstream.cpp:231:7:231:8 | b7 | |
+| stringstream.cpp:210:19:210:19 | 0 | stringstream.cpp:210:17:210:20 | {...} | TAINT |
+| stringstream.cpp:211:17:211:20 | {...} | stringstream.cpp:230:37:230:38 | b8 | |
+| stringstream.cpp:211:17:211:20 | {...} | stringstream.cpp:232:7:232:8 | b8 | |
+| stringstream.cpp:211:19:211:19 | 0 | stringstream.cpp:211:17:211:20 | {...} | TAINT |
+| stringstream.cpp:212:14:212:15 | call to basic_string | stringstream.cpp:234:20:234:21 | s1 | |
+| stringstream.cpp:212:14:212:15 | call to basic_string | stringstream.cpp:238:7:238:8 | s1 | |
+| stringstream.cpp:212:18:212:19 | call to basic_string | stringstream.cpp:235:20:235:21 | s2 | |
+| stringstream.cpp:212:18:212:19 | call to basic_string | stringstream.cpp:239:7:239:8 | s2 | |
+| stringstream.cpp:212:22:212:23 | call to basic_string | stringstream.cpp:236:20:236:21 | s3 | |
+| stringstream.cpp:212:22:212:23 | call to basic_string | stringstream.cpp:237:20:237:21 | s3 | |
+| stringstream.cpp:212:22:212:23 | call to basic_string | stringstream.cpp:240:7:240:8 | s3 | |
+| stringstream.cpp:212:26:212:27 | call to basic_string | stringstream.cpp:242:20:242:21 | s4 | |
+| stringstream.cpp:212:26:212:27 | call to basic_string | stringstream.cpp:246:7:246:8 | s4 | |
+| stringstream.cpp:212:30:212:31 | call to basic_string | stringstream.cpp:243:20:243:21 | s5 | |
+| stringstream.cpp:212:30:212:31 | call to basic_string | stringstream.cpp:247:7:247:8 | s5 | |
+| stringstream.cpp:212:34:212:35 | call to basic_string | stringstream.cpp:244:20:244:21 | s6 | |
+| stringstream.cpp:212:34:212:35 | call to basic_string | stringstream.cpp:245:20:245:21 | s6 | |
+| stringstream.cpp:212:34:212:35 | call to basic_string | stringstream.cpp:248:7:248:8 | s6 | |
+| stringstream.cpp:212:38:212:39 | call to basic_string | stringstream.cpp:250:28:250:29 | s7 | |
+| stringstream.cpp:212:38:212:39 | call to basic_string | stringstream.cpp:251:7:251:8 | s7 | |
+| stringstream.cpp:212:42:212:43 | call to basic_string | stringstream.cpp:250:33:250:34 | s8 | |
+| stringstream.cpp:212:42:212:43 | call to basic_string | stringstream.cpp:252:7:252:8 | s8 | |
+| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:217:7:217:9 | ss1 | |
+| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:222:7:222:9 | ss1 | |
+| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:225:7:225:9 | ss1 | |
+| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:234:15:234:17 | ss1 | |
+| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:237:15:237:17 | ss1 | |
+| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | |
+| stringstream.cpp:214:7:214:9 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | |
+| stringstream.cpp:214:7:214:9 | ss1 | stringstream.cpp:214:11:214:17 | call to getline | |
+| stringstream.cpp:214:7:214:9 | ss1 | stringstream.cpp:214:19:214:20 | ref arg b1 | TAINT |
+| stringstream.cpp:214:19:214:20 | ref arg b1 | stringstream.cpp:218:7:218:8 | b1 | |
+| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:216:7:216:9 | ss2 | |
+| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:223:7:223:9 | ss2 | |
+| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:224:7:224:9 | ss2 | |
+| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:230:7:230:9 | ss2 | |
+| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:235:15:235:17 | ss2 | |
+| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | |
+| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | |
+| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | |
+| stringstream.cpp:215:7:215:9 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | |
+| stringstream.cpp:215:7:215:9 | ss2 | stringstream.cpp:215:11:215:17 | call to getline | |
+| stringstream.cpp:215:7:215:9 | ss2 | stringstream.cpp:215:19:215:20 | ref arg b2 | TAINT |
+| stringstream.cpp:215:19:215:20 | ref arg b2 | stringstream.cpp:219:7:219:8 | b2 | |
+| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:223:7:223:9 | ss2 | |
+| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:224:7:224:9 | ss2 | |
+| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:230:7:230:9 | ss2 | |
+| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:235:15:235:17 | ss2 | |
+| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | |
+| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | |
+| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | |
+| stringstream.cpp:216:7:216:9 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | |
+| stringstream.cpp:216:7:216:9 | ss2 | stringstream.cpp:216:11:216:17 | call to getline | |
+| stringstream.cpp:216:7:216:9 | ss2 | stringstream.cpp:216:19:216:20 | ref arg b3 | TAINT |
+| stringstream.cpp:216:19:216:20 | ref arg b3 | stringstream.cpp:217:19:217:20 | b3 | |
+| stringstream.cpp:216:19:216:20 | ref arg b3 | stringstream.cpp:220:7:220:8 | b3 | |
+| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:222:7:222:9 | ss1 | |
+| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:225:7:225:9 | ss1 | |
+| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:234:15:234:17 | ss1 | |
+| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:237:15:237:17 | ss1 | |
+| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | |
+| stringstream.cpp:217:7:217:9 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | |
+| stringstream.cpp:217:7:217:9 | ss1 | stringstream.cpp:217:11:217:17 | call to getline | |
+| stringstream.cpp:217:7:217:9 | ss1 | stringstream.cpp:217:19:217:20 | ref arg b3 | TAINT |
+| stringstream.cpp:217:19:217:20 | ref arg b3 | stringstream.cpp:220:7:220:8 | b3 | |
+| stringstream.cpp:218:7:218:8 | b1 | stringstream.cpp:218:7:218:8 | call to basic_string | TAINT |
+| stringstream.cpp:219:7:219:8 | b2 | stringstream.cpp:219:7:219:8 | call to basic_string | TAINT |
+| stringstream.cpp:220:7:220:8 | b3 | stringstream.cpp:220:7:220:8 | call to basic_string | TAINT |
+| stringstream.cpp:222:7:222:9 | ref arg ss1 | stringstream.cpp:225:7:225:9 | ss1 | |
+| stringstream.cpp:222:7:222:9 | ref arg ss1 | stringstream.cpp:234:15:234:17 | ss1 | |
+| stringstream.cpp:222:7:222:9 | ref arg ss1 | stringstream.cpp:237:15:237:17 | ss1 | |
+| stringstream.cpp:222:7:222:9 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | |
+| stringstream.cpp:222:7:222:9 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | |
+| stringstream.cpp:222:7:222:9 | ss1 | stringstream.cpp:222:11:222:17 | call to getline | |
+| stringstream.cpp:222:7:222:9 | ss1 | stringstream.cpp:222:19:222:20 | ref arg b4 | TAINT |
+| stringstream.cpp:222:19:222:20 | ref arg b4 | stringstream.cpp:226:7:226:8 | b4 | |
+| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:224:7:224:9 | ss2 | |
+| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:230:7:230:9 | ss2 | |
+| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:235:15:235:17 | ss2 | |
+| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | |
+| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | |
+| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | |
+| stringstream.cpp:223:7:223:9 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | |
+| stringstream.cpp:223:7:223:9 | ss2 | stringstream.cpp:223:11:223:17 | call to getline | |
+| stringstream.cpp:223:7:223:9 | ss2 | stringstream.cpp:223:19:223:20 | ref arg b5 | TAINT |
+| stringstream.cpp:223:19:223:20 | ref arg b5 | stringstream.cpp:227:7:227:8 | b5 | |
+| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:230:7:230:9 | ss2 | |
+| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:235:15:235:17 | ss2 | |
+| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | |
+| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | |
+| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | |
+| stringstream.cpp:224:7:224:9 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | |
+| stringstream.cpp:224:7:224:9 | ss2 | stringstream.cpp:224:11:224:17 | call to getline | |
+| stringstream.cpp:224:7:224:9 | ss2 | stringstream.cpp:224:19:224:20 | ref arg b6 | TAINT |
+| stringstream.cpp:224:19:224:20 | ref arg b6 | stringstream.cpp:225:19:225:20 | b6 | |
+| stringstream.cpp:224:19:224:20 | ref arg b6 | stringstream.cpp:228:7:228:8 | b6 | |
+| stringstream.cpp:225:7:225:9 | ref arg ss1 | stringstream.cpp:234:15:234:17 | ss1 | |
+| stringstream.cpp:225:7:225:9 | ref arg ss1 | stringstream.cpp:237:15:237:17 | ss1 | |
+| stringstream.cpp:225:7:225:9 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | |
+| stringstream.cpp:225:7:225:9 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | |
+| stringstream.cpp:225:7:225:9 | ss1 | stringstream.cpp:225:11:225:17 | call to getline | |
+| stringstream.cpp:225:7:225:9 | ss1 | stringstream.cpp:225:19:225:20 | ref arg b6 | TAINT |
+| stringstream.cpp:225:19:225:20 | ref arg b6 | stringstream.cpp:228:7:228:8 | b6 | |
+| stringstream.cpp:226:7:226:8 | b4 | stringstream.cpp:226:7:226:8 | call to basic_string | TAINT |
+| stringstream.cpp:227:7:227:8 | b5 | stringstream.cpp:227:7:227:8 | call to basic_string | TAINT |
+| stringstream.cpp:228:7:228:8 | b6 | stringstream.cpp:228:7:228:8 | call to basic_string | TAINT |
+| stringstream.cpp:230:7:230:9 | ref arg ss2 | stringstream.cpp:235:15:235:17 | ss2 | |
+| stringstream.cpp:230:7:230:9 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | |
+| stringstream.cpp:230:7:230:9 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | |
+| stringstream.cpp:230:7:230:9 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | |
+| stringstream.cpp:230:7:230:9 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | |
+| stringstream.cpp:230:7:230:9 | ss2 | stringstream.cpp:230:11:230:17 | call to getline | |
+| stringstream.cpp:230:7:230:9 | ss2 | stringstream.cpp:230:19:230:20 | ref arg b7 | TAINT |
+| stringstream.cpp:230:11:230:17 | call to getline | stringstream.cpp:230:29:230:35 | call to getline | |
+| stringstream.cpp:230:11:230:17 | call to getline | stringstream.cpp:230:37:230:38 | ref arg b8 | TAINT |
+| stringstream.cpp:230:11:230:17 | ref arg call to getline | stringstream.cpp:230:7:230:9 | ref arg ss2 | TAINT |
+| stringstream.cpp:230:19:230:20 | ref arg b7 | stringstream.cpp:231:7:231:8 | b7 | |
+| stringstream.cpp:230:37:230:38 | ref arg b8 | stringstream.cpp:232:7:232:8 | b8 | |
+| stringstream.cpp:231:7:231:8 | b7 | stringstream.cpp:231:7:231:8 | call to basic_string | TAINT |
+| stringstream.cpp:232:7:232:8 | b8 | stringstream.cpp:232:7:232:8 | call to basic_string | TAINT |
+| stringstream.cpp:234:15:234:17 | ref arg ss1 | stringstream.cpp:237:15:237:17 | ss1 | |
+| stringstream.cpp:234:15:234:17 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | |
+| stringstream.cpp:234:15:234:17 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | |
+| stringstream.cpp:234:15:234:17 | ss1 | stringstream.cpp:234:7:234:13 | call to getline | |
+| stringstream.cpp:234:15:234:17 | ss1 | stringstream.cpp:234:20:234:21 | ref arg s1 | TAINT |
+| stringstream.cpp:234:20:234:21 | ref arg s1 | stringstream.cpp:238:7:238:8 | s1 | |
+| stringstream.cpp:235:15:235:17 | ref arg ss2 | stringstream.cpp:236:15:236:17 | ss2 | |
+| stringstream.cpp:235:15:235:17 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | |
+| stringstream.cpp:235:15:235:17 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | |
+| stringstream.cpp:235:15:235:17 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | |
+| stringstream.cpp:235:15:235:17 | ss2 | stringstream.cpp:235:7:235:13 | call to getline | |
+| stringstream.cpp:235:15:235:17 | ss2 | stringstream.cpp:235:20:235:21 | ref arg s2 | TAINT |
+| stringstream.cpp:235:20:235:21 | ref arg s2 | stringstream.cpp:239:7:239:8 | s2 | |
+| stringstream.cpp:236:15:236:17 | ref arg ss2 | stringstream.cpp:243:15:243:17 | ss2 | |
+| stringstream.cpp:236:15:236:17 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | |
+| stringstream.cpp:236:15:236:17 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | |
+| stringstream.cpp:236:15:236:17 | ss2 | stringstream.cpp:236:7:236:13 | call to getline | |
+| stringstream.cpp:236:15:236:17 | ss2 | stringstream.cpp:236:20:236:21 | ref arg s3 | TAINT |
+| stringstream.cpp:236:20:236:21 | ref arg s3 | stringstream.cpp:237:20:237:21 | s3 | |
+| stringstream.cpp:236:20:236:21 | ref arg s3 | stringstream.cpp:240:7:240:8 | s3 | |
+| stringstream.cpp:237:15:237:17 | ref arg ss1 | stringstream.cpp:242:15:242:17 | ss1 | |
+| stringstream.cpp:237:15:237:17 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | |
+| stringstream.cpp:237:15:237:17 | ss1 | stringstream.cpp:237:7:237:13 | call to getline | |
+| stringstream.cpp:237:15:237:17 | ss1 | stringstream.cpp:237:20:237:21 | ref arg s3 | TAINT |
+| stringstream.cpp:237:20:237:21 | ref arg s3 | stringstream.cpp:240:7:240:8 | s3 | |
+| stringstream.cpp:242:15:242:17 | ref arg ss1 | stringstream.cpp:245:15:245:17 | ss1 | |
+| stringstream.cpp:242:15:242:17 | ss1 | stringstream.cpp:242:7:242:13 | call to getline | |
+| stringstream.cpp:242:15:242:17 | ss1 | stringstream.cpp:242:20:242:21 | ref arg s4 | TAINT |
+| stringstream.cpp:242:20:242:21 | ref arg s4 | stringstream.cpp:246:7:246:8 | s4 | |
+| stringstream.cpp:243:15:243:17 | ref arg ss2 | stringstream.cpp:244:15:244:17 | ss2 | |
+| stringstream.cpp:243:15:243:17 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | |
+| stringstream.cpp:243:15:243:17 | ss2 | stringstream.cpp:243:7:243:13 | call to getline | |
+| stringstream.cpp:243:15:243:17 | ss2 | stringstream.cpp:243:20:243:21 | ref arg s5 | TAINT |
+| stringstream.cpp:243:20:243:21 | ref arg s5 | stringstream.cpp:247:7:247:8 | s5 | |
+| stringstream.cpp:244:15:244:17 | ref arg ss2 | stringstream.cpp:250:23:250:25 | ss2 | |
+| stringstream.cpp:244:15:244:17 | ss2 | stringstream.cpp:244:7:244:13 | call to getline | |
+| stringstream.cpp:244:15:244:17 | ss2 | stringstream.cpp:244:20:244:21 | ref arg s6 | TAINT |
+| stringstream.cpp:244:20:244:21 | ref arg s6 | stringstream.cpp:245:20:245:21 | s6 | |
+| stringstream.cpp:244:20:244:21 | ref arg s6 | stringstream.cpp:248:7:248:8 | s6 | |
+| stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:7:245:13 | call to getline | |
+| stringstream.cpp:245:15:245:17 | ss1 | stringstream.cpp:245:20:245:21 | ref arg s6 | TAINT |
+| stringstream.cpp:245:20:245:21 | ref arg s6 | stringstream.cpp:248:7:248:8 | s6 | |
+| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:7:250:13 | call to getline | |
+| stringstream.cpp:250:15:250:21 | call to getline | stringstream.cpp:250:33:250:34 | ref arg s8 | TAINT |
+| stringstream.cpp:250:15:250:21 | ref arg call to getline | stringstream.cpp:250:23:250:25 | ref arg ss2 | TAINT |
+| stringstream.cpp:250:23:250:25 | ss2 | stringstream.cpp:250:15:250:21 | call to getline | |
+| stringstream.cpp:250:23:250:25 | ss2 | stringstream.cpp:250:28:250:29 | ref arg s7 | TAINT |
+| stringstream.cpp:250:28:250:29 | ref arg s7 | stringstream.cpp:251:7:251:8 | s7 | |
+| stringstream.cpp:250:33:250:34 | ref arg s8 | stringstream.cpp:252:7:252:8 | s8 | |
+| stringstream.cpp:257:24:257:29 | call to source | stringstream.cpp:257:24:257:31 | call to basic_string | TAINT |
+| stringstream.cpp:257:24:257:31 | call to basic_string | stringstream.cpp:257:24:257:32 | call to basic_stringstream | TAINT |
+| stringstream.cpp:257:24:257:32 | call to basic_stringstream | stringstream.cpp:262:7:262:9 | ss1 | |
+| stringstream.cpp:258:20:258:22 | call to basic_stringstream | stringstream.cpp:266:7:266:9 | ss2 | |
+| stringstream.cpp:258:20:258:22 | call to basic_stringstream | stringstream.cpp:267:7:267:9 | ss2 | |
+| stringstream.cpp:259:17:259:20 | {...} | stringstream.cpp:262:15:262:16 | b1 | |
+| stringstream.cpp:259:17:259:20 | {...} | stringstream.cpp:263:7:263:8 | b1 | |
+| stringstream.cpp:259:19:259:19 | 0 | stringstream.cpp:259:17:259:20 | {...} | TAINT |
+| stringstream.cpp:260:17:260:20 | {...} | stringstream.cpp:262:36:262:37 | b2 | |
+| stringstream.cpp:260:17:260:20 | {...} | stringstream.cpp:264:7:264:8 | b2 | |
+| stringstream.cpp:260:19:260:19 | 0 | stringstream.cpp:260:17:260:20 | {...} | TAINT |
+| stringstream.cpp:262:7:262:9 | ss1 | stringstream.cpp:262:11:262:13 | call to get | |
+| stringstream.cpp:262:7:262:9 | ss1 | stringstream.cpp:262:15:262:16 | ref arg b1 | TAINT |
+| stringstream.cpp:262:11:262:13 | call to get | stringstream.cpp:262:24:262:28 | call to unget | |
+| stringstream.cpp:262:11:262:13 | ref arg call to get | stringstream.cpp:262:7:262:9 | ref arg ss1 | TAINT |
+| stringstream.cpp:262:15:262:16 | ref arg b1 | stringstream.cpp:263:7:263:8 | b1 | |
+| stringstream.cpp:262:24:262:28 | call to unget | stringstream.cpp:262:32:262:34 | call to get | |
+| stringstream.cpp:262:24:262:28 | call to unget | stringstream.cpp:262:36:262:37 | ref arg b2 | TAINT |
+| stringstream.cpp:262:24:262:28 | ref arg call to unget | stringstream.cpp:262:11:262:13 | ref arg call to get | TAINT |
+| stringstream.cpp:262:36:262:37 | ref arg b2 | stringstream.cpp:264:7:264:8 | b2 | |
+| stringstream.cpp:263:7:263:8 | b1 | stringstream.cpp:263:7:263:8 | call to basic_string | TAINT |
+| stringstream.cpp:264:7:264:8 | b2 | stringstream.cpp:264:7:264:8 | call to basic_string | TAINT |
+| stringstream.cpp:266:7:266:9 | ref arg ss2 | stringstream.cpp:267:7:267:9 | ss2 | |
+| stringstream.cpp:266:7:266:9 | ss2 | stringstream.cpp:266:11:266:15 | call to write | |
+| stringstream.cpp:266:11:266:15 | call to write | stringstream.cpp:266:27:266:31 | call to flush | |
+| stringstream.cpp:266:11:266:15 | ref arg call to write | stringstream.cpp:266:7:266:9 | ref arg ss2 | TAINT |
+| stringstream.cpp:266:17:266:21 | abc | stringstream.cpp:266:7:266:9 | ref arg ss2 | TAINT |
+| stringstream.cpp:266:17:266:21 | abc | stringstream.cpp:266:11:266:15 | call to write | TAINT |
+| stringstream.cpp:266:27:266:31 | call to flush | stringstream.cpp:266:35:266:39 | call to write | |
+| stringstream.cpp:266:27:266:31 | ref arg call to flush | stringstream.cpp:266:11:266:15 | ref arg call to write | TAINT |
+| stringstream.cpp:266:35:266:39 | call to write | stringstream.cpp:266:54:266:58 | call to flush | |
+| stringstream.cpp:266:35:266:39 | ref arg call to write | stringstream.cpp:266:27:266:31 | ref arg call to flush | TAINT |
+| stringstream.cpp:266:41:266:46 | call to source | stringstream.cpp:266:27:266:31 | ref arg call to flush | TAINT |
+| stringstream.cpp:266:41:266:46 | call to source | stringstream.cpp:266:35:266:39 | call to write | TAINT |
+| stringstream.cpp:266:54:266:58 | call to flush | stringstream.cpp:266:62:266:66 | call to write | |
+| stringstream.cpp:266:54:266:58 | ref arg call to flush | stringstream.cpp:266:35:266:39 | ref arg call to write | TAINT |
+| stringstream.cpp:266:68:266:72 | xyz | stringstream.cpp:266:54:266:58 | ref arg call to flush | TAINT |
+| stringstream.cpp:266:68:266:72 | xyz | stringstream.cpp:266:62:266:66 | call to write | TAINT |
+| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
+| structlikeclass.cpp:5:7:5:7 | Unknown literal | structlikeclass.cpp:5:7:5:7 | constructor init of field v | TAINT |
+| structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | |
+| structlikeclass.cpp:5:7:5:7 | this | structlikeclass.cpp:5:7:5:7 | constructor init of field v [pre-this] | |
+| structlikeclass.cpp:8:2:8:16 | this | structlikeclass.cpp:8:28:8:32 | constructor init of field v [pre-this] | |
+| structlikeclass.cpp:8:22:8:23 | _v | structlikeclass.cpp:8:30:8:31 | _v | |
+| structlikeclass.cpp:8:30:8:31 | _v | structlikeclass.cpp:8:28:8:32 | constructor init of field v | TAINT |
+| structlikeclass.cpp:16:22:16:22 | 1 | structlikeclass.cpp:16:22:16:23 | call to StructLikeClass | TAINT |
+| structlikeclass.cpp:16:22:16:23 | call to StructLikeClass | structlikeclass.cpp:18:22:18:23 | s1 | |
+| structlikeclass.cpp:16:22:16:23 | call to StructLikeClass | structlikeclass.cpp:22:8:22:9 | s1 | |
+| structlikeclass.cpp:17:23:17:24 | call to StructLikeClass | structlikeclass.cpp:23:8:23:9 | s2 | |
+| structlikeclass.cpp:17:24:17:24 | 1 | structlikeclass.cpp:17:23:17:24 | call to StructLikeClass | TAINT |
+| structlikeclass.cpp:18:22:18:23 | s1 | structlikeclass.cpp:24:8:24:9 | s3 | |
+| structlikeclass.cpp:20:8:20:8 | 1 | structlikeclass.cpp:20:8:20:8 | call to StructLikeClass | TAINT |
+| structlikeclass.cpp:20:8:20:8 | call to StructLikeClass | structlikeclass.cpp:20:3:20:8 | ... = ... | |
+| structlikeclass.cpp:20:8:20:8 | call to StructLikeClass | structlikeclass.cpp:25:8:25:9 | s4 | |
+| structlikeclass.cpp:29:22:29:27 | call to source | structlikeclass.cpp:29:22:29:30 | call to StructLikeClass | TAINT |
+| structlikeclass.cpp:29:22:29:30 | call to StructLikeClass | structlikeclass.cpp:31:22:31:23 | s1 | |
+| structlikeclass.cpp:29:22:29:30 | call to StructLikeClass | structlikeclass.cpp:35:8:35:9 | s1 | |
+| structlikeclass.cpp:30:23:30:31 | call to StructLikeClass | structlikeclass.cpp:36:8:36:9 | s2 | |
+| structlikeclass.cpp:30:24:30:29 | call to source | structlikeclass.cpp:30:23:30:31 | call to StructLikeClass | TAINT |
+| structlikeclass.cpp:31:22:31:23 | s1 | structlikeclass.cpp:37:8:37:9 | s3 | |
+| structlikeclass.cpp:33:8:33:13 | call to source | structlikeclass.cpp:33:8:33:15 | call to StructLikeClass | TAINT |
+| structlikeclass.cpp:33:8:33:15 | call to StructLikeClass | structlikeclass.cpp:33:3:33:15 | ... = ... | |
+| structlikeclass.cpp:33:8:33:15 | call to StructLikeClass | structlikeclass.cpp:38:8:38:9 | s4 | |
+| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:43:24:43:25 | s1 | |
+| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:44:22:44:23 | s1 | |
+| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:46:8:46:9 | s1 | |
+| structlikeclass.cpp:42:19:42:20 | call to StructLikeClass | structlikeclass.cpp:48:8:48:9 | s1 | |
+| structlikeclass.cpp:43:24:43:25 | s1 | structlikeclass.cpp:49:8:49:9 | s2 | |
+| structlikeclass.cpp:44:22:44:23 | s1 | structlikeclass.cpp:50:8:50:9 | s3 | |
+| structlikeclass.cpp:46:8:46:9 | s1 | structlikeclass.cpp:46:3:46:9 | ... = ... | |
+| structlikeclass.cpp:46:8:46:9 | s1 | structlikeclass.cpp:51:8:51:9 | s4 | |
+| structlikeclass.cpp:55:23:55:48 | call to StructLikeClass | structlikeclass.cpp:60:8:60:9 | s1 | |
+| structlikeclass.cpp:55:40:55:45 | call to source | structlikeclass.cpp:55:23:55:48 | call to StructLikeClass | TAINT |
+| structlikeclass.cpp:58:8:58:32 | call to StructLikeClass | structlikeclass.cpp:58:3:58:32 | ... = ... | |
+| structlikeclass.cpp:58:8:58:32 | call to StructLikeClass | structlikeclass.cpp:61:8:61:9 | s2 | |
+| structlikeclass.cpp:58:24:58:29 | call to source | structlikeclass.cpp:58:8:58:32 | call to StructLikeClass | TAINT |
+| structlikeclass.cpp:62:8:62:9 | s3 | structlikeclass.cpp:62:8:62:20 | ... = ... | |
+| structlikeclass.cpp:62:13:62:18 | call to source | structlikeclass.cpp:62:13:62:20 | call to StructLikeClass | TAINT |
+| structlikeclass.cpp:62:13:62:20 | call to StructLikeClass | structlikeclass.cpp:62:8:62:20 | ... = ... | |
+| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | |
+| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:17:14:17 | t | |
+| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:56:14:56 | t | |
+| swap1.cpp:14:17:14:17 | t | swap1.cpp:14:56:14:56 | t | |
+| swap1.cpp:24:9:24:13 | this | swap1.cpp:24:31:24:34 | this | |
+| swap1.cpp:24:23:24:26 | that | swap1.cpp:24:23:24:26 | that | |
+| swap1.cpp:24:23:24:26 | that | swap1.cpp:24:36:24:39 | that | |
+| swap1.cpp:24:36:24:39 | ref arg that | swap1.cpp:24:23:24:26 | that | |
+| swap1.cpp:25:9:25:13 | this | swap1.cpp:25:36:25:52 | constructor init of field data1 [pre-this] | |
+| swap1.cpp:25:28:25:31 | that | swap1.cpp:25:42:25:45 | that | |
+| swap1.cpp:25:47:25:51 | data1 | swap1.cpp:25:36:25:52 | constructor init of field data1 | TAINT |
+| swap1.cpp:25:47:25:51 | data1 | swap1.cpp:25:47:25:51 | data1 | |
+| swap1.cpp:27:16:27:24 | this | swap1.cpp:30:13:30:16 | this | |
+| swap1.cpp:27:39:27:42 | that | swap1.cpp:29:24:29:27 | that | |
+| swap1.cpp:29:23:29:27 | call to Class | swap1.cpp:30:18:30:20 | tmp | |
+| swap1.cpp:29:24:29:27 | that | swap1.cpp:29:23:29:27 | call to Class | |
+| swap1.cpp:30:13:30:16 | ref arg this | swap1.cpp:31:21:31:24 | this | |
+| swap1.cpp:30:13:30:16 | this | swap1.cpp:31:21:31:24 | this | |
+| swap1.cpp:31:21:31:24 | this | swap1.cpp:31:20:31:24 | * ... | TAINT |
+| swap1.cpp:34:16:34:24 | this | swap1.cpp:36:13:36:16 | this | |
+| swap1.cpp:34:34:34:37 | that | swap1.cpp:34:34:34:37 | that | |
+| swap1.cpp:34:34:34:37 | that | swap1.cpp:36:18:36:21 | that | |
+| swap1.cpp:36:13:36:16 | ref arg this | swap1.cpp:37:21:37:24 | this | |
+| swap1.cpp:36:13:36:16 | this | swap1.cpp:37:21:37:24 | this | |
+| swap1.cpp:36:18:36:21 | ref arg that | swap1.cpp:34:34:34:37 | that | |
+| swap1.cpp:37:21:37:24 | this | swap1.cpp:37:20:37:24 | * ... | TAINT |
+| swap1.cpp:40:16:40:26 | this | swap1.cpp:43:13:43:16 | this | |
+| swap1.cpp:40:41:40:44 | that | swap1.cpp:42:24:42:27 | that | |
+| swap1.cpp:42:23:42:27 | call to Class | swap1.cpp:43:18:43:20 | tmp | |
+| swap1.cpp:42:24:42:27 | that | swap1.cpp:42:23:42:27 | call to Class | |
+| swap1.cpp:43:13:43:16 | ref arg this | swap1.cpp:44:21:44:24 | this | |
+| swap1.cpp:43:13:43:16 | this | swap1.cpp:44:21:44:24 | this | |
+| swap1.cpp:44:21:44:24 | this | swap1.cpp:44:20:44:24 | * ... | TAINT |
+| swap1.cpp:47:16:47:26 | this | swap1.cpp:49:13:49:16 | this | |
+| swap1.cpp:47:36:47:39 | that | swap1.cpp:47:36:47:39 | that | |
+| swap1.cpp:47:36:47:39 | that | swap1.cpp:49:18:49:21 | that | |
+| swap1.cpp:49:13:49:16 | ref arg this | swap1.cpp:50:21:50:24 | this | |
+| swap1.cpp:49:13:49:16 | this | swap1.cpp:50:21:50:24 | this | |
+| swap1.cpp:49:18:49:21 | ref arg that | swap1.cpp:47:36:47:39 | that | |
+| swap1.cpp:50:21:50:24 | this | swap1.cpp:50:20:50:24 | * ... | TAINT |
+| swap1.cpp:53:14:53:17 | this | swap1.cpp:56:18:56:22 | this | |
+| swap1.cpp:53:26:53:29 | that | swap1.cpp:53:26:53:29 | that | |
+| swap1.cpp:53:26:53:29 | that | swap1.cpp:56:25:56:28 | that | |
+| swap1.cpp:56:18:56:22 | data1 | swap1.cpp:56:30:56:34 | ref arg data1 | |
+| swap1.cpp:56:25:56:28 | that | swap1.cpp:56:18:56:22 | ref arg data1 | |
+| swap1.cpp:56:25:56:28 | that [post update] | swap1.cpp:53:26:53:29 | that | |
+| swap1.cpp:56:30:56:34 | data1 | swap1.cpp:56:18:56:22 | ref arg data1 | |
+| swap1.cpp:61:22:61:22 | x | swap1.cpp:61:22:61:22 | x | |
+| swap1.cpp:61:22:61:22 | x | swap1.cpp:63:9:63:9 | x | |
+| swap1.cpp:61:22:61:22 | x | swap2.cpp:61:22:61:22 | x | |
+| swap1.cpp:61:22:61:22 | x | swap2.cpp:63:9:63:9 | x | |
+| swap1.cpp:61:32:61:32 | y | swap1.cpp:61:32:61:32 | y | |
+| swap1.cpp:61:32:61:32 | y | swap1.cpp:63:16:63:16 | y | |
+| swap1.cpp:61:32:61:32 | y | swap2.cpp:61:32:61:32 | y | |
+| swap1.cpp:61:32:61:32 | y | swap2.cpp:63:16:63:16 | y | |
+| swap1.cpp:63:9:63:9 | ref arg x | swap1.cpp:61:22:61:22 | x | |
+| swap1.cpp:63:9:63:9 | ref arg x | swap2.cpp:61:22:61:22 | x | |
+| swap1.cpp:63:16:63:16 | ref arg y | swap1.cpp:61:32:61:32 | y | |
+| swap1.cpp:63:16:63:16 | ref arg y | swap2.cpp:61:32:61:32 | y | |
+| swap1.cpp:69:23:69:23 | x | swap1.cpp:71:5:71:5 | x | |
+| swap1.cpp:69:23:69:23 | x | swap1.cpp:73:10:73:10 | x | |
+| swap1.cpp:69:23:69:23 | x | swap1.cpp:76:9:76:9 | x | |
+| swap1.cpp:69:23:69:23 | x | swap1.cpp:79:10:79:10 | x | |
+| swap1.cpp:70:23:70:23 | y | swap1.cpp:74:10:74:10 | y | |
+| swap1.cpp:70:23:70:23 | y | swap1.cpp:76:5:76:5 | y | |
+| swap1.cpp:70:23:70:23 | y | swap1.cpp:78:10:78:10 | y | |
+| swap1.cpp:71:5:71:5 | x [post update] | swap1.cpp:73:10:73:10 | x | |
+| swap1.cpp:71:5:71:5 | x [post update] | swap1.cpp:76:9:76:9 | x | |
+| swap1.cpp:71:5:71:5 | x [post update] | swap1.cpp:79:10:79:10 | x | |
+| swap1.cpp:71:5:71:22 | ... = ... | swap1.cpp:71:7:71:11 | data1 [post update] | |
+| swap1.cpp:71:5:71:22 | ... = ... | swap1.cpp:73:12:73:16 | data1 | |
+| swap1.cpp:71:5:71:22 | ... = ... | swap1.cpp:79:12:79:16 | data1 | |
+| swap1.cpp:71:15:71:20 | call to source | swap1.cpp:71:5:71:22 | ... = ... | |
+| swap1.cpp:76:5:76:5 | ref arg y | swap1.cpp:78:10:78:10 | y | |
+| swap1.cpp:76:9:76:9 | x | swap1.cpp:76:5:76:5 | ref arg y | TAINT |
+| swap1.cpp:76:9:76:9 | x | swap1.cpp:76:7:76:7 | call to operator= | TAINT |
+| swap1.cpp:81:23:81:24 | z1 | swap1.cpp:82:5:82:6 | z1 | |
+| swap1.cpp:81:23:81:24 | z1 | swap1.cpp:83:10:83:11 | z1 | |
+| swap1.cpp:81:23:81:24 | z1 | swap1.cpp:85:10:85:11 | z1 | |
+| swap1.cpp:81:23:81:24 | z1 | swap1.cpp:88:10:88:11 | z1 | |
+| swap1.cpp:81:27:81:28 | z2 | swap1.cpp:85:14:85:15 | z2 | |
+| swap1.cpp:81:27:81:28 | z2 | swap1.cpp:87:10:87:11 | z2 | |
+| swap1.cpp:82:5:82:6 | z1 [post update] | swap1.cpp:83:10:83:11 | z1 | |
+| swap1.cpp:82:5:82:6 | z1 [post update] | swap1.cpp:85:10:85:11 | z1 | |
+| swap1.cpp:82:5:82:6 | z1 [post update] | swap1.cpp:88:10:88:11 | z1 | |
+| swap1.cpp:82:5:82:23 | ... = ... | swap1.cpp:82:8:82:12 | data1 [post update] | |
+| swap1.cpp:82:5:82:23 | ... = ... | swap1.cpp:83:13:83:17 | data1 | |
+| swap1.cpp:82:5:82:23 | ... = ... | swap1.cpp:88:13:88:17 | data1 | |
+| swap1.cpp:82:16:82:21 | call to source | swap1.cpp:82:5:82:23 | ... = ... | |
+| swap1.cpp:85:10:85:11 | ref arg z1 | swap1.cpp:88:10:88:11 | z1 | |
+| swap1.cpp:85:14:85:15 | ref arg z2 | swap1.cpp:87:10:87:11 | z2 | |
+| swap1.cpp:93:23:93:23 | x | swap1.cpp:95:5:95:5 | x | |
+| swap1.cpp:93:23:93:23 | x | swap1.cpp:97:10:97:10 | x | |
+| swap1.cpp:93:23:93:23 | x | swap1.cpp:100:19:100:19 | x | |
+| swap1.cpp:93:23:93:23 | x | swap1.cpp:103:10:103:10 | x | |
+| swap1.cpp:94:23:94:23 | y | swap1.cpp:98:10:98:10 | y | |
+| swap1.cpp:94:23:94:23 | y | swap1.cpp:100:5:100:5 | y | |
+| swap1.cpp:94:23:94:23 | y | swap1.cpp:102:10:102:10 | y | |
+| swap1.cpp:95:5:95:5 | x [post update] | swap1.cpp:97:10:97:10 | x | |
+| swap1.cpp:95:5:95:5 | x [post update] | swap1.cpp:100:19:100:19 | x | |
+| swap1.cpp:95:5:95:5 | x [post update] | swap1.cpp:103:10:103:10 | x | |
+| swap1.cpp:95:5:95:22 | ... = ... | swap1.cpp:95:7:95:11 | data1 [post update] | |
+| swap1.cpp:95:5:95:22 | ... = ... | swap1.cpp:97:12:97:16 | data1 | |
+| swap1.cpp:95:5:95:22 | ... = ... | swap1.cpp:103:12:103:16 | data1 | |
+| swap1.cpp:95:15:95:20 | call to source | swap1.cpp:95:5:95:22 | ... = ... | |
+| swap1.cpp:100:5:100:5 | ref arg y | swap1.cpp:102:10:102:10 | y | |
+| swap1.cpp:100:9:100:17 | call to move | swap1.cpp:100:5:100:5 | ref arg y | TAINT |
+| swap1.cpp:100:9:100:17 | call to move | swap1.cpp:100:7:100:7 | call to operator= | TAINT |
+| swap1.cpp:100:9:100:17 | ref arg call to move | swap1.cpp:100:19:100:19 | x [inner post update] | |
+| swap1.cpp:100:9:100:17 | ref arg call to move | swap1.cpp:103:10:103:10 | x | |
+| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:5:100:5 | ref arg y | TAINT |
+| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:7:100:7 | call to operator= | TAINT |
+| swap1.cpp:100:19:100:19 | x | swap1.cpp:100:9:100:17 | call to move | |
+| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:109:5:109:13 | move_from | |
+| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:111:10:111:18 | move_from | |
+| swap1.cpp:108:23:108:31 | move_from | swap1.cpp:113:41:113:49 | move_from | |
+| swap1.cpp:109:5:109:13 | move_from [post update] | swap1.cpp:111:10:111:18 | move_from | |
+| swap1.cpp:109:5:109:13 | move_from [post update] | swap1.cpp:113:41:113:49 | move_from | |
+| swap1.cpp:109:5:109:30 | ... = ... | swap1.cpp:109:15:109:19 | data1 [post update] | |
+| swap1.cpp:109:5:109:30 | ... = ... | swap1.cpp:111:20:111:24 | data1 | |
+| swap1.cpp:109:5:109:30 | ... = ... | swap1.cpp:115:18:115:22 | data1 | |
+| swap1.cpp:109:23:109:28 | call to source | swap1.cpp:109:5:109:30 | ... = ... | |
+| swap1.cpp:113:31:113:39 | call to move | swap1.cpp:113:31:113:51 | call to Class | |
+| swap1.cpp:113:31:113:39 | ref arg call to move | swap1.cpp:113:41:113:49 | move_from [inner post update] | |
+| swap1.cpp:113:31:113:51 | call to Class | swap1.cpp:115:10:115:16 | move_to | |
+| swap1.cpp:113:41:113:49 | move_from | swap1.cpp:113:31:113:39 | call to move | |
+| swap1.cpp:120:23:120:23 | x | swap1.cpp:122:5:122:5 | x | |
+| swap1.cpp:120:23:120:23 | x | swap1.cpp:124:10:124:10 | x | |
+| swap1.cpp:120:23:120:23 | x | swap1.cpp:127:19:127:19 | x | |
+| swap1.cpp:120:23:120:23 | x | swap1.cpp:130:10:130:10 | x | |
+| swap1.cpp:121:23:121:23 | y | swap1.cpp:125:10:125:10 | y | |
+| swap1.cpp:121:23:121:23 | y | swap1.cpp:127:5:127:5 | y | |
+| swap1.cpp:121:23:121:23 | y | swap1.cpp:129:10:129:10 | y | |
+| swap1.cpp:122:5:122:5 | x [post update] | swap1.cpp:124:10:124:10 | x | |
+| swap1.cpp:122:5:122:5 | x [post update] | swap1.cpp:127:19:127:19 | x | |
+| swap1.cpp:122:5:122:5 | x [post update] | swap1.cpp:130:10:130:10 | x | |
+| swap1.cpp:122:5:122:22 | ... = ... | swap1.cpp:122:7:122:11 | data1 [post update] | |
+| swap1.cpp:122:5:122:22 | ... = ... | swap1.cpp:124:12:124:16 | data1 | |
+| swap1.cpp:122:5:122:22 | ... = ... | swap1.cpp:130:12:130:16 | data1 | |
+| swap1.cpp:122:15:122:20 | call to source | swap1.cpp:122:5:122:22 | ... = ... | |
+| swap1.cpp:127:5:127:5 | ref arg y | swap1.cpp:129:10:129:10 | y | |
+| swap1.cpp:135:23:135:23 | x | swap1.cpp:137:5:137:5 | x | |
+| swap1.cpp:135:23:135:23 | x | swap1.cpp:139:10:139:10 | x | |
+| swap1.cpp:135:23:135:23 | x | swap1.cpp:142:29:142:29 | x | |
+| swap1.cpp:135:23:135:23 | x | swap1.cpp:145:10:145:10 | x | |
+| swap1.cpp:136:23:136:23 | y | swap1.cpp:140:10:140:10 | y | |
+| swap1.cpp:136:23:136:23 | y | swap1.cpp:142:5:142:5 | y | |
+| swap1.cpp:136:23:136:23 | y | swap1.cpp:144:10:144:10 | y | |
+| swap1.cpp:137:5:137:5 | x [post update] | swap1.cpp:139:10:139:10 | x | |
+| swap1.cpp:137:5:137:5 | x [post update] | swap1.cpp:142:29:142:29 | x | |
+| swap1.cpp:137:5:137:5 | x [post update] | swap1.cpp:145:10:145:10 | x | |
+| swap1.cpp:137:5:137:22 | ... = ... | swap1.cpp:137:7:137:11 | data1 [post update] | |
+| swap1.cpp:137:5:137:22 | ... = ... | swap1.cpp:139:12:139:16 | data1 | |
+| swap1.cpp:137:5:137:22 | ... = ... | swap1.cpp:145:12:145:16 | data1 | |
+| swap1.cpp:137:15:137:20 | call to source | swap1.cpp:137:5:137:22 | ... = ... | |
+| swap1.cpp:142:5:142:5 | ref arg y | swap1.cpp:144:10:144:10 | y | |
+| swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:142:29:142:29 | x [inner post update] | |
+| swap1.cpp:142:19:142:27 | ref arg call to move | swap1.cpp:145:10:145:10 | x | |
+| swap1.cpp:142:29:142:29 | x | swap1.cpp:142:19:142:27 | call to move | |
+| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | |
+| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:17:14:17 | t | |
+| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:56:14:56 | t | |
+| swap2.cpp:14:17:14:17 | t | swap2.cpp:14:56:14:56 | t | |
+| swap2.cpp:24:9:24:13 | this | swap2.cpp:24:31:24:34 | this | |
+| swap2.cpp:24:23:24:26 | that | swap2.cpp:24:23:24:26 | that | |
+| swap2.cpp:24:23:24:26 | that | swap2.cpp:24:36:24:39 | that | |
+| swap2.cpp:24:36:24:39 | ref arg that | swap2.cpp:24:23:24:26 | that | |
+| swap2.cpp:25:9:25:13 | this | swap2.cpp:25:36:25:52 | constructor init of field data1 [pre-this] | |
+| swap2.cpp:25:28:25:31 | that | swap2.cpp:25:42:25:45 | that | |
+| swap2.cpp:25:28:25:31 | that | swap2.cpp:25:61:25:64 | that | |
+| swap2.cpp:25:36:25:52 | constructor init of field data1 [post-this] | swap2.cpp:25:55:25:71 | constructor init of field data2 [pre-this] | |
+| swap2.cpp:25:36:25:52 | constructor init of field data1 [pre-this] | swap2.cpp:25:55:25:71 | constructor init of field data2 [pre-this] | |
+| swap2.cpp:25:47:25:51 | data1 | swap2.cpp:25:36:25:52 | constructor init of field data1 | TAINT |
+| swap2.cpp:25:47:25:51 | data1 | swap2.cpp:25:47:25:51 | data1 | |
+| swap2.cpp:25:66:25:70 | data2 | swap2.cpp:25:55:25:71 | constructor init of field data2 | TAINT |
+| swap2.cpp:25:66:25:70 | data2 | swap2.cpp:25:66:25:70 | data2 | |
+| swap2.cpp:27:16:27:24 | this | swap2.cpp:30:13:30:16 | this | |
+| swap2.cpp:27:39:27:42 | that | swap2.cpp:29:24:29:27 | that | |
+| swap2.cpp:29:23:29:27 | call to Class | swap2.cpp:30:18:30:20 | tmp | |
+| swap2.cpp:29:24:29:27 | that | swap2.cpp:29:23:29:27 | call to Class | |
+| swap2.cpp:30:13:30:16 | ref arg this | swap2.cpp:31:21:31:24 | this | |
+| swap2.cpp:30:13:30:16 | this | swap2.cpp:31:21:31:24 | this | |
+| swap2.cpp:31:21:31:24 | this | swap2.cpp:31:20:31:24 | * ... | TAINT |
+| swap2.cpp:34:16:34:24 | this | swap2.cpp:36:13:36:16 | this | |
+| swap2.cpp:34:34:34:37 | that | swap2.cpp:34:34:34:37 | that | |
+| swap2.cpp:34:34:34:37 | that | swap2.cpp:36:18:36:21 | that | |
+| swap2.cpp:36:13:36:16 | ref arg this | swap2.cpp:37:21:37:24 | this | |
+| swap2.cpp:36:13:36:16 | this | swap2.cpp:37:21:37:24 | this | |
+| swap2.cpp:36:18:36:21 | ref arg that | swap2.cpp:34:34:34:37 | that | |
+| swap2.cpp:37:21:37:24 | this | swap2.cpp:37:20:37:24 | * ... | TAINT |
+| swap2.cpp:40:16:40:26 | this | swap2.cpp:43:13:43:16 | this | |
+| swap2.cpp:40:41:40:44 | that | swap2.cpp:42:24:42:27 | that | |
+| swap2.cpp:42:23:42:27 | call to Class | swap2.cpp:43:18:43:20 | tmp | |
+| swap2.cpp:42:24:42:27 | that | swap2.cpp:42:23:42:27 | call to Class | |
+| swap2.cpp:43:13:43:16 | ref arg this | swap2.cpp:44:21:44:24 | this | |
+| swap2.cpp:43:13:43:16 | this | swap2.cpp:44:21:44:24 | this | |
+| swap2.cpp:44:21:44:24 | this | swap2.cpp:44:20:44:24 | * ... | TAINT |
+| swap2.cpp:47:16:47:26 | this | swap2.cpp:49:13:49:16 | this | |
+| swap2.cpp:47:36:47:39 | that | swap2.cpp:47:36:47:39 | that | |
+| swap2.cpp:47:36:47:39 | that | swap2.cpp:49:18:49:21 | that | |
+| swap2.cpp:49:13:49:16 | ref arg this | swap2.cpp:50:21:50:24 | this | |
+| swap2.cpp:49:13:49:16 | this | swap2.cpp:50:21:50:24 | this | |
+| swap2.cpp:49:18:49:21 | ref arg that | swap2.cpp:47:36:47:39 | that | |
+| swap2.cpp:50:21:50:24 | this | swap2.cpp:50:20:50:24 | * ... | TAINT |
+| swap2.cpp:53:14:53:17 | this | swap2.cpp:56:18:56:22 | this | |
+| swap2.cpp:53:26:53:29 | that | swap2.cpp:53:26:53:29 | that | |
+| swap2.cpp:53:26:53:29 | that | swap2.cpp:56:25:56:28 | that | |
+| swap2.cpp:53:26:53:29 | that | swap2.cpp:56:50:56:53 | that | |
+| swap2.cpp:56:18:56:22 | data1 | swap2.cpp:56:30:56:34 | ref arg data1 | |
+| swap2.cpp:56:18:56:22 | this | swap2.cpp:56:43:56:47 | this | |
+| swap2.cpp:56:18:56:22 | this [post update] | swap2.cpp:56:43:56:47 | this | |
+| swap2.cpp:56:25:56:28 | that | swap2.cpp:56:18:56:22 | ref arg data1 | |
+| swap2.cpp:56:25:56:28 | that [post update] | swap2.cpp:53:26:53:29 | that | |
+| swap2.cpp:56:25:56:28 | that [post update] | swap2.cpp:56:50:56:53 | that | |
+| swap2.cpp:56:30:56:34 | data1 | swap2.cpp:56:18:56:22 | ref arg data1 | |
+| swap2.cpp:56:43:56:47 | data2 | swap2.cpp:56:55:56:59 | ref arg data2 | |
+| swap2.cpp:56:50:56:53 | that | swap2.cpp:56:43:56:47 | ref arg data2 | |
+| swap2.cpp:56:50:56:53 | that [post update] | swap2.cpp:53:26:53:29 | that | |
+| swap2.cpp:56:55:56:59 | data2 | swap2.cpp:56:43:56:47 | ref arg data2 | |
+| swap2.cpp:61:22:61:22 | x | swap1.cpp:61:22:61:22 | x | |
+| swap2.cpp:61:22:61:22 | x | swap1.cpp:63:9:63:9 | x | |
+| swap2.cpp:61:22:61:22 | x | swap2.cpp:61:22:61:22 | x | |
+| swap2.cpp:61:22:61:22 | x | swap2.cpp:63:9:63:9 | x | |
+| swap2.cpp:61:32:61:32 | y | swap1.cpp:61:32:61:32 | y | |
+| swap2.cpp:61:32:61:32 | y | swap1.cpp:63:16:63:16 | y | |
+| swap2.cpp:61:32:61:32 | y | swap2.cpp:61:32:61:32 | y | |
+| swap2.cpp:61:32:61:32 | y | swap2.cpp:63:16:63:16 | y | |
+| swap2.cpp:63:9:63:9 | ref arg x | swap1.cpp:61:22:61:22 | x | |
+| swap2.cpp:63:9:63:9 | ref arg x | swap2.cpp:61:22:61:22 | x | |
+| swap2.cpp:63:16:63:16 | ref arg y | swap1.cpp:61:32:61:32 | y | |
+| swap2.cpp:63:16:63:16 | ref arg y | swap2.cpp:61:32:61:32 | y | |
+| swap2.cpp:69:23:69:23 | x | swap2.cpp:71:5:71:5 | x | |
+| swap2.cpp:69:23:69:23 | x | swap2.cpp:73:10:73:10 | x | |
+| swap2.cpp:69:23:69:23 | x | swap2.cpp:76:9:76:9 | x | |
+| swap2.cpp:69:23:69:23 | x | swap2.cpp:79:10:79:10 | x | |
+| swap2.cpp:70:23:70:23 | y | swap2.cpp:74:10:74:10 | y | |
+| swap2.cpp:70:23:70:23 | y | swap2.cpp:76:5:76:5 | y | |
+| swap2.cpp:70:23:70:23 | y | swap2.cpp:78:10:78:10 | y | |
+| swap2.cpp:71:5:71:5 | x [post update] | swap2.cpp:73:10:73:10 | x | |
+| swap2.cpp:71:5:71:5 | x [post update] | swap2.cpp:76:9:76:9 | x | |
+| swap2.cpp:71:5:71:5 | x [post update] | swap2.cpp:79:10:79:10 | x | |
+| swap2.cpp:71:5:71:22 | ... = ... | swap2.cpp:71:7:71:11 | data1 [post update] | |
+| swap2.cpp:71:5:71:22 | ... = ... | swap2.cpp:73:12:73:16 | data1 | |
+| swap2.cpp:71:5:71:22 | ... = ... | swap2.cpp:79:12:79:16 | data1 | |
+| swap2.cpp:71:15:71:20 | call to source | swap2.cpp:71:5:71:22 | ... = ... | |
+| swap2.cpp:76:5:76:5 | ref arg y | swap2.cpp:78:10:78:10 | y | |
+| swap2.cpp:76:9:76:9 | x | swap2.cpp:76:5:76:5 | ref arg y | TAINT |
+| swap2.cpp:76:9:76:9 | x | swap2.cpp:76:7:76:7 | call to operator= | TAINT |
+| swap2.cpp:81:23:81:24 | z1 | swap2.cpp:82:5:82:6 | z1 | |
+| swap2.cpp:81:23:81:24 | z1 | swap2.cpp:83:10:83:11 | z1 | |
+| swap2.cpp:81:23:81:24 | z1 | swap2.cpp:85:10:85:11 | z1 | |
+| swap2.cpp:81:23:81:24 | z1 | swap2.cpp:88:10:88:11 | z1 | |
+| swap2.cpp:81:27:81:28 | z2 | swap2.cpp:85:14:85:15 | z2 | |
+| swap2.cpp:81:27:81:28 | z2 | swap2.cpp:87:10:87:11 | z2 | |
+| swap2.cpp:82:5:82:6 | z1 [post update] | swap2.cpp:83:10:83:11 | z1 | |
+| swap2.cpp:82:5:82:6 | z1 [post update] | swap2.cpp:85:10:85:11 | z1 | |
+| swap2.cpp:82:5:82:6 | z1 [post update] | swap2.cpp:88:10:88:11 | z1 | |
+| swap2.cpp:82:5:82:23 | ... = ... | swap2.cpp:82:8:82:12 | data1 [post update] | |
+| swap2.cpp:82:5:82:23 | ... = ... | swap2.cpp:83:13:83:17 | data1 | |
+| swap2.cpp:82:5:82:23 | ... = ... | swap2.cpp:88:13:88:17 | data1 | |
+| swap2.cpp:82:16:82:21 | call to source | swap2.cpp:82:5:82:23 | ... = ... | |
+| swap2.cpp:85:10:85:11 | ref arg z1 | swap2.cpp:88:10:88:11 | z1 | |
+| swap2.cpp:85:14:85:15 | ref arg z2 | swap2.cpp:87:10:87:11 | z2 | |
+| swap2.cpp:93:23:93:23 | x | swap2.cpp:95:5:95:5 | x | |
+| swap2.cpp:93:23:93:23 | x | swap2.cpp:97:10:97:10 | x | |
+| swap2.cpp:93:23:93:23 | x | swap2.cpp:100:19:100:19 | x | |
+| swap2.cpp:93:23:93:23 | x | swap2.cpp:103:10:103:10 | x | |
+| swap2.cpp:94:23:94:23 | y | swap2.cpp:98:10:98:10 | y | |
+| swap2.cpp:94:23:94:23 | y | swap2.cpp:100:5:100:5 | y | |
+| swap2.cpp:94:23:94:23 | y | swap2.cpp:102:10:102:10 | y | |
+| swap2.cpp:95:5:95:5 | x [post update] | swap2.cpp:97:10:97:10 | x | |
+| swap2.cpp:95:5:95:5 | x [post update] | swap2.cpp:100:19:100:19 | x | |
+| swap2.cpp:95:5:95:5 | x [post update] | swap2.cpp:103:10:103:10 | x | |
+| swap2.cpp:95:5:95:22 | ... = ... | swap2.cpp:95:7:95:11 | data1 [post update] | |
+| swap2.cpp:95:5:95:22 | ... = ... | swap2.cpp:97:12:97:16 | data1 | |
+| swap2.cpp:95:5:95:22 | ... = ... | swap2.cpp:103:12:103:16 | data1 | |
+| swap2.cpp:95:15:95:20 | call to source | swap2.cpp:95:5:95:22 | ... = ... | |
+| swap2.cpp:100:5:100:5 | ref arg y | swap2.cpp:102:10:102:10 | y | |
+| swap2.cpp:100:9:100:17 | call to move | swap2.cpp:100:5:100:5 | ref arg y | TAINT |
+| swap2.cpp:100:9:100:17 | call to move | swap2.cpp:100:7:100:7 | call to operator= | TAINT |
+| swap2.cpp:100:9:100:17 | ref arg call to move | swap2.cpp:100:19:100:19 | x [inner post update] | |
+| swap2.cpp:100:9:100:17 | ref arg call to move | swap2.cpp:103:10:103:10 | x | |
+| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:5:100:5 | ref arg y | TAINT |
+| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:7:100:7 | call to operator= | TAINT |
+| swap2.cpp:100:19:100:19 | x | swap2.cpp:100:9:100:17 | call to move | |
+| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:109:5:109:13 | move_from | |
+| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:111:10:111:18 | move_from | |
+| swap2.cpp:108:23:108:31 | move_from | swap2.cpp:113:41:113:49 | move_from | |
+| swap2.cpp:109:5:109:13 | move_from [post update] | swap2.cpp:111:10:111:18 | move_from | |
+| swap2.cpp:109:5:109:13 | move_from [post update] | swap2.cpp:113:41:113:49 | move_from | |
+| swap2.cpp:109:5:109:30 | ... = ... | swap2.cpp:109:15:109:19 | data1 [post update] | |
+| swap2.cpp:109:5:109:30 | ... = ... | swap2.cpp:111:20:111:24 | data1 | |
+| swap2.cpp:109:5:109:30 | ... = ... | swap2.cpp:115:18:115:22 | data1 | |
+| swap2.cpp:109:23:109:28 | call to source | swap2.cpp:109:5:109:30 | ... = ... | |
+| swap2.cpp:113:31:113:39 | call to move | swap2.cpp:113:31:113:51 | call to Class | |
+| swap2.cpp:113:31:113:39 | ref arg call to move | swap2.cpp:113:41:113:49 | move_from [inner post update] | |
+| swap2.cpp:113:31:113:51 | call to Class | swap2.cpp:115:10:115:16 | move_to | |
+| swap2.cpp:113:41:113:49 | move_from | swap2.cpp:113:31:113:39 | call to move | |
+| swap2.cpp:120:23:120:23 | x | swap2.cpp:122:5:122:5 | x | |
+| swap2.cpp:120:23:120:23 | x | swap2.cpp:124:10:124:10 | x | |
+| swap2.cpp:120:23:120:23 | x | swap2.cpp:127:19:127:19 | x | |
+| swap2.cpp:120:23:120:23 | x | swap2.cpp:130:10:130:10 | x | |
+| swap2.cpp:121:23:121:23 | y | swap2.cpp:125:10:125:10 | y | |
+| swap2.cpp:121:23:121:23 | y | swap2.cpp:127:5:127:5 | y | |
+| swap2.cpp:121:23:121:23 | y | swap2.cpp:129:10:129:10 | y | |
+| swap2.cpp:122:5:122:5 | x [post update] | swap2.cpp:124:10:124:10 | x | |
+| swap2.cpp:122:5:122:5 | x [post update] | swap2.cpp:127:19:127:19 | x | |
+| swap2.cpp:122:5:122:5 | x [post update] | swap2.cpp:130:10:130:10 | x | |
+| swap2.cpp:122:5:122:22 | ... = ... | swap2.cpp:122:7:122:11 | data1 [post update] | |
+| swap2.cpp:122:5:122:22 | ... = ... | swap2.cpp:124:12:124:16 | data1 | |
+| swap2.cpp:122:5:122:22 | ... = ... | swap2.cpp:130:12:130:16 | data1 | |
+| swap2.cpp:122:15:122:20 | call to source | swap2.cpp:122:5:122:22 | ... = ... | |
+| swap2.cpp:127:5:127:5 | ref arg y | swap2.cpp:129:10:129:10 | y | |
+| swap2.cpp:135:23:135:23 | x | swap2.cpp:137:5:137:5 | x | |
+| swap2.cpp:135:23:135:23 | x | swap2.cpp:139:10:139:10 | x | |
+| swap2.cpp:135:23:135:23 | x | swap2.cpp:142:29:142:29 | x | |
+| swap2.cpp:135:23:135:23 | x | swap2.cpp:145:10:145:10 | x | |
+| swap2.cpp:136:23:136:23 | y | swap2.cpp:140:10:140:10 | y | |
+| swap2.cpp:136:23:136:23 | y | swap2.cpp:142:5:142:5 | y | |
+| swap2.cpp:136:23:136:23 | y | swap2.cpp:144:10:144:10 | y | |
+| swap2.cpp:137:5:137:5 | x [post update] | swap2.cpp:139:10:139:10 | x | |
+| swap2.cpp:137:5:137:5 | x [post update] | swap2.cpp:142:29:142:29 | x | |
+| swap2.cpp:137:5:137:5 | x [post update] | swap2.cpp:145:10:145:10 | x | |
+| swap2.cpp:137:5:137:22 | ... = ... | swap2.cpp:137:7:137:11 | data1 [post update] | |
+| swap2.cpp:137:5:137:22 | ... = ... | swap2.cpp:139:12:139:16 | data1 | |
+| swap2.cpp:137:5:137:22 | ... = ... | swap2.cpp:145:12:145:16 | data1 | |
+| swap2.cpp:137:15:137:20 | call to source | swap2.cpp:137:5:137:22 | ... = ... | |
+| swap2.cpp:142:5:142:5 | ref arg y | swap2.cpp:144:10:144:10 | y | |
+| swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:142:29:142:29 | x [inner post update] | |
+| swap2.cpp:142:19:142:27 | ref arg call to move | swap2.cpp:145:10:145:10 | x | |
+| swap2.cpp:142:29:142:29 | x | swap2.cpp:142:19:142:27 | call to move | |
| taint.cpp:4:27:4:33 | source1 | taint.cpp:6:13:6:19 | source1 | |
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:5:8:5:13 | clean1 | |
| taint.cpp:4:40:4:45 | clean1 | taint.cpp:6:3:6:8 | clean1 | |
@@ -197,9 +5314,11 @@
| taint.cpp:7:3:7:8 | clean1 | taint.cpp:7:3:7:13 | ... += ... | TAINT |
| taint.cpp:7:3:7:13 | ... += ... | taint.cpp:8:8:8:13 | clean1 | |
| taint.cpp:7:13:7:13 | 1 | taint.cpp:7:3:7:13 | ... += ... | TAINT |
+| taint.cpp:10:12:10:18 | source1 | taint.cpp:10:12:10:22 | ... = ... | |
| taint.cpp:10:12:10:22 | ... = ... | taint.cpp:10:3:10:22 | ... = ... | |
| taint.cpp:10:12:10:22 | ... = ... | taint.cpp:11:8:11:13 | clean1 | |
| taint.cpp:10:22:10:22 | 1 | taint.cpp:10:12:10:22 | ... = ... | |
+| taint.cpp:12:13:12:18 | clean1 | taint.cpp:12:13:12:29 | ... = ... | |
| taint.cpp:12:13:12:29 | ... = ... | taint.cpp:12:3:12:29 | ... = ... | |
| taint.cpp:12:13:12:29 | ... = ... | taint.cpp:13:3:13:9 | source1 | |
| taint.cpp:12:22:12:27 | call to source | taint.cpp:12:13:12:29 | ... = ... | |
@@ -211,23 +5330,34 @@
| taint.cpp:15:3:15:14 | ... += ... | taint.cpp:16:8:16:14 | source1 | |
| taint.cpp:15:3:15:14 | ... += ... | taint.cpp:17:10:17:16 | source1 | |
| taint.cpp:15:14:15:14 | 1 | taint.cpp:15:3:15:14 | ... += ... | TAINT |
-| taint.cpp:17:10:17:16 | source1 | taint.cpp:17:8:17:16 | ++ ... | TAINT |
+| taint.cpp:17:10:17:16 | source1 | taint.cpp:17:8:17:16 | ++ ... | |
| taint.cpp:22:19:22:19 | x | taint.cpp:22:30:22:30 | x | |
| taint.cpp:22:30:22:30 | x | taint.cpp:22:30:22:34 | ... + ... | TAINT |
| taint.cpp:22:34:22:34 | 1 | taint.cpp:22:30:22:34 | ... + ... | TAINT |
| taint.cpp:27:15:27:21 | global2 | taint.cpp:27:15:27:25 | ... + ... | TAINT |
| taint.cpp:27:25:27:25 | 1 | taint.cpp:27:15:27:25 | ... + ... | TAINT |
+| taint.cpp:34:2:34:8 | global6 [post update] | taint.cpp:40:7:40:13 | global6 | |
+| taint.cpp:34:2:34:12 | ... = ... | taint.cpp:34:2:34:8 | global6 [post update] | |
| taint.cpp:34:12:34:12 | 0 | taint.cpp:34:2:34:12 | ... = ... | |
| taint.cpp:34:12:34:12 | 0 | taint.cpp:40:7:40:13 | global6 | |
+| taint.cpp:35:2:35:8 | global7 [post update] | taint.cpp:36:12:36:18 | global7 | |
+| taint.cpp:35:2:35:8 | global7 [post update] | taint.cpp:41:7:41:13 | global7 | |
+| taint.cpp:35:2:35:19 | ... = ... | taint.cpp:35:2:35:8 | global7 [post update] | |
| taint.cpp:35:12:35:17 | call to source | taint.cpp:35:2:35:19 | ... = ... | |
| taint.cpp:35:12:35:17 | call to source | taint.cpp:36:12:36:18 | global7 | |
| taint.cpp:35:12:35:17 | call to source | taint.cpp:41:7:41:13 | global7 | |
+| taint.cpp:36:2:36:8 | global8 [post update] | taint.cpp:42:7:42:13 | global8 | |
+| taint.cpp:36:2:36:22 | ... = ... | taint.cpp:36:2:36:8 | global8 [post update] | |
| taint.cpp:36:12:36:18 | global7 | taint.cpp:36:12:36:22 | ... + ... | TAINT |
| taint.cpp:36:12:36:22 | ... + ... | taint.cpp:36:2:36:22 | ... = ... | |
| taint.cpp:36:12:36:22 | ... + ... | taint.cpp:42:7:42:13 | global8 | |
| taint.cpp:36:22:36:22 | 1 | taint.cpp:36:12:36:22 | ... + ... | TAINT |
+| taint.cpp:37:2:37:8 | global9 [post update] | taint.cpp:43:7:43:13 | global9 | |
+| taint.cpp:37:2:37:30 | ... = ... | taint.cpp:37:2:37:8 | global9 [post update] | |
| taint.cpp:37:12:37:20 | call to increment | taint.cpp:37:2:37:30 | ... = ... | |
| taint.cpp:37:12:37:20 | call to increment | taint.cpp:43:7:43:13 | global9 | |
+| taint.cpp:38:2:38:9 | global10 [post update] | taint.cpp:44:7:44:14 | global10 | |
+| taint.cpp:38:2:38:26 | ... = ... | taint.cpp:38:2:38:9 | global10 [post update] | |
| taint.cpp:38:13:38:16 | call to zero | taint.cpp:38:2:38:26 | ... = ... | |
| taint.cpp:38:13:38:16 | call to zero | taint.cpp:44:7:44:14 | global10 | |
| taint.cpp:71:2:71:8 | this | taint.cpp:71:14:71:17 | constructor init of field a [pre-this] | |
@@ -239,9 +5369,12 @@
| taint.cpp:71:22:71:27 | call to source | taint.cpp:71:20:71:30 | constructor init of field b | TAINT |
| taint.cpp:72:3:72:3 | this | taint.cpp:73:3:73:3 | this | |
| taint.cpp:72:3:72:3 | this [post update] | taint.cpp:73:3:73:3 | this | |
+| taint.cpp:72:3:72:14 | ... = ... | taint.cpp:72:3:72:3 | c [post update] | |
| taint.cpp:72:7:72:12 | call to source | taint.cpp:72:3:72:14 | ... = ... | |
+| taint.cpp:73:3:73:7 | ... = ... | taint.cpp:73:3:73:3 | d [post update] | |
| taint.cpp:73:7:73:7 | 0 | taint.cpp:73:3:73:7 | ... = ... | |
| taint.cpp:76:7:76:14 | this | taint.cpp:77:3:77:3 | this | |
+| taint.cpp:77:3:77:14 | ... = ... | taint.cpp:77:3:77:3 | d [post update] | |
| taint.cpp:77:7:77:12 | call to source | taint.cpp:77:3:77:14 | ... = ... | |
| taint.cpp:84:10:84:12 | call to MyClass | taint.cpp:86:2:86:4 | mc1 | |
| taint.cpp:84:10:84:12 | call to MyClass | taint.cpp:88:7:88:9 | mc1 | |
@@ -261,31 +5394,49 @@
| taint.cpp:100:21:100:21 | i | taint.cpp:112:12:112:12 | i | |
| taint.cpp:100:21:100:21 | i | taint.cpp:114:12:114:12 | i | |
| taint.cpp:101:16:101:19 | {...} | taint.cpp:105:2:105:5 | arr1 | |
+| taint.cpp:101:16:101:19 | {...} | taint.cpp:109:7:109:10 | arr1 | |
+| taint.cpp:101:16:101:19 | {...} | taint.cpp:110:7:110:10 | arr1 | |
| taint.cpp:101:18:101:18 | 0 | taint.cpp:101:16:101:19 | {...} | TAINT |
| taint.cpp:102:16:102:19 | {...} | taint.cpp:106:2:106:5 | arr2 | |
+| taint.cpp:102:16:102:19 | {...} | taint.cpp:111:7:111:10 | arr2 | |
+| taint.cpp:102:16:102:19 | {...} | taint.cpp:112:7:112:10 | arr2 | |
| taint.cpp:102:18:102:18 | 0 | taint.cpp:102:16:102:19 | {...} | TAINT |
| taint.cpp:103:16:103:19 | {...} | taint.cpp:107:2:107:5 | arr3 | |
+| taint.cpp:103:16:103:19 | {...} | taint.cpp:113:7:113:10 | arr3 | |
+| taint.cpp:103:16:103:19 | {...} | taint.cpp:114:7:114:10 | arr3 | |
| taint.cpp:103:18:103:18 | 0 | taint.cpp:103:16:103:19 | {...} | TAINT |
-| taint.cpp:105:2:105:5 | arr1 | taint.cpp:105:2:105:8 | access to array | TAINT |
+| taint.cpp:105:2:105:5 | arr1 | taint.cpp:105:2:105:8 | access to array | |
+| taint.cpp:105:2:105:8 | access to array [post update] | taint.cpp:105:2:105:5 | arr1 [inner post update] | |
+| taint.cpp:105:2:105:8 | access to array [post update] | taint.cpp:109:7:109:10 | arr1 | |
+| taint.cpp:105:2:105:8 | access to array [post update] | taint.cpp:110:7:110:10 | arr1 | |
+| taint.cpp:105:2:105:19 | ... = ... | taint.cpp:105:2:105:8 | access to array [post update] | |
| taint.cpp:105:7:105:7 | 5 | taint.cpp:105:2:105:8 | access to array | TAINT |
| taint.cpp:105:12:105:17 | call to source | taint.cpp:105:2:105:19 | ... = ... | |
-| taint.cpp:106:2:106:5 | arr2 | taint.cpp:106:2:106:8 | access to array | TAINT |
+| taint.cpp:106:2:106:5 | arr2 | taint.cpp:106:2:106:8 | access to array | |
+| taint.cpp:106:2:106:8 | access to array [post update] | taint.cpp:106:2:106:5 | arr2 [inner post update] | |
+| taint.cpp:106:2:106:8 | access to array [post update] | taint.cpp:111:7:111:10 | arr2 | |
+| taint.cpp:106:2:106:8 | access to array [post update] | taint.cpp:112:7:112:10 | arr2 | |
+| taint.cpp:106:2:106:19 | ... = ... | taint.cpp:106:2:106:8 | access to array [post update] | |
| taint.cpp:106:7:106:7 | i | taint.cpp:106:2:106:8 | access to array | TAINT |
| taint.cpp:106:12:106:17 | call to source | taint.cpp:106:2:106:19 | ... = ... | |
-| taint.cpp:107:2:107:5 | arr3 | taint.cpp:107:2:107:8 | access to array | TAINT |
+| taint.cpp:107:2:107:5 | arr3 | taint.cpp:107:2:107:8 | access to array | |
+| taint.cpp:107:2:107:8 | access to array [post update] | taint.cpp:107:2:107:5 | arr3 [inner post update] | |
+| taint.cpp:107:2:107:8 | access to array [post update] | taint.cpp:113:7:113:10 | arr3 | |
+| taint.cpp:107:2:107:8 | access to array [post update] | taint.cpp:114:7:114:10 | arr3 | |
+| taint.cpp:107:2:107:12 | ... = ... | taint.cpp:107:2:107:8 | access to array [post update] | |
| taint.cpp:107:7:107:7 | 5 | taint.cpp:107:2:107:8 | access to array | TAINT |
| taint.cpp:107:12:107:12 | 0 | taint.cpp:107:2:107:12 | ... = ... | |
-| taint.cpp:109:7:109:10 | arr1 | taint.cpp:109:7:109:13 | access to array | TAINT |
+| taint.cpp:109:7:109:10 | arr1 | taint.cpp:109:7:109:13 | access to array | |
| taint.cpp:109:12:109:12 | 5 | taint.cpp:109:7:109:13 | access to array | TAINT |
-| taint.cpp:110:7:110:10 | arr1 | taint.cpp:110:7:110:13 | access to array | TAINT |
+| taint.cpp:110:7:110:10 | arr1 | taint.cpp:110:7:110:13 | access to array | |
| taint.cpp:110:12:110:12 | i | taint.cpp:110:7:110:13 | access to array | TAINT |
-| taint.cpp:111:7:111:10 | arr2 | taint.cpp:111:7:111:13 | access to array | TAINT |
+| taint.cpp:111:7:111:10 | arr2 | taint.cpp:111:7:111:13 | access to array | |
| taint.cpp:111:12:111:12 | 5 | taint.cpp:111:7:111:13 | access to array | TAINT |
-| taint.cpp:112:7:112:10 | arr2 | taint.cpp:112:7:112:13 | access to array | TAINT |
+| taint.cpp:112:7:112:10 | arr2 | taint.cpp:112:7:112:13 | access to array | |
| taint.cpp:112:12:112:12 | i | taint.cpp:112:7:112:13 | access to array | TAINT |
-| taint.cpp:113:7:113:10 | arr3 | taint.cpp:113:7:113:13 | access to array | TAINT |
+| taint.cpp:113:7:113:10 | arr3 | taint.cpp:113:7:113:13 | access to array | |
| taint.cpp:113:12:113:12 | 5 | taint.cpp:113:7:113:13 | access to array | TAINT |
-| taint.cpp:114:7:114:10 | arr3 | taint.cpp:114:7:114:13 | access to array | TAINT |
+| taint.cpp:114:7:114:10 | arr3 | taint.cpp:114:7:114:13 | access to array | |
| taint.cpp:114:12:114:12 | i | taint.cpp:114:7:114:13 | access to array | TAINT |
| taint.cpp:120:11:120:16 | call to source | taint.cpp:123:13:123:14 | t1 | |
| taint.cpp:120:11:120:16 | call to source | taint.cpp:133:8:133:9 | t1 | |
@@ -298,6 +5449,9 @@
| taint.cpp:124:13:124:14 | t2 | taint.cpp:124:12:124:14 | & ... | |
| taint.cpp:125:12:125:14 | & ... | taint.cpp:131:8:131:9 | p3 | |
| taint.cpp:125:13:125:14 | t3 | taint.cpp:125:12:125:14 | & ... | |
+| taint.cpp:127:2:127:4 | * ... [post update] | taint.cpp:127:3:127:4 | p2 [inner post update] | |
+| taint.cpp:127:2:127:4 | * ... [post update] | taint.cpp:130:8:130:9 | p2 | |
+| taint.cpp:127:2:127:15 | ... = ... | taint.cpp:127:2:127:4 | * ... [post update] | |
| taint.cpp:127:3:127:4 | p2 | taint.cpp:127:2:127:4 | * ... | TAINT |
| taint.cpp:127:8:127:13 | call to source | taint.cpp:127:2:127:15 | ... = ... | |
| taint.cpp:129:8:129:9 | p1 | taint.cpp:129:7:129:9 | * ... | TAINT |
@@ -309,6 +5463,9 @@
| taint.cpp:133:7:133:9 | & ... | taint.cpp:137:8:137:9 | p3 | |
| taint.cpp:133:8:133:9 | t1 | taint.cpp:133:7:133:9 | & ... | |
| taint.cpp:134:8:134:9 | p3 | taint.cpp:134:7:134:9 | * ... | TAINT |
+| taint.cpp:136:2:136:4 | * ... [post update] | taint.cpp:136:3:136:4 | p3 [inner post update] | |
+| taint.cpp:136:2:136:4 | * ... [post update] | taint.cpp:137:8:137:9 | p3 | |
+| taint.cpp:136:2:136:8 | ... = ... | taint.cpp:136:2:136:4 | * ... [post update] | |
| taint.cpp:136:3:136:4 | p3 | taint.cpp:136:2:136:4 | * ... | TAINT |
| taint.cpp:136:8:136:8 | 0 | taint.cpp:136:2:136:8 | ... = ... | |
| taint.cpp:137:8:137:9 | p3 | taint.cpp:137:7:137:9 | * ... | TAINT |
@@ -401,6 +5558,7 @@
| taint.cpp:235:15:235:15 | this | taint.cpp:236:3:236:6 | this | |
| taint.cpp:236:3:236:6 | this | taint.cpp:237:3:237:6 | this | |
| taint.cpp:237:3:237:6 | this | taint.cpp:238:3:238:14 | this | |
+| taint.cpp:238:3:238:14 | ... = ... | taint.cpp:238:3:238:14 | v [post update] | |
| taint.cpp:238:7:238:12 | call to source | taint.cpp:238:3:238:14 | ... = ... | |
| taint.cpp:243:10:246:2 | [...](...){...} | taint.cpp:247:2:247:2 | c | |
| taint.cpp:243:10:246:2 | {...} | taint.cpp:243:10:246:2 | [...](...){...} | |
@@ -419,6 +5577,8 @@
| taint.cpp:255:19:255:19 | a | taint.cpp:256:8:256:8 | a | |
| taint.cpp:255:27:255:27 | b | taint.cpp:255:27:255:27 | b | |
| taint.cpp:255:27:255:27 | b | taint.cpp:257:8:257:8 | b | |
+| taint.cpp:258:3:258:3 | c [post update] | taint.cpp:255:35:255:35 | c | |
+| taint.cpp:258:3:258:14 | ... = ... | taint.cpp:258:3:258:3 | c [post update] | |
| taint.cpp:258:7:258:12 | call to source | taint.cpp:255:35:255:35 | c | |
| taint.cpp:258:7:258:12 | call to source | taint.cpp:258:3:258:14 | ... = ... | |
| taint.cpp:260:10:260:10 | ref arg w | taint.cpp:261:7:261:7 | w | |
@@ -443,13 +5603,19 @@
| taint.cpp:287:6:287:7 | call to id | taint.cpp:292:7:292:7 | z | |
| taint.cpp:297:29:297:29 | b | taint.cpp:297:29:297:29 | b | |
| taint.cpp:297:29:297:29 | b | taint.cpp:299:6:299:6 | b | |
+| taint.cpp:299:2:299:2 | a [post update] | taint.cpp:297:21:297:21 | a | |
+| taint.cpp:299:2:299:6 | ... = ... | taint.cpp:299:2:299:2 | a [post update] | |
| taint.cpp:299:6:299:6 | b | taint.cpp:297:21:297:21 | a | |
| taint.cpp:299:6:299:6 | b | taint.cpp:299:2:299:6 | ... = ... | |
| taint.cpp:302:28:302:28 | b | taint.cpp:304:6:304:6 | b | |
+| taint.cpp:304:2:304:2 | a [post update] | taint.cpp:302:21:302:21 | a | |
+| taint.cpp:304:2:304:6 | ... = ... | taint.cpp:304:2:304:2 | a [post update] | |
| taint.cpp:304:6:304:6 | b | taint.cpp:302:21:302:21 | a | |
| taint.cpp:304:6:304:6 | b | taint.cpp:304:2:304:6 | ... = ... | |
| taint.cpp:307:21:307:21 | a | taint.cpp:309:3:309:3 | a | |
| taint.cpp:307:28:307:28 | b | taint.cpp:309:7:309:7 | b | |
+| taint.cpp:309:2:309:3 | * ... [post update] | taint.cpp:309:3:309:3 | a [inner post update] | |
+| taint.cpp:309:2:309:7 | ... = ... | taint.cpp:309:2:309:3 | * ... [post update] | |
| taint.cpp:309:3:309:3 | a | taint.cpp:309:2:309:3 | * ... | TAINT |
| taint.cpp:309:7:309:7 | b | taint.cpp:309:2:309:7 | ... = ... | |
| taint.cpp:312:21:312:21 | a | taint.cpp:317:3:317:3 | a | |
@@ -458,14 +5624,20 @@
| taint.cpp:316:6:316:10 | ... + ... | taint.cpp:316:2:316:10 | ... = ... | |
| taint.cpp:316:6:316:10 | ... + ... | taint.cpp:317:7:317:7 | c | |
| taint.cpp:316:10:316:10 | 1 | taint.cpp:316:6:316:10 | ... + ... | TAINT |
+| taint.cpp:317:2:317:3 | * ... [post update] | taint.cpp:317:3:317:3 | a [inner post update] | |
+| taint.cpp:317:2:317:7 | ... = ... | taint.cpp:317:2:317:3 | * ... [post update] | |
| taint.cpp:317:3:317:3 | a | taint.cpp:317:2:317:3 | * ... | TAINT |
| taint.cpp:317:7:317:7 | c | taint.cpp:317:2:317:7 | ... = ... | |
| taint.cpp:320:23:320:23 | a | taint.cpp:322:6:322:6 | a | |
| taint.cpp:320:31:320:31 | b | taint.cpp:323:6:323:6 | b | |
+| taint.cpp:322:2:322:2 | a [post update] | taint.cpp:320:23:320:23 | a | |
+| taint.cpp:322:2:322:10 | ... = ... | taint.cpp:322:2:322:2 | a [post update] | |
| taint.cpp:322:6:322:6 | a | taint.cpp:322:6:322:10 | ... + ... | TAINT |
| taint.cpp:322:6:322:10 | ... + ... | taint.cpp:320:23:320:23 | a | |
| taint.cpp:322:6:322:10 | ... + ... | taint.cpp:322:2:322:10 | ... = ... | |
| taint.cpp:322:10:322:10 | 1 | taint.cpp:322:6:322:10 | ... + ... | TAINT |
+| taint.cpp:323:2:323:2 | b [post update] | taint.cpp:320:31:320:31 | b | |
+| taint.cpp:323:2:323:10 | ... = ... | taint.cpp:323:2:323:2 | b [post update] | |
| taint.cpp:323:6:323:6 | b | taint.cpp:323:6:323:10 | ... + ... | TAINT |
| taint.cpp:323:6:323:10 | ... + ... | taint.cpp:320:31:320:31 | b | |
| taint.cpp:323:6:323:10 | ... + ... | taint.cpp:323:2:323:10 | ... = ... | |
@@ -537,17 +5709,20 @@
| taint.cpp:390:6:390:11 | call to wcsdup | taint.cpp:390:2:390:28 | ... = ... | |
| taint.cpp:390:6:390:11 | call to wcsdup | taint.cpp:392:7:392:7 | b | |
| taint.cpp:390:13:390:27 | hello, world | taint.cpp:390:6:390:11 | call to wcsdup | TAINT |
+| taint.cpp:417:13:417:13 | 0 | taint.cpp:417:13:417:14 | call to MyClass2 | TAINT |
| taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:420:7:420:7 | a | |
| taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:421:7:421:7 | a | |
| taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:422:2:422:2 | a | |
| taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:423:7:423:7 | a | |
| taint.cpp:417:13:417:14 | call to MyClass2 | taint.cpp:424:7:424:7 | a | |
+| taint.cpp:417:19:417:19 | 0 | taint.cpp:417:19:417:20 | call to MyClass2 | TAINT |
| taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:426:7:426:7 | b | |
| taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:427:7:427:7 | b | |
| taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:428:2:428:2 | b | |
| taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:429:7:429:7 | b | |
| taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:430:7:430:7 | b | |
| taint.cpp:417:19:417:20 | call to MyClass2 | taint.cpp:431:7:431:7 | b | |
+| taint.cpp:418:13:418:14 | | taint.cpp:418:13:418:15 | call to MyClass3 | TAINT |
| taint.cpp:418:13:418:15 | call to MyClass3 | taint.cpp:443:7:443:7 | d | |
| taint.cpp:418:13:418:15 | call to MyClass3 | taint.cpp:444:7:444:7 | d | |
| taint.cpp:418:13:418:15 | call to MyClass3 | taint.cpp:445:2:445:2 | d | |
@@ -565,6 +5740,7 @@
| taint.cpp:428:2:428:2 | b [post update] | taint.cpp:429:7:429:7 | b | |
| taint.cpp:428:2:428:2 | b [post update] | taint.cpp:430:7:430:7 | b | |
| taint.cpp:428:2:428:2 | b [post update] | taint.cpp:431:7:431:7 | b | |
+| taint.cpp:428:2:428:20 | ... = ... | taint.cpp:428:4:428:9 | member [post update] | |
| taint.cpp:428:2:428:20 | ... = ... | taint.cpp:430:9:430:14 | member | |
| taint.cpp:428:13:428:18 | call to source | taint.cpp:428:2:428:20 | ... = ... | |
| taint.cpp:433:6:433:20 | call to MyClass2 | taint.cpp:433:6:433:20 | new | |
@@ -575,6 +5751,7 @@
| taint.cpp:433:6:433:20 | new | taint.cpp:438:7:438:7 | c | |
| taint.cpp:433:6:433:20 | new | taint.cpp:439:7:439:7 | c | |
| taint.cpp:433:6:433:20 | new | taint.cpp:441:9:441:9 | c | |
+| taint.cpp:433:19:433:19 | 0 | taint.cpp:433:6:433:20 | call to MyClass2 | TAINT |
| taint.cpp:435:7:435:7 | ref arg c | taint.cpp:436:7:436:7 | c | |
| taint.cpp:435:7:435:7 | ref arg c | taint.cpp:437:2:437:2 | c | |
| taint.cpp:435:7:435:7 | ref arg c | taint.cpp:438:7:438:7 | c | |
@@ -599,8 +5776,12 @@
| taint.cpp:452:16:452:16 | a | taint.cpp:454:10:454:10 | a | |
| taint.cpp:452:24:452:24 | b | taint.cpp:455:6:455:6 | b | |
| taint.cpp:454:10:454:10 | a | taint.cpp:456:6:456:6 | c | |
+| taint.cpp:455:2:455:2 | a [post update] | taint.cpp:452:16:452:16 | a | |
+| taint.cpp:455:2:455:6 | ... = ... | taint.cpp:455:2:455:2 | a [post update] | |
| taint.cpp:455:6:455:6 | b | taint.cpp:452:16:452:16 | a | |
| taint.cpp:455:6:455:6 | b | taint.cpp:455:2:455:6 | ... = ... | |
+| taint.cpp:456:2:456:2 | b [post update] | taint.cpp:452:24:452:24 | b | |
+| taint.cpp:456:2:456:6 | ... = ... | taint.cpp:456:2:456:2 | b [post update] | |
| taint.cpp:456:6:456:6 | c | taint.cpp:452:24:452:24 | b | |
| taint.cpp:456:6:456:6 | c | taint.cpp:456:2:456:6 | ... = ... | |
| taint.cpp:462:6:462:11 | call to source | taint.cpp:462:2:462:13 | ... = ... | |
@@ -623,3 +5804,1239 @@
| taint.cpp:483:18:483:19 | ref arg & ... | taint.cpp:483:19:483:19 | n [inner post update] | |
| taint.cpp:483:19:483:19 | n | taint.cpp:483:18:483:19 | & ... | |
| taint.cpp:483:28:483:34 | source1 | taint.cpp:483:11:483:15 | ref arg & ... | TAINT |
+| vector.cpp:16:43:16:49 | source1 | vector.cpp:17:26:17:32 | source1 | |
+| vector.cpp:16:43:16:49 | source1 | vector.cpp:31:38:31:44 | source1 | |
+| vector.cpp:17:21:17:33 | call to vector | vector.cpp:19:14:19:14 | v | |
+| vector.cpp:17:21:17:33 | call to vector | vector.cpp:23:38:23:38 | v | |
+| vector.cpp:17:21:17:33 | call to vector | vector.cpp:23:55:23:55 | v | |
+| vector.cpp:17:21:17:33 | call to vector | vector.cpp:27:15:27:15 | v | |
+| vector.cpp:17:21:17:33 | call to vector | vector.cpp:35:1:35:1 | v | |
+| vector.cpp:17:26:17:32 | source1 | vector.cpp:17:21:17:33 | call to vector | TAINT |
+| vector.cpp:19:14:19:14 | (__begin) | vector.cpp:19:14:19:14 | call to operator* | TAINT |
+| vector.cpp:19:14:19:14 | (__begin) | vector.cpp:19:14:19:14 | call to operator++ | |
+| vector.cpp:19:14:19:14 | (__end) | vector.cpp:19:14:19:14 | call to iterator | |
+| vector.cpp:19:14:19:14 | (__range) | vector.cpp:19:14:19:14 | call to begin | TAINT |
+| vector.cpp:19:14:19:14 | (__range) | vector.cpp:19:14:19:14 | call to end | TAINT |
+| vector.cpp:19:14:19:14 | call to begin | vector.cpp:19:14:19:14 | (__begin) | |
+| vector.cpp:19:14:19:14 | call to begin | vector.cpp:19:14:19:14 | (__begin) | |
+| vector.cpp:19:14:19:14 | call to begin | vector.cpp:19:14:19:14 | (__begin) | |
+| vector.cpp:19:14:19:14 | call to end | vector.cpp:19:14:19:14 | (__end) | |
+| vector.cpp:19:14:19:14 | call to operator* | vector.cpp:20:8:20:8 | x | |
+| vector.cpp:19:14:19:14 | ref arg (__begin) | vector.cpp:19:14:19:14 | (__begin) | |
+| vector.cpp:19:14:19:14 | ref arg (__begin) | vector.cpp:19:14:19:14 | (__begin) | |
+| vector.cpp:19:14:19:14 | ref arg (__begin) | vector.cpp:19:14:19:14 | (__begin) | |
+| vector.cpp:19:14:19:14 | ref arg (__range) | vector.cpp:19:14:19:14 | (__range) | |
+| vector.cpp:19:14:19:14 | v | vector.cpp:19:14:19:14 | (__range) | |
+| vector.cpp:19:14:19:14 | v | vector.cpp:19:14:19:14 | (__range) | |
+| vector.cpp:19:14:19:14 | v | vector.cpp:19:14:19:14 | call to operator* | TAINT |
+| vector.cpp:23:38:23:38 | ref arg v | vector.cpp:23:55:23:55 | v | |
+| vector.cpp:23:38:23:38 | ref arg v | vector.cpp:27:15:27:15 | v | |
+| vector.cpp:23:38:23:38 | ref arg v | vector.cpp:35:1:35:1 | v | |
+| vector.cpp:23:38:23:38 | v | vector.cpp:23:40:23:44 | call to begin | TAINT |
+| vector.cpp:23:40:23:44 | call to begin | vector.cpp:23:49:23:50 | it | |
+| vector.cpp:23:40:23:44 | call to begin | vector.cpp:23:66:23:67 | it | |
+| vector.cpp:23:40:23:44 | call to begin | vector.cpp:24:9:24:10 | it | |
+| vector.cpp:23:55:23:55 | ref arg v | vector.cpp:23:55:23:55 | v | |
+| vector.cpp:23:55:23:55 | ref arg v | vector.cpp:27:15:27:15 | v | |
+| vector.cpp:23:55:23:55 | ref arg v | vector.cpp:35:1:35:1 | v | |
+| vector.cpp:23:55:23:55 | v | vector.cpp:23:57:23:59 | call to end | TAINT |
+| vector.cpp:23:66:23:67 | it | vector.cpp:23:64:23:64 | call to operator++ | |
+| vector.cpp:23:66:23:67 | ref arg it | vector.cpp:23:49:23:50 | it | |
+| vector.cpp:23:66:23:67 | ref arg it | vector.cpp:23:66:23:67 | it | |
+| vector.cpp:23:66:23:67 | ref arg it | vector.cpp:24:9:24:10 | it | |
+| vector.cpp:24:9:24:10 | it | vector.cpp:24:8:24:8 | call to operator* | TAINT |
+| vector.cpp:27:15:27:15 | (__begin) | vector.cpp:27:15:27:15 | call to operator* | TAINT |
+| vector.cpp:27:15:27:15 | (__begin) | vector.cpp:27:15:27:15 | call to operator++ | |
+| vector.cpp:27:15:27:15 | (__end) | vector.cpp:27:15:27:15 | call to iterator | |
+| vector.cpp:27:15:27:15 | (__range) | vector.cpp:27:15:27:15 | call to begin | TAINT |
+| vector.cpp:27:15:27:15 | (__range) | vector.cpp:27:15:27:15 | call to end | TAINT |
+| vector.cpp:27:15:27:15 | call to begin | vector.cpp:27:15:27:15 | (__begin) | |
+| vector.cpp:27:15:27:15 | call to begin | vector.cpp:27:15:27:15 | (__begin) | |
+| vector.cpp:27:15:27:15 | call to begin | vector.cpp:27:15:27:15 | (__begin) | |
+| vector.cpp:27:15:27:15 | call to end | vector.cpp:27:15:27:15 | (__end) | |
+| vector.cpp:27:15:27:15 | call to operator* | vector.cpp:28:8:28:8 | x | |
+| vector.cpp:27:15:27:15 | ref arg (__begin) | vector.cpp:27:15:27:15 | (__begin) | |
+| vector.cpp:27:15:27:15 | ref arg (__begin) | vector.cpp:27:15:27:15 | (__begin) | |
+| vector.cpp:27:15:27:15 | ref arg (__begin) | vector.cpp:27:15:27:15 | (__begin) | |
+| vector.cpp:27:15:27:15 | ref arg (__range) | vector.cpp:27:15:27:15 | (__range) | |
+| vector.cpp:27:15:27:15 | v | vector.cpp:27:15:27:15 | (__range) | |
+| vector.cpp:27:15:27:15 | v | vector.cpp:27:15:27:15 | (__range) | |
+| vector.cpp:27:15:27:15 | v | vector.cpp:27:15:27:15 | call to operator* | TAINT |
+| vector.cpp:31:33:31:45 | call to vector | vector.cpp:32:21:32:27 | const_v | |
+| vector.cpp:31:33:31:45 | call to vector | vector.cpp:35:1:35:1 | const_v | |
+| vector.cpp:31:38:31:44 | source1 | vector.cpp:31:33:31:45 | call to vector | TAINT |
+| vector.cpp:32:21:32:21 | (__begin) | vector.cpp:32:21:32:21 | call to operator* | TAINT |
+| vector.cpp:32:21:32:21 | (__begin) | vector.cpp:32:21:32:21 | call to operator++ | |
+| vector.cpp:32:21:32:21 | (__range) | vector.cpp:32:21:32:21 | call to begin | TAINT |
+| vector.cpp:32:21:32:21 | (__range) | vector.cpp:32:21:32:21 | call to end | TAINT |
+| vector.cpp:32:21:32:21 | call to begin | vector.cpp:32:21:32:21 | (__begin) | |
+| vector.cpp:32:21:32:21 | call to begin | vector.cpp:32:21:32:21 | (__begin) | |
+| vector.cpp:32:21:32:21 | call to begin | vector.cpp:32:21:32:21 | (__begin) | |
+| vector.cpp:32:21:32:21 | call to end | vector.cpp:32:21:32:21 | (__end) | |
+| vector.cpp:32:21:32:21 | call to operator* | vector.cpp:33:8:33:8 | x | |
+| vector.cpp:32:21:32:21 | ref arg (__begin) | vector.cpp:32:21:32:21 | (__begin) | |
+| vector.cpp:32:21:32:21 | ref arg (__begin) | vector.cpp:32:21:32:21 | (__begin) | |
+| vector.cpp:32:21:32:21 | ref arg (__begin) | vector.cpp:32:21:32:21 | (__begin) | |
+| vector.cpp:32:21:32:27 | const_v | vector.cpp:32:21:32:21 | (__range) | |
+| vector.cpp:32:21:32:27 | const_v | vector.cpp:32:21:32:21 | (__range) | |
+| vector.cpp:32:21:32:27 | const_v | vector.cpp:32:21:32:21 | call to operator* | TAINT |
+| vector.cpp:37:29:37:29 | x | vector.cpp:42:5:42:5 | x | |
+| vector.cpp:37:29:37:29 | x | vector.cpp:47:10:47:10 | x | |
+| vector.cpp:37:29:37:29 | x | vector.cpp:55:10:55:10 | x | |
+| vector.cpp:37:29:37:29 | x | vector.cpp:61:10:61:10 | x | |
+| vector.cpp:37:29:37:29 | x | vector.cpp:63:5:63:5 | x | |
+| vector.cpp:37:29:37:29 | x | vector.cpp:67:10:67:10 | x | |
+| vector.cpp:37:29:37:29 | x | vector.cpp:96:8:96:8 | x | |
+| vector.cpp:37:29:37:29 | x | vector.cpp:100:13:100:13 | x | |
+| vector.cpp:38:22:38:24 | call to vector | vector.cpp:40:2:40:3 | v1 | |
+| vector.cpp:38:22:38:24 | call to vector | vector.cpp:41:2:41:3 | v1 | |
+| vector.cpp:38:22:38:24 | call to vector | vector.cpp:42:2:42:3 | v1 | |
+| vector.cpp:38:22:38:24 | call to vector | vector.cpp:43:2:43:3 | v1 | |
+| vector.cpp:38:22:38:24 | call to vector | vector.cpp:44:7:44:8 | v1 | |
+| vector.cpp:38:22:38:24 | call to vector | vector.cpp:45:7:45:8 | v1 | |
+| vector.cpp:38:22:38:24 | call to vector | vector.cpp:46:7:46:8 | v1 | |
+| vector.cpp:38:22:38:24 | call to vector | vector.cpp:47:7:47:8 | v1 | |
+| vector.cpp:38:22:38:24 | call to vector | vector.cpp:48:7:48:8 | v1 | |
+| vector.cpp:38:22:38:24 | call to vector | vector.cpp:49:7:49:8 | v1 | |
+| vector.cpp:38:22:38:24 | call to vector | vector.cpp:101:1:101:1 | v1 | |
+| vector.cpp:38:30:38:32 | call to vector | vector.cpp:51:2:51:3 | v2 | |
+| vector.cpp:38:30:38:32 | call to vector | vector.cpp:52:7:52:8 | v2 | |
+| vector.cpp:38:30:38:32 | call to vector | vector.cpp:53:7:53:8 | v2 | |
+| vector.cpp:38:30:38:32 | call to vector | vector.cpp:54:7:54:8 | v2 | |
+| vector.cpp:38:30:38:32 | call to vector | vector.cpp:55:7:55:8 | v2 | |
+| vector.cpp:38:30:38:32 | call to vector | vector.cpp:57:7:57:8 | v2 | |
+| vector.cpp:38:30:38:32 | call to vector | vector.cpp:101:1:101:1 | v2 | |
+| vector.cpp:38:38:38:40 | call to vector | vector.cpp:57:2:57:3 | v3 | |
+| vector.cpp:38:38:38:40 | call to vector | vector.cpp:58:7:58:8 | v3 | |
+| vector.cpp:38:38:38:40 | call to vector | vector.cpp:59:7:59:8 | v3 | |
+| vector.cpp:38:38:38:40 | call to vector | vector.cpp:60:7:60:8 | v3 | |
+| vector.cpp:38:38:38:40 | call to vector | vector.cpp:61:7:61:8 | v3 | |
+| vector.cpp:38:38:38:40 | call to vector | vector.cpp:101:1:101:1 | v3 | |
+| vector.cpp:38:46:38:48 | call to vector | vector.cpp:63:2:63:3 | v4 | |
+| vector.cpp:38:46:38:48 | call to vector | vector.cpp:64:7:64:8 | v4 | |
+| vector.cpp:38:46:38:48 | call to vector | vector.cpp:65:7:65:8 | v4 | |
+| vector.cpp:38:46:38:48 | call to vector | vector.cpp:66:7:66:8 | v4 | |
+| vector.cpp:38:46:38:48 | call to vector | vector.cpp:67:7:67:8 | v4 | |
+| vector.cpp:38:46:38:48 | call to vector | vector.cpp:101:1:101:1 | v4 | |
+| vector.cpp:38:54:38:56 | call to vector | vector.cpp:69:2:69:3 | v5 | |
+| vector.cpp:38:54:38:56 | call to vector | vector.cpp:70:7:70:8 | v5 | |
+| vector.cpp:38:54:38:56 | call to vector | vector.cpp:71:7:71:8 | v5 | |
+| vector.cpp:38:54:38:56 | call to vector | vector.cpp:72:7:72:8 | v5 | |
+| vector.cpp:38:54:38:56 | call to vector | vector.cpp:101:1:101:1 | v5 | |
+| vector.cpp:38:62:38:64 | call to vector | vector.cpp:74:2:74:3 | v6 | |
+| vector.cpp:38:62:38:64 | call to vector | vector.cpp:75:7:75:8 | v6 | |
+| vector.cpp:38:62:38:64 | call to vector | vector.cpp:76:7:76:8 | v6 | |
+| vector.cpp:38:62:38:64 | call to vector | vector.cpp:101:1:101:1 | v6 | |
+| vector.cpp:38:70:38:72 | call to vector | vector.cpp:80:41:80:42 | v7 | |
+| vector.cpp:38:70:38:72 | call to vector | vector.cpp:81:3:81:4 | v7 | |
+| vector.cpp:38:70:38:72 | call to vector | vector.cpp:83:7:83:8 | v7 | |
+| vector.cpp:38:70:38:72 | call to vector | vector.cpp:84:7:84:8 | v7 | |
+| vector.cpp:38:70:38:72 | call to vector | vector.cpp:85:7:85:8 | v7 | |
+| vector.cpp:38:70:38:72 | call to vector | vector.cpp:101:1:101:1 | v7 | |
+| vector.cpp:38:78:38:80 | call to vector | vector.cpp:88:33:88:34 | v8 | |
+| vector.cpp:38:78:38:80 | call to vector | vector.cpp:90:3:90:4 | v8 | |
+| vector.cpp:38:78:38:80 | call to vector | vector.cpp:92:7:92:8 | v8 | |
+| vector.cpp:38:78:38:80 | call to vector | vector.cpp:93:7:93:8 | v8 | |
+| vector.cpp:38:78:38:80 | call to vector | vector.cpp:94:7:94:8 | v8 | |
+| vector.cpp:38:78:38:80 | call to vector | vector.cpp:101:1:101:1 | v8 | |
+| vector.cpp:38:86:38:88 | call to vector | vector.cpp:96:2:96:3 | v9 | |
+| vector.cpp:38:86:38:88 | call to vector | vector.cpp:97:7:97:8 | v9 | |
+| vector.cpp:38:86:38:88 | call to vector | vector.cpp:98:7:98:8 | v9 | |
+| vector.cpp:38:86:38:88 | call to vector | vector.cpp:99:7:99:8 | v9 | |
+| vector.cpp:38:86:38:88 | call to vector | vector.cpp:100:7:100:8 | v9 | |
+| vector.cpp:38:86:38:88 | call to vector | vector.cpp:101:1:101:1 | v9 | |
+| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:41:2:41:3 | v1 | |
+| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:42:2:42:3 | v1 | |
+| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:43:2:43:3 | v1 | |
+| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:44:7:44:8 | v1 | |
+| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:45:7:45:8 | v1 | |
+| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | |
+| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | |
+| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | |
+| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | |
+| vector.cpp:40:2:40:3 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | |
+| vector.cpp:40:2:40:3 | v1 | vector.cpp:40:4:40:4 | call to operator[] | TAINT |
+| vector.cpp:40:2:40:10 | ... = ... | vector.cpp:40:4:40:4 | call to operator[] [post update] | |
+| vector.cpp:40:4:40:4 | call to operator[] [post update] | vector.cpp:40:2:40:3 | ref arg v1 | TAINT |
+| vector.cpp:40:10:40:10 | 0 | vector.cpp:40:2:40:10 | ... = ... | |
+| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:42:2:42:3 | v1 | |
+| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:43:2:43:3 | v1 | |
+| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:44:7:44:8 | v1 | |
+| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:45:7:45:8 | v1 | |
+| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | |
+| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | |
+| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | |
+| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | |
+| vector.cpp:41:2:41:3 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | |
+| vector.cpp:41:2:41:3 | v1 | vector.cpp:41:4:41:4 | call to operator[] | TAINT |
+| vector.cpp:41:2:41:10 | ... = ... | vector.cpp:41:4:41:4 | call to operator[] [post update] | |
+| vector.cpp:41:4:41:4 | call to operator[] [post update] | vector.cpp:41:2:41:3 | ref arg v1 | TAINT |
+| vector.cpp:41:10:41:10 | 0 | vector.cpp:41:2:41:10 | ... = ... | |
+| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:43:2:43:3 | v1 | |
+| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:44:7:44:8 | v1 | |
+| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:45:7:45:8 | v1 | |
+| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | |
+| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | |
+| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | |
+| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | |
+| vector.cpp:42:2:42:3 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | |
+| vector.cpp:42:2:42:3 | v1 | vector.cpp:42:4:42:4 | call to operator[] | TAINT |
+| vector.cpp:42:2:42:10 | ... = ... | vector.cpp:42:4:42:4 | call to operator[] [post update] | |
+| vector.cpp:42:4:42:4 | call to operator[] [post update] | vector.cpp:42:2:42:3 | ref arg v1 | TAINT |
+| vector.cpp:42:10:42:10 | 0 | vector.cpp:42:2:42:10 | ... = ... | |
+| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:44:7:44:8 | v1 | |
+| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:45:7:45:8 | v1 | |
+| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | |
+| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | |
+| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | |
+| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | |
+| vector.cpp:43:2:43:3 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | |
+| vector.cpp:43:15:43:15 | 1 | vector.cpp:43:2:43:3 | ref arg v1 | TAINT |
+| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:45:7:45:8 | v1 | |
+| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | |
+| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | |
+| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | |
+| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | |
+| vector.cpp:44:7:44:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | |
+| vector.cpp:45:7:45:8 | ref arg v1 | vector.cpp:46:7:46:8 | v1 | |
+| vector.cpp:45:7:45:8 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | |
+| vector.cpp:45:7:45:8 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | |
+| vector.cpp:45:7:45:8 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | |
+| vector.cpp:45:7:45:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | |
+| vector.cpp:45:7:45:8 | v1 | vector.cpp:45:9:45:9 | call to operator[] | TAINT |
+| vector.cpp:46:7:46:8 | ref arg v1 | vector.cpp:47:7:47:8 | v1 | |
+| vector.cpp:46:7:46:8 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | |
+| vector.cpp:46:7:46:8 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | |
+| vector.cpp:46:7:46:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | |
+| vector.cpp:46:7:46:8 | v1 | vector.cpp:46:9:46:9 | call to operator[] | TAINT |
+| vector.cpp:47:7:47:8 | ref arg v1 | vector.cpp:48:7:48:8 | v1 | |
+| vector.cpp:47:7:47:8 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | |
+| vector.cpp:47:7:47:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | |
+| vector.cpp:47:7:47:8 | v1 | vector.cpp:47:9:47:9 | call to operator[] | TAINT |
+| vector.cpp:48:7:48:8 | ref arg v1 | vector.cpp:49:7:49:8 | v1 | |
+| vector.cpp:48:7:48:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | |
+| vector.cpp:48:7:48:8 | v1 | vector.cpp:48:10:48:14 | call to front | TAINT |
+| vector.cpp:49:7:49:8 | ref arg v1 | vector.cpp:101:1:101:1 | v1 | |
+| vector.cpp:49:7:49:8 | v1 | vector.cpp:49:10:49:13 | call to back | TAINT |
+| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:52:7:52:8 | v2 | |
+| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:53:7:53:8 | v2 | |
+| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:54:7:54:8 | v2 | |
+| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:55:7:55:8 | v2 | |
+| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:57:7:57:8 | v2 | |
+| vector.cpp:51:2:51:3 | ref arg v2 | vector.cpp:101:1:101:1 | v2 | |
+| vector.cpp:51:2:51:3 | v2 | vector.cpp:51:4:51:4 | call to operator[] | TAINT |
+| vector.cpp:51:2:51:17 | ... = ... | vector.cpp:51:4:51:4 | call to operator[] [post update] | |
+| vector.cpp:51:4:51:4 | call to operator[] [post update] | vector.cpp:51:2:51:3 | ref arg v2 | TAINT |
+| vector.cpp:51:10:51:15 | call to source | vector.cpp:51:2:51:17 | ... = ... | |
+| vector.cpp:52:7:52:8 | ref arg v2 | vector.cpp:53:7:53:8 | v2 | |
+| vector.cpp:52:7:52:8 | ref arg v2 | vector.cpp:54:7:54:8 | v2 | |
+| vector.cpp:52:7:52:8 | ref arg v2 | vector.cpp:55:7:55:8 | v2 | |
+| vector.cpp:52:7:52:8 | ref arg v2 | vector.cpp:57:7:57:8 | v2 | |
+| vector.cpp:52:7:52:8 | ref arg v2 | vector.cpp:101:1:101:1 | v2 | |
+| vector.cpp:53:7:53:8 | ref arg v2 | vector.cpp:54:7:54:8 | v2 | |
+| vector.cpp:53:7:53:8 | ref arg v2 | vector.cpp:55:7:55:8 | v2 | |
+| vector.cpp:53:7:53:8 | ref arg v2 | vector.cpp:57:7:57:8 | v2 | |
+| vector.cpp:53:7:53:8 | ref arg v2 | vector.cpp:101:1:101:1 | v2 | |
+| vector.cpp:53:7:53:8 | v2 | vector.cpp:53:9:53:9 | call to operator[] | TAINT |
+| vector.cpp:54:7:54:8 | ref arg v2 | vector.cpp:55:7:55:8 | v2 | |
+| vector.cpp:54:7:54:8 | ref arg v2 | vector.cpp:57:7:57:8 | v2 | |
+| vector.cpp:54:7:54:8 | ref arg v2 | vector.cpp:101:1:101:1 | v2 | |
+| vector.cpp:54:7:54:8 | v2 | vector.cpp:54:9:54:9 | call to operator[] | TAINT |
+| vector.cpp:55:7:55:8 | ref arg v2 | vector.cpp:57:7:57:8 | v2 | |
+| vector.cpp:55:7:55:8 | ref arg v2 | vector.cpp:101:1:101:1 | v2 | |
+| vector.cpp:55:7:55:8 | v2 | vector.cpp:55:9:55:9 | call to operator[] | TAINT |
+| vector.cpp:57:2:57:3 | ref arg v3 | vector.cpp:58:7:58:8 | v3 | |
+| vector.cpp:57:2:57:3 | ref arg v3 | vector.cpp:59:7:59:8 | v3 | |
+| vector.cpp:57:2:57:3 | ref arg v3 | vector.cpp:60:7:60:8 | v3 | |
+| vector.cpp:57:2:57:3 | ref arg v3 | vector.cpp:61:7:61:8 | v3 | |
+| vector.cpp:57:2:57:3 | ref arg v3 | vector.cpp:101:1:101:1 | v3 | |
+| vector.cpp:57:7:57:8 | v2 | vector.cpp:57:2:57:3 | ref arg v3 | TAINT |
+| vector.cpp:57:7:57:8 | v2 | vector.cpp:57:5:57:5 | call to operator= | TAINT |
+| vector.cpp:58:7:58:8 | ref arg v3 | vector.cpp:59:7:59:8 | v3 | |
+| vector.cpp:58:7:58:8 | ref arg v3 | vector.cpp:60:7:60:8 | v3 | |
+| vector.cpp:58:7:58:8 | ref arg v3 | vector.cpp:61:7:61:8 | v3 | |
+| vector.cpp:58:7:58:8 | ref arg v3 | vector.cpp:101:1:101:1 | v3 | |
+| vector.cpp:59:7:59:8 | ref arg v3 | vector.cpp:60:7:60:8 | v3 | |
+| vector.cpp:59:7:59:8 | ref arg v3 | vector.cpp:61:7:61:8 | v3 | |
+| vector.cpp:59:7:59:8 | ref arg v3 | vector.cpp:101:1:101:1 | v3 | |
+| vector.cpp:59:7:59:8 | v3 | vector.cpp:59:9:59:9 | call to operator[] | TAINT |
+| vector.cpp:60:7:60:8 | ref arg v3 | vector.cpp:61:7:61:8 | v3 | |
+| vector.cpp:60:7:60:8 | ref arg v3 | vector.cpp:101:1:101:1 | v3 | |
+| vector.cpp:60:7:60:8 | v3 | vector.cpp:60:9:60:9 | call to operator[] | TAINT |
+| vector.cpp:61:7:61:8 | ref arg v3 | vector.cpp:101:1:101:1 | v3 | |
+| vector.cpp:61:7:61:8 | v3 | vector.cpp:61:9:61:9 | call to operator[] | TAINT |
+| vector.cpp:63:2:63:3 | ref arg v4 | vector.cpp:64:7:64:8 | v4 | |
+| vector.cpp:63:2:63:3 | ref arg v4 | vector.cpp:65:7:65:8 | v4 | |
+| vector.cpp:63:2:63:3 | ref arg v4 | vector.cpp:66:7:66:8 | v4 | |
+| vector.cpp:63:2:63:3 | ref arg v4 | vector.cpp:67:7:67:8 | v4 | |
+| vector.cpp:63:2:63:3 | ref arg v4 | vector.cpp:101:1:101:1 | v4 | |
+| vector.cpp:63:2:63:3 | v4 | vector.cpp:63:4:63:4 | call to operator[] | TAINT |
+| vector.cpp:63:2:63:17 | ... = ... | vector.cpp:63:4:63:4 | call to operator[] [post update] | |
+| vector.cpp:63:4:63:4 | call to operator[] [post update] | vector.cpp:63:2:63:3 | ref arg v4 | TAINT |
+| vector.cpp:63:10:63:15 | call to source | vector.cpp:63:2:63:17 | ... = ... | |
+| vector.cpp:64:7:64:8 | ref arg v4 | vector.cpp:65:7:65:8 | v4 | |
+| vector.cpp:64:7:64:8 | ref arg v4 | vector.cpp:66:7:66:8 | v4 | |
+| vector.cpp:64:7:64:8 | ref arg v4 | vector.cpp:67:7:67:8 | v4 | |
+| vector.cpp:64:7:64:8 | ref arg v4 | vector.cpp:101:1:101:1 | v4 | |
+| vector.cpp:65:7:65:8 | ref arg v4 | vector.cpp:66:7:66:8 | v4 | |
+| vector.cpp:65:7:65:8 | ref arg v4 | vector.cpp:67:7:67:8 | v4 | |
+| vector.cpp:65:7:65:8 | ref arg v4 | vector.cpp:101:1:101:1 | v4 | |
+| vector.cpp:65:7:65:8 | v4 | vector.cpp:65:9:65:9 | call to operator[] | TAINT |
+| vector.cpp:66:7:66:8 | ref arg v4 | vector.cpp:67:7:67:8 | v4 | |
+| vector.cpp:66:7:66:8 | ref arg v4 | vector.cpp:101:1:101:1 | v4 | |
+| vector.cpp:66:7:66:8 | v4 | vector.cpp:66:9:66:9 | call to operator[] | TAINT |
+| vector.cpp:67:7:67:8 | ref arg v4 | vector.cpp:101:1:101:1 | v4 | |
+| vector.cpp:67:7:67:8 | v4 | vector.cpp:67:9:67:9 | call to operator[] | TAINT |
+| vector.cpp:69:2:69:3 | ref arg v5 | vector.cpp:70:7:70:8 | v5 | |
+| vector.cpp:69:2:69:3 | ref arg v5 | vector.cpp:71:7:71:8 | v5 | |
+| vector.cpp:69:2:69:3 | ref arg v5 | vector.cpp:72:7:72:8 | v5 | |
+| vector.cpp:69:2:69:3 | ref arg v5 | vector.cpp:101:1:101:1 | v5 | |
+| vector.cpp:69:15:69:20 | call to source | vector.cpp:69:2:69:3 | ref arg v5 | TAINT |
+| vector.cpp:70:7:70:8 | ref arg v5 | vector.cpp:71:7:71:8 | v5 | |
+| vector.cpp:70:7:70:8 | ref arg v5 | vector.cpp:72:7:72:8 | v5 | |
+| vector.cpp:70:7:70:8 | ref arg v5 | vector.cpp:101:1:101:1 | v5 | |
+| vector.cpp:71:7:71:8 | ref arg v5 | vector.cpp:72:7:72:8 | v5 | |
+| vector.cpp:71:7:71:8 | ref arg v5 | vector.cpp:101:1:101:1 | v5 | |
+| vector.cpp:71:7:71:8 | v5 | vector.cpp:71:10:71:14 | call to front | TAINT |
+| vector.cpp:72:7:72:8 | ref arg v5 | vector.cpp:101:1:101:1 | v5 | |
+| vector.cpp:72:7:72:8 | v5 | vector.cpp:72:10:72:13 | call to back | TAINT |
+| vector.cpp:74:2:74:3 | ref arg v6 | vector.cpp:75:7:75:8 | v6 | |
+| vector.cpp:74:2:74:3 | ref arg v6 | vector.cpp:76:7:76:8 | v6 | |
+| vector.cpp:74:2:74:3 | ref arg v6 | vector.cpp:101:1:101:1 | v6 | |
+| vector.cpp:74:2:74:3 | v6 | vector.cpp:74:5:74:8 | call to data | TAINT |
+| vector.cpp:74:2:74:13 | access to array [post update] | vector.cpp:74:5:74:8 | call to data [inner post update] | |
+| vector.cpp:74:2:74:24 | ... = ... | vector.cpp:74:2:74:13 | access to array [post update] | |
+| vector.cpp:74:5:74:8 | call to data | vector.cpp:74:2:74:13 | access to array | TAINT |
+| vector.cpp:74:5:74:8 | call to data [inner post update] | vector.cpp:74:2:74:3 | ref arg v6 | TAINT |
+| vector.cpp:74:12:74:12 | 2 | vector.cpp:74:2:74:13 | access to array | TAINT |
+| vector.cpp:74:17:74:22 | call to source | vector.cpp:74:2:74:24 | ... = ... | |
+| vector.cpp:75:7:75:8 | ref arg v6 | vector.cpp:76:7:76:8 | v6 | |
+| vector.cpp:75:7:75:8 | ref arg v6 | vector.cpp:101:1:101:1 | v6 | |
+| vector.cpp:76:7:76:8 | ref arg v6 | vector.cpp:101:1:101:1 | v6 | |
+| vector.cpp:76:7:76:8 | v6 | vector.cpp:76:10:76:13 | call to data | TAINT |
+| vector.cpp:76:10:76:13 | call to data | vector.cpp:76:7:76:18 | access to array | TAINT |
+| vector.cpp:76:17:76:17 | 2 | vector.cpp:76:7:76:18 | access to array | TAINT |
+| vector.cpp:80:40:80:50 | call to iterator | vector.cpp:81:13:81:14 | it | |
+| vector.cpp:80:41:80:42 | ref arg v7 | vector.cpp:81:3:81:4 | v7 | |
+| vector.cpp:80:41:80:42 | ref arg v7 | vector.cpp:83:7:83:8 | v7 | |
+| vector.cpp:80:41:80:42 | ref arg v7 | vector.cpp:84:7:84:8 | v7 | |
+| vector.cpp:80:41:80:42 | ref arg v7 | vector.cpp:85:7:85:8 | v7 | |
+| vector.cpp:80:41:80:42 | ref arg v7 | vector.cpp:101:1:101:1 | v7 | |
+| vector.cpp:80:41:80:42 | v7 | vector.cpp:80:44:80:48 | call to begin | TAINT |
+| vector.cpp:80:44:80:48 | call to begin | vector.cpp:80:40:80:50 | call to iterator | TAINT |
+| vector.cpp:81:3:81:4 | ref arg v7 | vector.cpp:83:7:83:8 | v7 | |
+| vector.cpp:81:3:81:4 | ref arg v7 | vector.cpp:84:7:84:8 | v7 | |
+| vector.cpp:81:3:81:4 | ref arg v7 | vector.cpp:85:7:85:8 | v7 | |
+| vector.cpp:81:3:81:4 | ref arg v7 | vector.cpp:101:1:101:1 | v7 | |
+| vector.cpp:81:3:81:4 | v7 | vector.cpp:81:6:81:11 | call to insert | TAINT |
+| vector.cpp:81:17:81:22 | call to source | vector.cpp:81:3:81:4 | ref arg v7 | TAINT |
+| vector.cpp:81:17:81:22 | call to source | vector.cpp:81:6:81:11 | call to insert | TAINT |
+| vector.cpp:83:7:83:8 | ref arg v7 | vector.cpp:84:7:84:8 | v7 | |
+| vector.cpp:83:7:83:8 | ref arg v7 | vector.cpp:85:7:85:8 | v7 | |
+| vector.cpp:83:7:83:8 | ref arg v7 | vector.cpp:101:1:101:1 | v7 | |
+| vector.cpp:84:7:84:8 | ref arg v7 | vector.cpp:85:7:85:8 | v7 | |
+| vector.cpp:84:7:84:8 | ref arg v7 | vector.cpp:101:1:101:1 | v7 | |
+| vector.cpp:84:7:84:8 | v7 | vector.cpp:84:10:84:14 | call to front | TAINT |
+| vector.cpp:85:7:85:8 | ref arg v7 | vector.cpp:101:1:101:1 | v7 | |
+| vector.cpp:85:7:85:8 | v7 | vector.cpp:85:10:85:13 | call to back | TAINT |
+| vector.cpp:88:33:88:34 | v8 | vector.cpp:89:41:89:43 | v8c | |
+| vector.cpp:89:41:89:43 | v8c | vector.cpp:89:45:89:49 | call to begin | TAINT |
+| vector.cpp:89:45:89:49 | call to begin | vector.cpp:90:13:90:14 | it | |
+| vector.cpp:90:3:90:4 | ref arg v8 | vector.cpp:92:7:92:8 | v8 | |
+| vector.cpp:90:3:90:4 | ref arg v8 | vector.cpp:93:7:93:8 | v8 | |
+| vector.cpp:90:3:90:4 | ref arg v8 | vector.cpp:94:7:94:8 | v8 | |
+| vector.cpp:90:3:90:4 | ref arg v8 | vector.cpp:101:1:101:1 | v8 | |
+| vector.cpp:90:3:90:4 | v8 | vector.cpp:90:6:90:11 | call to insert | TAINT |
+| vector.cpp:92:7:92:8 | ref arg v8 | vector.cpp:93:7:93:8 | v8 | |
+| vector.cpp:92:7:92:8 | ref arg v8 | vector.cpp:94:7:94:8 | v8 | |
+| vector.cpp:92:7:92:8 | ref arg v8 | vector.cpp:101:1:101:1 | v8 | |
+| vector.cpp:93:7:93:8 | ref arg v8 | vector.cpp:94:7:94:8 | v8 | |
+| vector.cpp:93:7:93:8 | ref arg v8 | vector.cpp:101:1:101:1 | v8 | |
+| vector.cpp:93:7:93:8 | v8 | vector.cpp:93:10:93:14 | call to front | TAINT |
+| vector.cpp:94:7:94:8 | ref arg v8 | vector.cpp:101:1:101:1 | v8 | |
+| vector.cpp:94:7:94:8 | v8 | vector.cpp:94:10:94:13 | call to back | TAINT |
+| vector.cpp:96:2:96:3 | ref arg v9 | vector.cpp:97:7:97:8 | v9 | |
+| vector.cpp:96:2:96:3 | ref arg v9 | vector.cpp:98:7:98:8 | v9 | |
+| vector.cpp:96:2:96:3 | ref arg v9 | vector.cpp:99:7:99:8 | v9 | |
+| vector.cpp:96:2:96:3 | ref arg v9 | vector.cpp:100:7:100:8 | v9 | |
+| vector.cpp:96:2:96:3 | ref arg v9 | vector.cpp:101:1:101:1 | v9 | |
+| vector.cpp:96:2:96:3 | v9 | vector.cpp:96:5:96:6 | call to at | TAINT |
+| vector.cpp:96:2:96:20 | ... = ... | vector.cpp:96:5:96:6 | call to at [post update] | |
+| vector.cpp:96:5:96:6 | call to at [post update] | vector.cpp:96:2:96:3 | ref arg v9 | TAINT |
+| vector.cpp:96:13:96:18 | call to source | vector.cpp:96:2:96:20 | ... = ... | |
+| vector.cpp:97:7:97:8 | ref arg v9 | vector.cpp:98:7:98:8 | v9 | |
+| vector.cpp:97:7:97:8 | ref arg v9 | vector.cpp:99:7:99:8 | v9 | |
+| vector.cpp:97:7:97:8 | ref arg v9 | vector.cpp:100:7:100:8 | v9 | |
+| vector.cpp:97:7:97:8 | ref arg v9 | vector.cpp:101:1:101:1 | v9 | |
+| vector.cpp:98:7:98:8 | ref arg v9 | vector.cpp:99:7:99:8 | v9 | |
+| vector.cpp:98:7:98:8 | ref arg v9 | vector.cpp:100:7:100:8 | v9 | |
+| vector.cpp:98:7:98:8 | ref arg v9 | vector.cpp:101:1:101:1 | v9 | |
+| vector.cpp:98:7:98:8 | v9 | vector.cpp:98:10:98:11 | call to at | TAINT |
+| vector.cpp:99:7:99:8 | ref arg v9 | vector.cpp:100:7:100:8 | v9 | |
+| vector.cpp:99:7:99:8 | ref arg v9 | vector.cpp:101:1:101:1 | v9 | |
+| vector.cpp:99:7:99:8 | v9 | vector.cpp:99:10:99:11 | call to at | TAINT |
+| vector.cpp:100:7:100:8 | ref arg v9 | vector.cpp:101:1:101:1 | v9 | |
+| vector.cpp:100:7:100:8 | v9 | vector.cpp:100:10:100:11 | call to at | TAINT |
+| vector.cpp:104:22:104:24 | call to vector | vector.cpp:106:2:106:3 | v1 | |
+| vector.cpp:104:22:104:24 | call to vector | vector.cpp:109:7:109:8 | v1 | |
+| vector.cpp:104:22:104:24 | call to vector | vector.cpp:114:2:114:3 | v1 | |
+| vector.cpp:104:22:104:24 | call to vector | vector.cpp:117:7:117:8 | v1 | |
+| vector.cpp:104:22:104:24 | call to vector | vector.cpp:121:1:121:1 | v1 | |
+| vector.cpp:104:30:104:32 | call to vector | vector.cpp:110:7:110:8 | v2 | |
+| vector.cpp:104:30:104:32 | call to vector | vector.cpp:114:10:114:11 | v2 | |
+| vector.cpp:104:30:104:32 | call to vector | vector.cpp:118:7:118:8 | v2 | |
+| vector.cpp:104:30:104:32 | call to vector | vector.cpp:121:1:121:1 | v2 | |
+| vector.cpp:104:38:104:40 | call to vector | vector.cpp:111:7:111:8 | v3 | |
+| vector.cpp:104:38:104:40 | call to vector | vector.cpp:115:2:115:3 | v3 | |
+| vector.cpp:104:38:104:40 | call to vector | vector.cpp:119:7:119:8 | v3 | |
+| vector.cpp:104:38:104:40 | call to vector | vector.cpp:121:1:121:1 | v3 | |
+| vector.cpp:104:46:104:48 | call to vector | vector.cpp:107:2:107:3 | v4 | |
+| vector.cpp:104:46:104:48 | call to vector | vector.cpp:112:7:112:8 | v4 | |
+| vector.cpp:104:46:104:48 | call to vector | vector.cpp:115:10:115:11 | v4 | |
+| vector.cpp:104:46:104:48 | call to vector | vector.cpp:120:7:120:8 | v4 | |
+| vector.cpp:104:46:104:48 | call to vector | vector.cpp:121:1:121:1 | v4 | |
+| vector.cpp:106:2:106:3 | ref arg v1 | vector.cpp:109:7:109:8 | v1 | |
+| vector.cpp:106:2:106:3 | ref arg v1 | vector.cpp:114:2:114:3 | v1 | |
+| vector.cpp:106:2:106:3 | ref arg v1 | vector.cpp:117:7:117:8 | v1 | |
+| vector.cpp:106:2:106:3 | ref arg v1 | vector.cpp:121:1:121:1 | v1 | |
+| vector.cpp:106:15:106:20 | call to source | vector.cpp:106:2:106:3 | ref arg v1 | TAINT |
+| vector.cpp:107:2:107:3 | ref arg v4 | vector.cpp:112:7:112:8 | v4 | |
+| vector.cpp:107:2:107:3 | ref arg v4 | vector.cpp:115:10:115:11 | v4 | |
+| vector.cpp:107:2:107:3 | ref arg v4 | vector.cpp:120:7:120:8 | v4 | |
+| vector.cpp:107:2:107:3 | ref arg v4 | vector.cpp:121:1:121:1 | v4 | |
+| vector.cpp:107:15:107:20 | call to source | vector.cpp:107:2:107:3 | ref arg v4 | TAINT |
+| vector.cpp:109:7:109:8 | ref arg v1 | vector.cpp:114:2:114:3 | v1 | |
+| vector.cpp:109:7:109:8 | ref arg v1 | vector.cpp:117:7:117:8 | v1 | |
+| vector.cpp:109:7:109:8 | ref arg v1 | vector.cpp:121:1:121:1 | v1 | |
+| vector.cpp:110:7:110:8 | ref arg v2 | vector.cpp:114:10:114:11 | v2 | |
+| vector.cpp:110:7:110:8 | ref arg v2 | vector.cpp:118:7:118:8 | v2 | |
+| vector.cpp:110:7:110:8 | ref arg v2 | vector.cpp:121:1:121:1 | v2 | |
+| vector.cpp:111:7:111:8 | ref arg v3 | vector.cpp:115:2:115:3 | v3 | |
+| vector.cpp:111:7:111:8 | ref arg v3 | vector.cpp:119:7:119:8 | v3 | |
+| vector.cpp:111:7:111:8 | ref arg v3 | vector.cpp:121:1:121:1 | v3 | |
+| vector.cpp:112:7:112:8 | ref arg v4 | vector.cpp:115:10:115:11 | v4 | |
+| vector.cpp:112:7:112:8 | ref arg v4 | vector.cpp:120:7:120:8 | v4 | |
+| vector.cpp:112:7:112:8 | ref arg v4 | vector.cpp:121:1:121:1 | v4 | |
+| vector.cpp:114:2:114:3 | ref arg v1 | vector.cpp:117:7:117:8 | v1 | |
+| vector.cpp:114:2:114:3 | ref arg v1 | vector.cpp:121:1:121:1 | v1 | |
+| vector.cpp:114:2:114:3 | v1 | vector.cpp:114:10:114:11 | ref arg v2 | TAINT |
+| vector.cpp:114:10:114:11 | ref arg v2 | vector.cpp:118:7:118:8 | v2 | |
+| vector.cpp:114:10:114:11 | ref arg v2 | vector.cpp:121:1:121:1 | v2 | |
+| vector.cpp:114:10:114:11 | v2 | vector.cpp:114:2:114:3 | ref arg v1 | TAINT |
+| vector.cpp:115:2:115:3 | ref arg v3 | vector.cpp:119:7:119:8 | v3 | |
+| vector.cpp:115:2:115:3 | ref arg v3 | vector.cpp:121:1:121:1 | v3 | |
+| vector.cpp:115:2:115:3 | v3 | vector.cpp:115:10:115:11 | ref arg v4 | TAINT |
+| vector.cpp:115:10:115:11 | ref arg v4 | vector.cpp:120:7:120:8 | v4 | |
+| vector.cpp:115:10:115:11 | ref arg v4 | vector.cpp:121:1:121:1 | v4 | |
+| vector.cpp:115:10:115:11 | v4 | vector.cpp:115:2:115:3 | ref arg v3 | TAINT |
+| vector.cpp:117:7:117:8 | ref arg v1 | vector.cpp:121:1:121:1 | v1 | |
+| vector.cpp:118:7:118:8 | ref arg v2 | vector.cpp:121:1:121:1 | v2 | |
+| vector.cpp:119:7:119:8 | ref arg v3 | vector.cpp:121:1:121:1 | v3 | |
+| vector.cpp:120:7:120:8 | ref arg v4 | vector.cpp:121:1:121:1 | v4 | |
+| vector.cpp:124:22:124:24 | call to vector | vector.cpp:126:2:126:3 | v1 | |
+| vector.cpp:124:22:124:24 | call to vector | vector.cpp:130:7:130:8 | v1 | |
+| vector.cpp:124:22:124:24 | call to vector | vector.cpp:135:2:135:3 | v1 | |
+| vector.cpp:124:22:124:24 | call to vector | vector.cpp:139:7:139:8 | v1 | |
+| vector.cpp:124:22:124:24 | call to vector | vector.cpp:143:1:143:1 | v1 | |
+| vector.cpp:124:30:124:32 | call to vector | vector.cpp:127:2:127:3 | v2 | |
+| vector.cpp:124:30:124:32 | call to vector | vector.cpp:131:7:131:8 | v2 | |
+| vector.cpp:124:30:124:32 | call to vector | vector.cpp:136:2:136:3 | v2 | |
+| vector.cpp:124:30:124:32 | call to vector | vector.cpp:136:7:136:8 | v2 | |
+| vector.cpp:124:30:124:32 | call to vector | vector.cpp:140:7:140:8 | v2 | |
+| vector.cpp:124:30:124:32 | call to vector | vector.cpp:143:1:143:1 | v2 | |
+| vector.cpp:124:38:124:40 | call to vector | vector.cpp:128:2:128:3 | v3 | |
+| vector.cpp:124:38:124:40 | call to vector | vector.cpp:132:7:132:8 | v3 | |
+| vector.cpp:124:38:124:40 | call to vector | vector.cpp:137:2:137:3 | v3 | |
+| vector.cpp:124:38:124:40 | call to vector | vector.cpp:141:7:141:8 | v3 | |
+| vector.cpp:124:38:124:40 | call to vector | vector.cpp:143:1:143:1 | v3 | |
+| vector.cpp:124:46:124:48 | call to vector | vector.cpp:133:7:133:8 | v4 | |
+| vector.cpp:124:46:124:48 | call to vector | vector.cpp:137:7:137:8 | v4 | |
+| vector.cpp:124:46:124:48 | call to vector | vector.cpp:142:7:142:8 | v4 | |
+| vector.cpp:124:46:124:48 | call to vector | vector.cpp:143:1:143:1 | v4 | |
+| vector.cpp:126:2:126:3 | ref arg v1 | vector.cpp:130:7:130:8 | v1 | |
+| vector.cpp:126:2:126:3 | ref arg v1 | vector.cpp:135:2:135:3 | v1 | |
+| vector.cpp:126:2:126:3 | ref arg v1 | vector.cpp:139:7:139:8 | v1 | |
+| vector.cpp:126:2:126:3 | ref arg v1 | vector.cpp:143:1:143:1 | v1 | |
+| vector.cpp:126:15:126:20 | call to source | vector.cpp:126:2:126:3 | ref arg v1 | TAINT |
+| vector.cpp:127:2:127:3 | ref arg v2 | vector.cpp:131:7:131:8 | v2 | |
+| vector.cpp:127:2:127:3 | ref arg v2 | vector.cpp:136:2:136:3 | v2 | |
+| vector.cpp:127:2:127:3 | ref arg v2 | vector.cpp:136:7:136:8 | v2 | |
+| vector.cpp:127:2:127:3 | ref arg v2 | vector.cpp:140:7:140:8 | v2 | |
+| vector.cpp:127:2:127:3 | ref arg v2 | vector.cpp:143:1:143:1 | v2 | |
+| vector.cpp:127:15:127:20 | call to source | vector.cpp:127:2:127:3 | ref arg v2 | TAINT |
+| vector.cpp:128:2:128:3 | ref arg v3 | vector.cpp:132:7:132:8 | v3 | |
+| vector.cpp:128:2:128:3 | ref arg v3 | vector.cpp:137:2:137:3 | v3 | |
+| vector.cpp:128:2:128:3 | ref arg v3 | vector.cpp:141:7:141:8 | v3 | |
+| vector.cpp:128:2:128:3 | ref arg v3 | vector.cpp:143:1:143:1 | v3 | |
+| vector.cpp:128:15:128:20 | call to source | vector.cpp:128:2:128:3 | ref arg v3 | TAINT |
+| vector.cpp:130:7:130:8 | ref arg v1 | vector.cpp:135:2:135:3 | v1 | |
+| vector.cpp:130:7:130:8 | ref arg v1 | vector.cpp:139:7:139:8 | v1 | |
+| vector.cpp:130:7:130:8 | ref arg v1 | vector.cpp:143:1:143:1 | v1 | |
+| vector.cpp:131:7:131:8 | ref arg v2 | vector.cpp:136:2:136:3 | v2 | |
+| vector.cpp:131:7:131:8 | ref arg v2 | vector.cpp:136:7:136:8 | v2 | |
+| vector.cpp:131:7:131:8 | ref arg v2 | vector.cpp:140:7:140:8 | v2 | |
+| vector.cpp:131:7:131:8 | ref arg v2 | vector.cpp:143:1:143:1 | v2 | |
+| vector.cpp:132:7:132:8 | ref arg v3 | vector.cpp:137:2:137:3 | v3 | |
+| vector.cpp:132:7:132:8 | ref arg v3 | vector.cpp:141:7:141:8 | v3 | |
+| vector.cpp:132:7:132:8 | ref arg v3 | vector.cpp:143:1:143:1 | v3 | |
+| vector.cpp:133:7:133:8 | ref arg v4 | vector.cpp:137:7:137:8 | v4 | |
+| vector.cpp:133:7:133:8 | ref arg v4 | vector.cpp:142:7:142:8 | v4 | |
+| vector.cpp:133:7:133:8 | ref arg v4 | vector.cpp:143:1:143:1 | v4 | |
+| vector.cpp:135:2:135:3 | ref arg v1 | vector.cpp:139:7:139:8 | v1 | |
+| vector.cpp:135:2:135:3 | ref arg v1 | vector.cpp:143:1:143:1 | v1 | |
+| vector.cpp:136:2:136:3 | ref arg v2 | vector.cpp:140:7:140:8 | v2 | |
+| vector.cpp:136:2:136:3 | ref arg v2 | vector.cpp:143:1:143:1 | v2 | |
+| vector.cpp:136:7:136:8 | v2 | vector.cpp:136:2:136:3 | ref arg v2 | TAINT |
+| vector.cpp:136:7:136:8 | v2 | vector.cpp:136:5:136:5 | call to operator= | TAINT |
+| vector.cpp:137:2:137:3 | ref arg v3 | vector.cpp:141:7:141:8 | v3 | |
+| vector.cpp:137:2:137:3 | ref arg v3 | vector.cpp:143:1:143:1 | v3 | |
+| vector.cpp:137:7:137:8 | v4 | vector.cpp:137:2:137:3 | ref arg v3 | TAINT |
+| vector.cpp:137:7:137:8 | v4 | vector.cpp:137:5:137:5 | call to operator= | TAINT |
+| vector.cpp:139:7:139:8 | ref arg v1 | vector.cpp:143:1:143:1 | v1 | |
+| vector.cpp:140:7:140:8 | ref arg v2 | vector.cpp:143:1:143:1 | v2 | |
+| vector.cpp:141:7:141:8 | ref arg v3 | vector.cpp:143:1:143:1 | v3 | |
+| vector.cpp:142:7:142:8 | ref arg v4 | vector.cpp:143:1:143:1 | v4 | |
+| vector.cpp:150:8:150:8 | call to vector | vector.cpp:150:8:150:8 | constructor init of field vs | TAINT |
+| vector.cpp:150:8:150:8 | call to ~vector | vector.cpp:150:8:150:8 | destructor field destruction of vs | TAINT |
+| vector.cpp:150:8:150:8 | this | vector.cpp:150:8:150:8 | constructor init of field vs [pre-this] | |
+| vector.cpp:158:19:158:22 | {...} | vector.cpp:160:8:160:9 | aa | |
+| vector.cpp:158:19:158:22 | {...} | vector.cpp:161:3:161:4 | aa | |
+| vector.cpp:158:19:158:22 | {...} | vector.cpp:162:8:162:9 | aa | |
+| vector.cpp:158:21:158:21 | 0 | vector.cpp:158:21:158:21 | {...} | TAINT |
+| vector.cpp:158:21:158:21 | {...} | vector.cpp:158:19:158:22 | {...} | TAINT |
+| vector.cpp:160:8:160:9 | aa | vector.cpp:160:8:160:12 | access to array | TAINT |
+| vector.cpp:160:8:160:9 | aa | vector.cpp:160:8:160:15 | access to array | |
+| vector.cpp:160:8:160:12 | access to array | vector.cpp:160:8:160:15 | access to array | TAINT |
+| vector.cpp:160:11:160:11 | 0 | vector.cpp:160:8:160:12 | access to array | TAINT |
+| vector.cpp:160:14:160:14 | 0 | vector.cpp:160:8:160:15 | access to array | TAINT |
+| vector.cpp:161:3:161:4 | aa | vector.cpp:161:3:161:7 | access to array | TAINT |
+| vector.cpp:161:3:161:4 | aa | vector.cpp:161:3:161:10 | access to array | |
+| vector.cpp:161:3:161:7 | access to array | vector.cpp:161:3:161:10 | access to array | TAINT |
+| vector.cpp:161:3:161:10 | access to array [post update] | vector.cpp:161:3:161:4 | aa [inner post update] | |
+| vector.cpp:161:3:161:10 | access to array [post update] | vector.cpp:162:8:162:9 | aa | |
+| vector.cpp:161:3:161:21 | ... = ... | vector.cpp:161:3:161:10 | access to array [post update] | |
+| vector.cpp:161:6:161:6 | 0 | vector.cpp:161:3:161:7 | access to array | TAINT |
+| vector.cpp:161:9:161:9 | 0 | vector.cpp:161:3:161:10 | access to array | TAINT |
+| vector.cpp:161:14:161:19 | call to source | vector.cpp:161:3:161:21 | ... = ... | |
+| vector.cpp:162:8:162:9 | aa | vector.cpp:162:8:162:12 | access to array | TAINT |
+| vector.cpp:162:8:162:9 | aa | vector.cpp:162:8:162:15 | access to array | |
+| vector.cpp:162:8:162:12 | access to array | vector.cpp:162:8:162:15 | access to array | TAINT |
+| vector.cpp:162:11:162:11 | 0 | vector.cpp:162:8:162:12 | access to array | TAINT |
+| vector.cpp:162:14:162:14 | 0 | vector.cpp:162:8:162:15 | access to array | TAINT |
+| vector.cpp:166:37:166:39 | call to vector | vector.cpp:168:3:168:4 | bb | |
+| vector.cpp:166:37:166:39 | call to vector | vector.cpp:169:8:169:9 | bb | |
+| vector.cpp:166:37:166:39 | call to vector | vector.cpp:170:3:170:4 | bb | |
+| vector.cpp:166:37:166:39 | call to vector | vector.cpp:171:8:171:9 | bb | |
+| vector.cpp:166:37:166:39 | call to vector | vector.cpp:172:2:172:2 | bb | |
+| vector.cpp:168:3:168:4 | bb | vector.cpp:168:5:168:5 | call to operator[] | TAINT |
+| vector.cpp:168:3:168:4 | ref arg bb | vector.cpp:169:8:169:9 | bb | |
+| vector.cpp:168:3:168:4 | ref arg bb | vector.cpp:170:3:170:4 | bb | |
+| vector.cpp:168:3:168:4 | ref arg bb | vector.cpp:171:8:171:9 | bb | |
+| vector.cpp:168:3:168:4 | ref arg bb | vector.cpp:172:2:172:2 | bb | |
+| vector.cpp:168:5:168:5 | ref arg call to operator[] | vector.cpp:168:3:168:4 | ref arg bb | TAINT |
+| vector.cpp:168:19:168:19 | 0 | vector.cpp:168:5:168:5 | ref arg call to operator[] | TAINT |
+| vector.cpp:169:8:169:9 | bb | vector.cpp:169:10:169:10 | call to operator[] | TAINT |
+| vector.cpp:169:8:169:9 | ref arg bb | vector.cpp:170:3:170:4 | bb | |
+| vector.cpp:169:8:169:9 | ref arg bb | vector.cpp:171:8:171:9 | bb | |
+| vector.cpp:169:8:169:9 | ref arg bb | vector.cpp:172:2:172:2 | bb | |
+| vector.cpp:169:10:169:10 | call to operator[] | vector.cpp:169:13:169:13 | call to operator[] | TAINT |
+| vector.cpp:169:10:169:10 | ref arg call to operator[] | vector.cpp:169:8:169:9 | ref arg bb | TAINT |
+| vector.cpp:170:3:170:4 | bb | vector.cpp:170:5:170:5 | call to operator[] | TAINT |
+| vector.cpp:170:3:170:4 | ref arg bb | vector.cpp:171:8:171:9 | bb | |
+| vector.cpp:170:3:170:4 | ref arg bb | vector.cpp:172:2:172:2 | bb | |
+| vector.cpp:170:3:170:21 | ... = ... | vector.cpp:170:8:170:8 | call to operator[] [post update] | |
+| vector.cpp:170:5:170:5 | call to operator[] | vector.cpp:170:8:170:8 | call to operator[] | TAINT |
+| vector.cpp:170:5:170:5 | ref arg call to operator[] | vector.cpp:170:3:170:4 | ref arg bb | TAINT |
+| vector.cpp:170:8:170:8 | call to operator[] [post update] | vector.cpp:170:5:170:5 | ref arg call to operator[] | TAINT |
+| vector.cpp:170:14:170:19 | call to source | vector.cpp:170:3:170:21 | ... = ... | |
+| vector.cpp:171:8:171:9 | bb | vector.cpp:171:10:171:10 | call to operator[] | TAINT |
+| vector.cpp:171:8:171:9 | ref arg bb | vector.cpp:172:2:172:2 | bb | |
+| vector.cpp:171:10:171:10 | call to operator[] | vector.cpp:171:13:171:13 | call to operator[] | TAINT |
+| vector.cpp:171:10:171:10 | ref arg call to operator[] | vector.cpp:171:8:171:9 | ref arg bb | TAINT |
+| vector.cpp:175:20:175:21 | call to vector | vector.cpp:175:20:175:21 | {...} | TAINT |
+| vector.cpp:175:20:175:21 | {...} | vector.cpp:177:3:177:4 | cc | |
+| vector.cpp:175:20:175:21 | {...} | vector.cpp:178:8:178:9 | cc | |
+| vector.cpp:175:20:175:21 | {...} | vector.cpp:179:3:179:4 | cc | |
+| vector.cpp:175:20:175:21 | {...} | vector.cpp:180:8:180:9 | cc | |
+| vector.cpp:175:20:175:21 | {...} | vector.cpp:181:2:181:2 | cc | |
+| vector.cpp:177:3:177:4 | cc | vector.cpp:177:3:177:7 | access to array | |
+| vector.cpp:177:3:177:7 | ref arg access to array | vector.cpp:177:3:177:4 | cc [inner post update] | |
+| vector.cpp:177:3:177:7 | ref arg access to array | vector.cpp:178:8:178:9 | cc | |
+| vector.cpp:177:3:177:7 | ref arg access to array | vector.cpp:179:3:179:4 | cc | |
+| vector.cpp:177:3:177:7 | ref arg access to array | vector.cpp:180:8:180:9 | cc | |
+| vector.cpp:177:3:177:7 | ref arg access to array | vector.cpp:181:2:181:2 | cc | |
+| vector.cpp:177:6:177:6 | 0 | vector.cpp:177:3:177:7 | access to array | TAINT |
+| vector.cpp:177:19:177:19 | 0 | vector.cpp:177:3:177:7 | ref arg access to array | TAINT |
+| vector.cpp:178:8:178:9 | cc | vector.cpp:178:8:178:12 | access to array | |
+| vector.cpp:178:8:178:12 | access to array | vector.cpp:178:13:178:13 | call to operator[] | TAINT |
+| vector.cpp:178:8:178:12 | ref arg access to array | vector.cpp:178:8:178:9 | cc [inner post update] | |
+| vector.cpp:178:8:178:12 | ref arg access to array | vector.cpp:179:3:179:4 | cc | |
+| vector.cpp:178:8:178:12 | ref arg access to array | vector.cpp:180:8:180:9 | cc | |
+| vector.cpp:178:8:178:12 | ref arg access to array | vector.cpp:181:2:181:2 | cc | |
+| vector.cpp:178:11:178:11 | 0 | vector.cpp:178:8:178:12 | access to array | TAINT |
+| vector.cpp:179:3:179:4 | cc | vector.cpp:179:3:179:7 | access to array | |
+| vector.cpp:179:3:179:7 | access to array | vector.cpp:179:8:179:8 | call to operator[] | TAINT |
+| vector.cpp:179:3:179:7 | ref arg access to array | vector.cpp:179:3:179:4 | cc [inner post update] | |
+| vector.cpp:179:3:179:7 | ref arg access to array | vector.cpp:180:8:180:9 | cc | |
+| vector.cpp:179:3:179:7 | ref arg access to array | vector.cpp:181:2:181:2 | cc | |
+| vector.cpp:179:3:179:21 | ... = ... | vector.cpp:179:8:179:8 | call to operator[] [post update] | |
+| vector.cpp:179:6:179:6 | 0 | vector.cpp:179:3:179:7 | access to array | TAINT |
+| vector.cpp:179:8:179:8 | call to operator[] [post update] | vector.cpp:179:3:179:7 | ref arg access to array | TAINT |
+| vector.cpp:179:14:179:19 | call to source | vector.cpp:179:3:179:21 | ... = ... | |
+| vector.cpp:180:8:180:9 | cc | vector.cpp:180:8:180:12 | access to array | |
+| vector.cpp:180:8:180:12 | access to array | vector.cpp:180:13:180:13 | call to operator[] | TAINT |
+| vector.cpp:180:8:180:12 | ref arg access to array | vector.cpp:180:8:180:9 | cc [inner post update] | |
+| vector.cpp:180:8:180:12 | ref arg access to array | vector.cpp:181:2:181:2 | cc | |
+| vector.cpp:180:11:180:11 | 0 | vector.cpp:180:8:180:12 | access to array | TAINT |
+| vector.cpp:184:23:184:24 | call to vector | vector.cpp:187:3:187:4 | dd | |
+| vector.cpp:184:23:184:24 | call to vector | vector.cpp:188:8:188:9 | dd | |
+| vector.cpp:184:23:184:24 | call to vector | vector.cpp:189:8:189:9 | dd | |
+| vector.cpp:184:23:184:24 | call to vector | vector.cpp:190:3:190:4 | dd | |
+| vector.cpp:184:23:184:24 | call to vector | vector.cpp:191:8:191:9 | dd | |
+| vector.cpp:184:23:184:24 | call to vector | vector.cpp:192:8:192:9 | dd | |
+| vector.cpp:184:23:184:24 | call to vector | vector.cpp:193:2:193:2 | dd | |
+| vector.cpp:185:14:185:20 | {...} | vector.cpp:187:16:187:17 | mp | |
+| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:188:8:188:9 | dd | |
+| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:189:8:189:9 | dd | |
+| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:190:3:190:4 | dd | |
+| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:191:8:191:9 | dd | |
+| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:192:8:192:9 | dd | |
+| vector.cpp:187:3:187:4 | ref arg dd | vector.cpp:193:2:193:2 | dd | |
+| vector.cpp:187:16:187:17 | mp | vector.cpp:187:3:187:4 | ref arg dd | TAINT |
+| vector.cpp:188:8:188:9 | dd | vector.cpp:188:10:188:10 | call to operator[] | TAINT |
+| vector.cpp:188:8:188:9 | ref arg dd | vector.cpp:189:8:189:9 | dd | |
+| vector.cpp:188:8:188:9 | ref arg dd | vector.cpp:190:3:190:4 | dd | |
+| vector.cpp:188:8:188:9 | ref arg dd | vector.cpp:191:8:191:9 | dd | |
+| vector.cpp:188:8:188:9 | ref arg dd | vector.cpp:192:8:192:9 | dd | |
+| vector.cpp:188:8:188:9 | ref arg dd | vector.cpp:193:2:193:2 | dd | |
+| vector.cpp:189:8:189:9 | dd | vector.cpp:189:10:189:10 | call to operator[] | TAINT |
+| vector.cpp:189:8:189:9 | ref arg dd | vector.cpp:190:3:190:4 | dd | |
+| vector.cpp:189:8:189:9 | ref arg dd | vector.cpp:191:8:191:9 | dd | |
+| vector.cpp:189:8:189:9 | ref arg dd | vector.cpp:192:8:192:9 | dd | |
+| vector.cpp:189:8:189:9 | ref arg dd | vector.cpp:193:2:193:2 | dd | |
+| vector.cpp:190:3:190:4 | dd | vector.cpp:190:5:190:5 | call to operator[] | TAINT |
+| vector.cpp:190:3:190:4 | ref arg dd | vector.cpp:191:8:191:9 | dd | |
+| vector.cpp:190:3:190:4 | ref arg dd | vector.cpp:192:8:192:9 | dd | |
+| vector.cpp:190:3:190:4 | ref arg dd | vector.cpp:193:2:193:2 | dd | |
+| vector.cpp:190:3:190:20 | ... = ... | vector.cpp:190:9:190:9 | a [post update] | |
+| vector.cpp:190:5:190:5 | call to operator[] [post update] | vector.cpp:190:3:190:4 | ref arg dd | TAINT |
+| vector.cpp:190:13:190:18 | call to source | vector.cpp:190:3:190:20 | ... = ... | |
+| vector.cpp:191:8:191:9 | dd | vector.cpp:191:10:191:10 | call to operator[] | TAINT |
+| vector.cpp:191:8:191:9 | ref arg dd | vector.cpp:192:8:192:9 | dd | |
+| vector.cpp:191:8:191:9 | ref arg dd | vector.cpp:193:2:193:2 | dd | |
+| vector.cpp:192:8:192:9 | dd | vector.cpp:192:10:192:10 | call to operator[] | TAINT |
+| vector.cpp:192:8:192:9 | ref arg dd | vector.cpp:193:2:193:2 | dd | |
+| vector.cpp:196:21:196:22 | call to MyVectorContainer | vector.cpp:198:3:198:4 | ee | |
+| vector.cpp:196:21:196:22 | call to MyVectorContainer | vector.cpp:199:8:199:9 | ee | |
+| vector.cpp:196:21:196:22 | call to MyVectorContainer | vector.cpp:200:3:200:4 | ee | |
+| vector.cpp:196:21:196:22 | call to MyVectorContainer | vector.cpp:201:8:201:9 | ee | |
+| vector.cpp:196:21:196:22 | call to MyVectorContainer | vector.cpp:202:2:202:2 | ee | |
+| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:199:8:199:9 | ee | |
+| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:200:3:200:4 | ee | |
+| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:201:8:201:9 | ee | |
+| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:202:2:202:2 | ee | |
+| vector.cpp:198:19:198:19 | 0 | vector.cpp:198:6:198:7 | ref arg vs | TAINT |
+| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:200:3:200:4 | ee | |
+| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:201:8:201:9 | ee | |
+| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:202:2:202:2 | ee | |
+| vector.cpp:199:11:199:12 | vs | vector.cpp:199:13:199:13 | call to operator[] | TAINT |
+| vector.cpp:200:3:200:4 | ee [post update] | vector.cpp:201:8:201:9 | ee | |
+| vector.cpp:200:3:200:4 | ee [post update] | vector.cpp:202:2:202:2 | ee | |
+| vector.cpp:200:3:200:21 | ... = ... | vector.cpp:200:8:200:8 | call to operator[] [post update] | |
+| vector.cpp:200:6:200:7 | vs | vector.cpp:200:8:200:8 | call to operator[] | TAINT |
+| vector.cpp:200:8:200:8 | call to operator[] [post update] | vector.cpp:200:6:200:7 | ref arg vs | TAINT |
+| vector.cpp:200:14:200:19 | call to source | vector.cpp:200:3:200:21 | ... = ... | |
+| vector.cpp:201:8:201:9 | ee [post update] | vector.cpp:202:2:202:2 | ee | |
+| vector.cpp:201:11:201:12 | vs | vector.cpp:201:13:201:13 | call to operator[] | TAINT |
+| vector.cpp:205:34:205:35 | call to vector | vector.cpp:209:3:209:4 | ff | |
+| vector.cpp:205:34:205:35 | call to vector | vector.cpp:210:8:210:9 | ff | |
+| vector.cpp:205:34:205:35 | call to vector | vector.cpp:211:3:211:4 | ff | |
+| vector.cpp:205:34:205:35 | call to vector | vector.cpp:212:8:212:9 | ff | |
+| vector.cpp:205:34:205:35 | call to vector | vector.cpp:213:2:213:2 | ff | |
+| vector.cpp:206:21:206:23 | call to MyVectorContainer | vector.cpp:208:3:208:5 | mvc | |
+| vector.cpp:206:21:206:23 | call to MyVectorContainer | vector.cpp:209:16:209:18 | mvc | |
+| vector.cpp:206:21:206:23 | call to MyVectorContainer | vector.cpp:213:2:213:2 | mvc | |
+| vector.cpp:208:3:208:5 | mvc [post update] | vector.cpp:209:16:209:18 | mvc | |
+| vector.cpp:208:3:208:5 | mvc [post update] | vector.cpp:213:2:213:2 | mvc | |
+| vector.cpp:208:20:208:20 | 0 | vector.cpp:208:7:208:8 | ref arg vs | TAINT |
+| vector.cpp:209:3:209:4 | ref arg ff | vector.cpp:210:8:210:9 | ff | |
+| vector.cpp:209:3:209:4 | ref arg ff | vector.cpp:211:3:211:4 | ff | |
+| vector.cpp:209:3:209:4 | ref arg ff | vector.cpp:212:8:212:9 | ff | |
+| vector.cpp:209:3:209:4 | ref arg ff | vector.cpp:213:2:213:2 | ff | |
+| vector.cpp:209:16:209:18 | mvc | vector.cpp:209:3:209:4 | ref arg ff | TAINT |
+| vector.cpp:210:8:210:9 | ff | vector.cpp:210:10:210:10 | call to operator[] | TAINT |
+| vector.cpp:210:8:210:9 | ref arg ff | vector.cpp:211:3:211:4 | ff | |
+| vector.cpp:210:8:210:9 | ref arg ff | vector.cpp:212:8:212:9 | ff | |
+| vector.cpp:210:8:210:9 | ref arg ff | vector.cpp:213:2:213:2 | ff | |
+| vector.cpp:210:10:210:10 | call to operator[] [post update] | vector.cpp:210:8:210:9 | ref arg ff | TAINT |
+| vector.cpp:210:14:210:15 | vs | vector.cpp:210:16:210:16 | call to operator[] | TAINT |
+| vector.cpp:211:3:211:4 | ff | vector.cpp:211:5:211:5 | call to operator[] | TAINT |
+| vector.cpp:211:3:211:4 | ref arg ff | vector.cpp:212:8:212:9 | ff | |
+| vector.cpp:211:3:211:4 | ref arg ff | vector.cpp:213:2:213:2 | ff | |
+| vector.cpp:211:3:211:24 | ... = ... | vector.cpp:211:11:211:11 | call to operator[] [post update] | |
+| vector.cpp:211:5:211:5 | call to operator[] [post update] | vector.cpp:211:3:211:4 | ref arg ff | TAINT |
+| vector.cpp:211:9:211:10 | vs | vector.cpp:211:11:211:11 | call to operator[] | TAINT |
+| vector.cpp:211:11:211:11 | call to operator[] [post update] | vector.cpp:211:9:211:10 | ref arg vs | TAINT |
+| vector.cpp:211:17:211:22 | call to source | vector.cpp:211:3:211:24 | ... = ... | |
+| vector.cpp:212:8:212:9 | ff | vector.cpp:212:10:212:10 | call to operator[] | TAINT |
+| vector.cpp:212:8:212:9 | ref arg ff | vector.cpp:213:2:213:2 | ff | |
+| vector.cpp:212:10:212:10 | call to operator[] [post update] | vector.cpp:212:8:212:9 | ref arg ff | TAINT |
+| vector.cpp:212:14:212:15 | vs | vector.cpp:212:16:212:16 | call to operator[] | TAINT |
+| vector.cpp:235:19:235:20 | call to vector | vector.cpp:237:2:237:3 | v1 | |
+| vector.cpp:235:19:235:20 | call to vector | vector.cpp:241:7:241:8 | v1 | |
+| vector.cpp:235:19:235:20 | call to vector | vector.cpp:249:13:249:14 | v1 | |
+| vector.cpp:235:19:235:20 | call to vector | vector.cpp:249:25:249:26 | v1 | |
+| vector.cpp:235:19:235:20 | call to vector | vector.cpp:277:1:277:1 | v1 | |
+| vector.cpp:235:23:235:24 | call to vector | vector.cpp:238:2:238:3 | v2 | |
+| vector.cpp:235:23:235:24 | call to vector | vector.cpp:242:7:242:8 | v2 | |
+| vector.cpp:235:23:235:24 | call to vector | vector.cpp:277:1:277:1 | v2 | |
+| vector.cpp:235:27:235:28 | call to vector | vector.cpp:239:2:239:3 | v3 | |
+| vector.cpp:235:27:235:28 | call to vector | vector.cpp:243:7:243:8 | v3 | |
+| vector.cpp:235:27:235:28 | call to vector | vector.cpp:250:13:250:14 | v3 | |
+| vector.cpp:235:27:235:28 | call to vector | vector.cpp:250:25:250:26 | v3 | |
+| vector.cpp:235:27:235:28 | call to vector | vector.cpp:251:8:251:9 | v3 | |
+| vector.cpp:235:27:235:28 | call to vector | vector.cpp:277:1:277:1 | v3 | |
+| vector.cpp:237:2:237:3 | ref arg v1 | vector.cpp:241:7:241:8 | v1 | |
+| vector.cpp:237:2:237:3 | ref arg v1 | vector.cpp:249:13:249:14 | v1 | |
+| vector.cpp:237:2:237:3 | ref arg v1 | vector.cpp:249:25:249:26 | v1 | |
+| vector.cpp:237:2:237:3 | ref arg v1 | vector.cpp:277:1:277:1 | v1 | |
+| vector.cpp:237:17:237:17 | 0 | vector.cpp:237:2:237:3 | ref arg v1 | TAINT |
+| vector.cpp:238:2:238:3 | ref arg v2 | vector.cpp:242:7:242:8 | v2 | |
+| vector.cpp:238:2:238:3 | ref arg v2 | vector.cpp:277:1:277:1 | v2 | |
+| vector.cpp:238:17:238:30 | call to source | vector.cpp:238:2:238:3 | ref arg v2 | TAINT |
+| vector.cpp:239:2:239:3 | ref arg v3 | vector.cpp:243:7:243:8 | v3 | |
+| vector.cpp:239:2:239:3 | ref arg v3 | vector.cpp:250:13:250:14 | v3 | |
+| vector.cpp:239:2:239:3 | ref arg v3 | vector.cpp:250:25:250:26 | v3 | |
+| vector.cpp:239:2:239:3 | ref arg v3 | vector.cpp:251:8:251:9 | v3 | |
+| vector.cpp:239:2:239:3 | ref arg v3 | vector.cpp:277:1:277:1 | v3 | |
+| vector.cpp:239:15:239:20 | call to source | vector.cpp:239:2:239:3 | ref arg v3 | TAINT |
+| vector.cpp:241:7:241:8 | ref arg v1 | vector.cpp:249:13:249:14 | v1 | |
+| vector.cpp:241:7:241:8 | ref arg v1 | vector.cpp:249:25:249:26 | v1 | |
+| vector.cpp:241:7:241:8 | ref arg v1 | vector.cpp:277:1:277:1 | v1 | |
+| vector.cpp:242:7:242:8 | ref arg v2 | vector.cpp:277:1:277:1 | v2 | |
+| vector.cpp:243:7:243:8 | ref arg v3 | vector.cpp:250:13:250:14 | v3 | |
+| vector.cpp:243:7:243:8 | ref arg v3 | vector.cpp:250:25:250:26 | v3 | |
+| vector.cpp:243:7:243:8 | ref arg v3 | vector.cpp:251:8:251:9 | v3 | |
+| vector.cpp:243:7:243:8 | ref arg v3 | vector.cpp:277:1:277:1 | v3 | |
+| vector.cpp:246:20:246:21 | call to vector | vector.cpp:249:3:249:4 | v4 | |
+| vector.cpp:246:20:246:21 | call to vector | vector.cpp:257:8:257:9 | v4 | |
+| vector.cpp:246:20:246:21 | call to vector | vector.cpp:262:2:262:2 | v4 | |
+| vector.cpp:246:24:246:25 | call to vector | vector.cpp:250:3:250:4 | v5 | |
+| vector.cpp:246:24:246:25 | call to vector | vector.cpp:258:8:258:9 | v5 | |
+| vector.cpp:246:24:246:25 | call to vector | vector.cpp:262:2:262:2 | v5 | |
+| vector.cpp:246:28:246:29 | call to vector | vector.cpp:255:3:255:4 | v6 | |
+| vector.cpp:246:28:246:29 | call to vector | vector.cpp:261:8:261:9 | v6 | |
+| vector.cpp:246:28:246:29 | call to vector | vector.cpp:262:2:262:2 | v6 | |
+| vector.cpp:249:3:249:4 | ref arg v4 | vector.cpp:257:8:257:9 | v4 | |
+| vector.cpp:249:3:249:4 | ref arg v4 | vector.cpp:262:2:262:2 | v4 | |
+| vector.cpp:249:13:249:14 | ref arg v1 | vector.cpp:249:25:249:26 | v1 | |
+| vector.cpp:249:13:249:14 | ref arg v1 | vector.cpp:277:1:277:1 | v1 | |
+| vector.cpp:249:13:249:14 | v1 | vector.cpp:249:16:249:20 | call to begin | TAINT |
+| vector.cpp:249:16:249:20 | call to begin | vector.cpp:249:3:249:4 | ref arg v4 | TAINT |
+| vector.cpp:249:25:249:26 | ref arg v1 | vector.cpp:277:1:277:1 | v1 | |
+| vector.cpp:249:25:249:26 | v1 | vector.cpp:249:28:249:30 | call to end | TAINT |
+| vector.cpp:249:28:249:30 | call to end | vector.cpp:249:3:249:4 | ref arg v4 | TAINT |
+| vector.cpp:250:3:250:4 | ref arg v5 | vector.cpp:258:8:258:9 | v5 | |
+| vector.cpp:250:3:250:4 | ref arg v5 | vector.cpp:262:2:262:2 | v5 | |
+| vector.cpp:250:13:250:14 | ref arg v3 | vector.cpp:250:25:250:26 | v3 | |
+| vector.cpp:250:13:250:14 | ref arg v3 | vector.cpp:251:8:251:9 | v3 | |
+| vector.cpp:250:13:250:14 | ref arg v3 | vector.cpp:277:1:277:1 | v3 | |
+| vector.cpp:250:13:250:14 | v3 | vector.cpp:250:16:250:20 | call to begin | TAINT |
+| vector.cpp:250:16:250:20 | call to begin | vector.cpp:250:3:250:4 | ref arg v5 | TAINT |
+| vector.cpp:250:25:250:26 | ref arg v3 | vector.cpp:251:8:251:9 | v3 | |
+| vector.cpp:250:25:250:26 | ref arg v3 | vector.cpp:277:1:277:1 | v3 | |
+| vector.cpp:250:25:250:26 | v3 | vector.cpp:250:28:250:30 | call to end | TAINT |
+| vector.cpp:250:28:250:30 | call to end | vector.cpp:250:3:250:4 | ref arg v5 | TAINT |
+| vector.cpp:251:8:251:9 | ref arg v3 | vector.cpp:277:1:277:1 | v3 | |
+| vector.cpp:251:8:251:9 | v3 | vector.cpp:251:11:251:15 | call to begin | TAINT |
+| vector.cpp:251:11:251:15 | call to begin | vector.cpp:251:3:251:17 | ... = ... | |
+| vector.cpp:251:11:251:15 | call to begin | vector.cpp:252:3:252:4 | i1 | |
+| vector.cpp:251:11:251:15 | call to begin | vector.cpp:253:8:253:9 | i1 | |
+| vector.cpp:251:11:251:15 | call to begin | vector.cpp:255:13:255:14 | i1 | |
+| vector.cpp:251:11:251:15 | call to begin | vector.cpp:259:8:259:9 | i1 | |
+| vector.cpp:252:3:252:4 | i1 | vector.cpp:252:5:252:5 | call to operator++ | |
+| vector.cpp:252:3:252:4 | ref arg i1 | vector.cpp:253:8:253:9 | i1 | |
+| vector.cpp:252:3:252:4 | ref arg i1 | vector.cpp:255:13:255:14 | i1 | |
+| vector.cpp:252:3:252:4 | ref arg i1 | vector.cpp:259:8:259:9 | i1 | |
+| vector.cpp:253:8:253:9 | i1 | vector.cpp:253:3:253:9 | ... = ... | |
+| vector.cpp:253:8:253:9 | i1 | vector.cpp:254:3:254:4 | i2 | |
+| vector.cpp:253:8:253:9 | i1 | vector.cpp:255:17:255:18 | i2 | |
+| vector.cpp:253:8:253:9 | i1 | vector.cpp:260:8:260:9 | i2 | |
+| vector.cpp:254:3:254:4 | i2 | vector.cpp:254:5:254:5 | call to operator++ | |
+| vector.cpp:254:3:254:4 | ref arg i2 | vector.cpp:255:17:255:18 | i2 | |
+| vector.cpp:254:3:254:4 | ref arg i2 | vector.cpp:260:8:260:9 | i2 | |
+| vector.cpp:255:3:255:4 | ref arg v6 | vector.cpp:261:8:261:9 | v6 | |
+| vector.cpp:255:3:255:4 | ref arg v6 | vector.cpp:262:2:262:2 | v6 | |
+| vector.cpp:255:13:255:14 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT |
+| vector.cpp:255:13:255:14 | call to iterator [post update] | vector.cpp:277:1:277:1 | v3 | |
+| vector.cpp:255:13:255:14 | i1 | vector.cpp:255:13:255:14 | call to iterator | |
+| vector.cpp:255:13:255:14 | i1 [post update] | vector.cpp:277:1:277:1 | v3 | |
+| vector.cpp:255:17:255:18 | call to iterator | vector.cpp:255:3:255:4 | ref arg v6 | TAINT |
+| vector.cpp:255:17:255:18 | i2 | vector.cpp:255:17:255:18 | call to iterator | |
+| vector.cpp:257:8:257:9 | ref arg v4 | vector.cpp:262:2:262:2 | v4 | |
+| vector.cpp:258:8:258:9 | ref arg v5 | vector.cpp:262:2:262:2 | v5 | |
+| vector.cpp:259:8:259:9 | ref arg i1 | vector.cpp:277:1:277:1 | v3 | |
+| vector.cpp:261:8:261:9 | ref arg v6 | vector.cpp:262:2:262:2 | v6 | |
+| vector.cpp:265:22:265:23 | call to vector | vector.cpp:269:3:269:4 | v7 | |
+| vector.cpp:265:22:265:23 | call to vector | vector.cpp:273:8:273:9 | v7 | |
+| vector.cpp:265:22:265:23 | call to vector | vector.cpp:276:2:276:2 | v7 | |
+| vector.cpp:266:24:266:25 | call to vector | vector.cpp:270:3:270:4 | v8 | |
+| vector.cpp:266:24:266:25 | call to vector | vector.cpp:274:8:274:9 | v8 | |
+| vector.cpp:266:24:266:25 | call to vector | vector.cpp:276:2:276:2 | v8 | |
+| vector.cpp:267:28:267:29 | call to vector | vector.cpp:271:3:271:4 | v9 | |
+| vector.cpp:267:28:267:29 | call to vector | vector.cpp:275:8:275:9 | v9 | |
+| vector.cpp:267:28:267:29 | call to vector | vector.cpp:276:2:276:2 | v9 | |
+| vector.cpp:269:3:269:4 | ref arg v7 | vector.cpp:273:8:273:9 | v7 | |
+| vector.cpp:269:3:269:4 | ref arg v7 | vector.cpp:276:2:276:2 | v7 | |
+| vector.cpp:269:18:269:31 | call to source | vector.cpp:269:3:269:4 | ref arg v7 | TAINT |
+| vector.cpp:270:3:270:4 | ref arg v8 | vector.cpp:274:8:274:9 | v8 | |
+| vector.cpp:270:3:270:4 | ref arg v8 | vector.cpp:276:2:276:2 | v8 | |
+| vector.cpp:270:18:270:35 | call to source | vector.cpp:270:3:270:4 | ref arg v8 | TAINT |
+| vector.cpp:271:3:271:4 | ref arg v9 | vector.cpp:275:8:275:9 | v9 | |
+| vector.cpp:271:3:271:4 | ref arg v9 | vector.cpp:276:2:276:2 | v9 | |
+| vector.cpp:271:18:271:34 | call to source | vector.cpp:271:3:271:4 | ref arg v9 | TAINT |
+| vector.cpp:273:8:273:9 | ref arg v7 | vector.cpp:276:2:276:2 | v7 | |
+| vector.cpp:274:8:274:9 | ref arg v8 | vector.cpp:276:2:276:2 | v8 | |
+| vector.cpp:275:8:275:9 | ref arg v9 | vector.cpp:276:2:276:2 | v9 | |
+| vector.cpp:282:19:282:20 | call to vector | vector.cpp:284:2:284:3 | v1 | |
+| vector.cpp:282:19:282:20 | call to vector | vector.cpp:285:7:285:8 | v1 | |
+| vector.cpp:282:19:282:20 | call to vector | vector.cpp:286:7:286:8 | v1 | |
+| vector.cpp:282:19:282:20 | call to vector | vector.cpp:287:7:287:8 | v1 | |
+| vector.cpp:282:19:282:20 | call to vector | vector.cpp:293:1:293:1 | v1 | |
+| vector.cpp:282:23:282:24 | call to vector | vector.cpp:289:4:289:5 | v2 | |
+| vector.cpp:282:23:282:24 | call to vector | vector.cpp:290:7:290:8 | v2 | |
+| vector.cpp:282:23:282:24 | call to vector | vector.cpp:291:7:291:8 | v2 | |
+| vector.cpp:282:23:282:24 | call to vector | vector.cpp:292:7:292:8 | v2 | |
+| vector.cpp:282:23:282:24 | call to vector | vector.cpp:293:1:293:1 | v2 | |
+| vector.cpp:284:2:284:3 | ref arg v1 | vector.cpp:285:7:285:8 | v1 | |
+| vector.cpp:284:2:284:3 | ref arg v1 | vector.cpp:286:7:286:8 | v1 | |
+| vector.cpp:284:2:284:3 | ref arg v1 | vector.cpp:287:7:287:8 | v1 | |
+| vector.cpp:284:2:284:3 | ref arg v1 | vector.cpp:293:1:293:1 | v1 | |
+| vector.cpp:284:15:284:20 | call to source | vector.cpp:284:2:284:3 | ref arg v1 | TAINT |
+| vector.cpp:285:7:285:8 | ref arg v1 | vector.cpp:286:7:286:8 | v1 | |
+| vector.cpp:285:7:285:8 | ref arg v1 | vector.cpp:287:7:287:8 | v1 | |
+| vector.cpp:285:7:285:8 | ref arg v1 | vector.cpp:293:1:293:1 | v1 | |
+| vector.cpp:286:7:286:8 | ref arg v1 | vector.cpp:287:7:287:8 | v1 | |
+| vector.cpp:286:7:286:8 | ref arg v1 | vector.cpp:293:1:293:1 | v1 | |
+| vector.cpp:286:7:286:8 | v1 | vector.cpp:286:10:286:13 | call to data | TAINT |
+| vector.cpp:286:10:286:13 | ref arg call to data | vector.cpp:286:7:286:8 | ref arg v1 | TAINT |
+| vector.cpp:287:7:287:8 | ref arg v1 | vector.cpp:293:1:293:1 | v1 | |
+| vector.cpp:287:7:287:8 | v1 | vector.cpp:287:10:287:13 | call to data | TAINT |
+| vector.cpp:287:10:287:13 | call to data | vector.cpp:287:7:287:18 | access to array | TAINT |
+| vector.cpp:287:17:287:17 | 2 | vector.cpp:287:7:287:18 | access to array | TAINT |
+| vector.cpp:289:2:289:13 | * ... [post update] | vector.cpp:289:7:289:10 | call to data [inner post update] | |
+| vector.cpp:289:2:289:32 | ... = ... | vector.cpp:289:2:289:13 | * ... [post update] | |
+| vector.cpp:289:4:289:5 | ref arg v2 | vector.cpp:290:7:290:8 | v2 | |
+| vector.cpp:289:4:289:5 | ref arg v2 | vector.cpp:291:7:291:8 | v2 | |
+| vector.cpp:289:4:289:5 | ref arg v2 | vector.cpp:292:7:292:8 | v2 | |
+| vector.cpp:289:4:289:5 | ref arg v2 | vector.cpp:293:1:293:1 | v2 | |
+| vector.cpp:289:4:289:5 | v2 | vector.cpp:289:7:289:10 | call to data | TAINT |
+| vector.cpp:289:7:289:10 | call to data | vector.cpp:289:2:289:13 | * ... | TAINT |
+| vector.cpp:289:7:289:10 | call to data [inner post update] | vector.cpp:289:4:289:5 | ref arg v2 | TAINT |
+| vector.cpp:289:17:289:30 | call to source | vector.cpp:289:2:289:32 | ... = ... | |
+| vector.cpp:290:7:290:8 | ref arg v2 | vector.cpp:291:7:291:8 | v2 | |
+| vector.cpp:290:7:290:8 | ref arg v2 | vector.cpp:292:7:292:8 | v2 | |
+| vector.cpp:290:7:290:8 | ref arg v2 | vector.cpp:293:1:293:1 | v2 | |
+| vector.cpp:291:7:291:8 | ref arg v2 | vector.cpp:292:7:292:8 | v2 | |
+| vector.cpp:291:7:291:8 | ref arg v2 | vector.cpp:293:1:293:1 | v2 | |
+| vector.cpp:291:7:291:8 | v2 | vector.cpp:291:10:291:13 | call to data | TAINT |
+| vector.cpp:291:10:291:13 | ref arg call to data | vector.cpp:291:7:291:8 | ref arg v2 | TAINT |
+| vector.cpp:292:7:292:8 | ref arg v2 | vector.cpp:293:1:293:1 | v2 | |
+| vector.cpp:292:7:292:8 | v2 | vector.cpp:292:10:292:13 | call to data | TAINT |
+| vector.cpp:292:10:292:13 | call to data | vector.cpp:292:7:292:18 | access to array | TAINT |
+| vector.cpp:292:17:292:17 | 2 | vector.cpp:292:7:292:18 | access to array | TAINT |
+| vector.cpp:298:19:298:19 | call to vector | vector.cpp:305:7:305:7 | a | |
+| vector.cpp:298:19:298:19 | call to vector | vector.cpp:305:16:305:16 | a | |
+| vector.cpp:298:19:298:19 | call to vector | vector.cpp:306:7:306:7 | a | |
+| vector.cpp:298:19:298:19 | call to vector | vector.cpp:311:25:311:25 | a | |
+| vector.cpp:298:19:298:19 | call to vector | vector.cpp:311:36:311:36 | a | |
+| vector.cpp:298:19:298:19 | call to vector | vector.cpp:313:1:313:1 | a | |
+| vector.cpp:299:19:299:19 | call to vector | vector.cpp:305:25:305:25 | b | |
+| vector.cpp:299:19:299:19 | call to vector | vector.cpp:305:36:305:36 | b | |
+| vector.cpp:299:19:299:19 | call to vector | vector.cpp:313:1:313:1 | b | |
+| vector.cpp:300:19:300:19 | call to vector | vector.cpp:308:7:308:7 | c | |
+| vector.cpp:300:19:300:19 | call to vector | vector.cpp:308:16:308:16 | c | |
+| vector.cpp:300:19:300:19 | call to vector | vector.cpp:309:7:309:7 | c | |
+| vector.cpp:300:19:300:19 | call to vector | vector.cpp:313:1:313:1 | c | |
+| vector.cpp:301:19:301:19 | call to vector | vector.cpp:303:2:303:2 | d | |
+| vector.cpp:301:19:301:19 | call to vector | vector.cpp:308:25:308:25 | d | |
+| vector.cpp:301:19:301:19 | call to vector | vector.cpp:308:36:308:36 | d | |
+| vector.cpp:301:19:301:19 | call to vector | vector.cpp:311:7:311:7 | d | |
+| vector.cpp:301:19:301:19 | call to vector | vector.cpp:311:16:311:16 | d | |
+| vector.cpp:301:19:301:19 | call to vector | vector.cpp:312:7:312:7 | d | |
+| vector.cpp:301:19:301:19 | call to vector | vector.cpp:313:1:313:1 | d | |
+| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:308:25:308:25 | d | |
+| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:308:36:308:36 | d | |
+| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:311:7:311:7 | d | |
+| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:311:16:311:16 | d | |
+| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:312:7:312:7 | d | |
+| vector.cpp:303:2:303:2 | ref arg d | vector.cpp:313:1:313:1 | d | |
+| vector.cpp:303:14:303:19 | call to source | vector.cpp:303:2:303:2 | ref arg d | TAINT |
+| vector.cpp:305:7:305:7 | a | vector.cpp:305:9:305:14 | call to insert | TAINT |
+| vector.cpp:305:7:305:7 | ref arg a | vector.cpp:306:7:306:7 | a | |
+| vector.cpp:305:7:305:7 | ref arg a | vector.cpp:311:25:311:25 | a | |
+| vector.cpp:305:7:305:7 | ref arg a | vector.cpp:311:36:311:36 | a | |
+| vector.cpp:305:7:305:7 | ref arg a | vector.cpp:313:1:313:1 | a | |
+| vector.cpp:305:16:305:16 | a | vector.cpp:305:18:305:20 | call to end | TAINT |
+| vector.cpp:305:16:305:16 | ref arg a | vector.cpp:305:7:305:7 | a | |
+| vector.cpp:305:16:305:16 | ref arg a | vector.cpp:306:7:306:7 | a | |
+| vector.cpp:305:16:305:16 | ref arg a | vector.cpp:311:25:311:25 | a | |
+| vector.cpp:305:16:305:16 | ref arg a | vector.cpp:311:36:311:36 | a | |
+| vector.cpp:305:16:305:16 | ref arg a | vector.cpp:313:1:313:1 | a | |
+| vector.cpp:305:18:305:20 | call to end | vector.cpp:305:16:305:22 | call to iterator | TAINT |
+| vector.cpp:305:25:305:25 | b | vector.cpp:305:27:305:31 | call to begin | TAINT |
+| vector.cpp:305:25:305:25 | ref arg b | vector.cpp:305:36:305:36 | b | |
+| vector.cpp:305:25:305:25 | ref arg b | vector.cpp:313:1:313:1 | b | |
+| vector.cpp:305:27:305:31 | call to begin | vector.cpp:305:7:305:7 | ref arg a | TAINT |
+| vector.cpp:305:27:305:31 | call to begin | vector.cpp:305:9:305:14 | call to insert | TAINT |
+| vector.cpp:305:36:305:36 | b | vector.cpp:305:38:305:40 | call to end | TAINT |
+| vector.cpp:305:36:305:36 | ref arg b | vector.cpp:313:1:313:1 | b | |
+| vector.cpp:305:38:305:40 | call to end | vector.cpp:305:7:305:7 | ref arg a | TAINT |
+| vector.cpp:305:38:305:40 | call to end | vector.cpp:305:9:305:14 | call to insert | TAINT |
+| vector.cpp:306:7:306:7 | ref arg a | vector.cpp:311:25:311:25 | a | |
+| vector.cpp:306:7:306:7 | ref arg a | vector.cpp:311:36:311:36 | a | |
+| vector.cpp:306:7:306:7 | ref arg a | vector.cpp:313:1:313:1 | a | |
+| vector.cpp:308:7:308:7 | c | vector.cpp:308:9:308:14 | call to insert | TAINT |
+| vector.cpp:308:7:308:7 | ref arg c | vector.cpp:309:7:309:7 | c | |
+| vector.cpp:308:7:308:7 | ref arg c | vector.cpp:313:1:313:1 | c | |
+| vector.cpp:308:16:308:16 | c | vector.cpp:308:18:308:20 | call to end | TAINT |
+| vector.cpp:308:16:308:16 | ref arg c | vector.cpp:308:7:308:7 | c | |
+| vector.cpp:308:16:308:16 | ref arg c | vector.cpp:309:7:309:7 | c | |
+| vector.cpp:308:16:308:16 | ref arg c | vector.cpp:313:1:313:1 | c | |
+| vector.cpp:308:18:308:20 | call to end | vector.cpp:308:16:308:22 | call to iterator | TAINT |
+| vector.cpp:308:25:308:25 | d | vector.cpp:308:27:308:31 | call to begin | TAINT |
+| vector.cpp:308:25:308:25 | ref arg d | vector.cpp:308:36:308:36 | d | |
+| vector.cpp:308:25:308:25 | ref arg d | vector.cpp:311:7:311:7 | d | |
+| vector.cpp:308:25:308:25 | ref arg d | vector.cpp:311:16:311:16 | d | |
+| vector.cpp:308:25:308:25 | ref arg d | vector.cpp:312:7:312:7 | d | |
+| vector.cpp:308:25:308:25 | ref arg d | vector.cpp:313:1:313:1 | d | |
+| vector.cpp:308:27:308:31 | call to begin | vector.cpp:308:7:308:7 | ref arg c | TAINT |
+| vector.cpp:308:27:308:31 | call to begin | vector.cpp:308:9:308:14 | call to insert | TAINT |
+| vector.cpp:308:36:308:36 | d | vector.cpp:308:38:308:40 | call to end | TAINT |
+| vector.cpp:308:36:308:36 | ref arg d | vector.cpp:311:7:311:7 | d | |
+| vector.cpp:308:36:308:36 | ref arg d | vector.cpp:311:16:311:16 | d | |
+| vector.cpp:308:36:308:36 | ref arg d | vector.cpp:312:7:312:7 | d | |
+| vector.cpp:308:36:308:36 | ref arg d | vector.cpp:313:1:313:1 | d | |
+| vector.cpp:308:38:308:40 | call to end | vector.cpp:308:7:308:7 | ref arg c | TAINT |
+| vector.cpp:308:38:308:40 | call to end | vector.cpp:308:9:308:14 | call to insert | TAINT |
+| vector.cpp:309:7:309:7 | ref arg c | vector.cpp:313:1:313:1 | c | |
+| vector.cpp:311:7:311:7 | d | vector.cpp:311:9:311:14 | call to insert | TAINT |
+| vector.cpp:311:7:311:7 | ref arg d | vector.cpp:312:7:312:7 | d | |
+| vector.cpp:311:7:311:7 | ref arg d | vector.cpp:313:1:313:1 | d | |
+| vector.cpp:311:16:311:16 | d | vector.cpp:311:18:311:20 | call to end | TAINT |
+| vector.cpp:311:16:311:16 | ref arg d | vector.cpp:311:7:311:7 | d | |
+| vector.cpp:311:16:311:16 | ref arg d | vector.cpp:312:7:312:7 | d | |
+| vector.cpp:311:16:311:16 | ref arg d | vector.cpp:313:1:313:1 | d | |
+| vector.cpp:311:18:311:20 | call to end | vector.cpp:311:16:311:22 | call to iterator | TAINT |
+| vector.cpp:311:25:311:25 | a | vector.cpp:311:27:311:31 | call to begin | TAINT |
+| vector.cpp:311:25:311:25 | ref arg a | vector.cpp:311:36:311:36 | a | |
+| vector.cpp:311:25:311:25 | ref arg a | vector.cpp:313:1:313:1 | a | |
+| vector.cpp:311:27:311:31 | call to begin | vector.cpp:311:7:311:7 | ref arg d | TAINT |
+| vector.cpp:311:27:311:31 | call to begin | vector.cpp:311:9:311:14 | call to insert | TAINT |
+| vector.cpp:311:36:311:36 | a | vector.cpp:311:38:311:40 | call to end | TAINT |
+| vector.cpp:311:36:311:36 | ref arg a | vector.cpp:313:1:313:1 | a | |
+| vector.cpp:311:38:311:40 | call to end | vector.cpp:311:7:311:7 | ref arg d | TAINT |
+| vector.cpp:311:38:311:40 | call to end | vector.cpp:311:9:311:14 | call to insert | TAINT |
+| vector.cpp:312:7:312:7 | ref arg d | vector.cpp:313:1:313:1 | d | |
+| vector.cpp:316:19:316:20 | call to vector | vector.cpp:320:22:320:23 | v1 | |
+| vector.cpp:316:19:316:20 | call to vector | vector.cpp:320:34:320:35 | v1 | |
+| vector.cpp:316:19:316:20 | call to vector | vector.cpp:323:7:323:8 | v1 | |
+| vector.cpp:316:19:316:20 | call to vector | vector.cpp:327:1:327:1 | v1 | |
+| vector.cpp:317:19:317:20 | call to vector | vector.cpp:318:2:318:3 | v2 | |
+| vector.cpp:317:19:317:20 | call to vector | vector.cpp:321:22:321:23 | v2 | |
+| vector.cpp:317:19:317:20 | call to vector | vector.cpp:321:34:321:35 | v2 | |
+| vector.cpp:317:19:317:20 | call to vector | vector.cpp:324:7:324:8 | v2 | |
+| vector.cpp:317:19:317:20 | call to vector | vector.cpp:327:1:327:1 | v2 | |
+| vector.cpp:318:2:318:3 | ref arg v2 | vector.cpp:321:22:321:23 | v2 | |
+| vector.cpp:318:2:318:3 | ref arg v2 | vector.cpp:321:34:321:35 | v2 | |
+| vector.cpp:318:2:318:3 | ref arg v2 | vector.cpp:324:7:324:8 | v2 | |
+| vector.cpp:318:2:318:3 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | |
+| vector.cpp:318:15:318:20 | call to source | vector.cpp:318:2:318:3 | ref arg v2 | TAINT |
+| vector.cpp:320:22:320:23 | ref arg v1 | vector.cpp:320:34:320:35 | v1 | |
+| vector.cpp:320:22:320:23 | ref arg v1 | vector.cpp:323:7:323:8 | v1 | |
+| vector.cpp:320:22:320:23 | ref arg v1 | vector.cpp:327:1:327:1 | v1 | |
+| vector.cpp:320:22:320:23 | v1 | vector.cpp:320:25:320:29 | call to begin | TAINT |
+| vector.cpp:320:22:320:42 | call to vector | vector.cpp:325:7:325:8 | v3 | |
+| vector.cpp:320:22:320:42 | call to vector | vector.cpp:327:1:327:1 | v3 | |
+| vector.cpp:320:25:320:29 | call to begin | vector.cpp:320:22:320:42 | call to vector | TAINT |
+| vector.cpp:320:34:320:35 | ref arg v1 | vector.cpp:323:7:323:8 | v1 | |
+| vector.cpp:320:34:320:35 | ref arg v1 | vector.cpp:327:1:327:1 | v1 | |
+| vector.cpp:320:34:320:35 | v1 | vector.cpp:320:37:320:39 | call to end | TAINT |
+| vector.cpp:320:37:320:39 | call to end | vector.cpp:320:22:320:42 | call to vector | TAINT |
+| vector.cpp:321:22:321:23 | ref arg v2 | vector.cpp:321:34:321:35 | v2 | |
+| vector.cpp:321:22:321:23 | ref arg v2 | vector.cpp:324:7:324:8 | v2 | |
+| vector.cpp:321:22:321:23 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | |
+| vector.cpp:321:22:321:23 | v2 | vector.cpp:321:25:321:29 | call to begin | TAINT |
+| vector.cpp:321:22:321:42 | call to vector | vector.cpp:326:7:326:8 | v4 | |
+| vector.cpp:321:22:321:42 | call to vector | vector.cpp:327:1:327:1 | v4 | |
+| vector.cpp:321:25:321:29 | call to begin | vector.cpp:321:22:321:42 | call to vector | TAINT |
+| vector.cpp:321:34:321:35 | ref arg v2 | vector.cpp:324:7:324:8 | v2 | |
+| vector.cpp:321:34:321:35 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | |
+| vector.cpp:321:34:321:35 | v2 | vector.cpp:321:37:321:39 | call to end | TAINT |
+| vector.cpp:321:37:321:39 | call to end | vector.cpp:321:22:321:42 | call to vector | TAINT |
+| vector.cpp:323:7:323:8 | ref arg v1 | vector.cpp:327:1:327:1 | v1 | |
+| vector.cpp:324:7:324:8 | ref arg v2 | vector.cpp:327:1:327:1 | v2 | |
+| vector.cpp:325:7:325:8 | ref arg v3 | vector.cpp:327:1:327:1 | v3 | |
+| vector.cpp:326:7:326:8 | ref arg v4 | vector.cpp:327:1:327:1 | v4 | |
+| vector.cpp:329:62:329:65 | iter | vector.cpp:329:62:329:65 | iter | |
+| vector.cpp:329:62:329:65 | iter | vector.cpp:330:3:330:6 | iter | |
+| vector.cpp:330:2:330:2 | call to operator* [post update] | vector.cpp:329:62:329:65 | iter | |
+| vector.cpp:330:2:330:17 | ... = ... | vector.cpp:330:2:330:2 | call to operator* [post update] | |
+| vector.cpp:330:3:330:6 | iter | vector.cpp:330:2:330:2 | call to operator* | TAINT |
+| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:2 | call to operator* [post update] | TAINT |
+| vector.cpp:330:10:330:15 | call to source | vector.cpp:330:2:330:17 | ... = ... | |
+| vector.cpp:333:64:333:67 | iter | vector.cpp:333:64:333:67 | iter | |
+| vector.cpp:333:64:333:67 | iter | vector.cpp:334:3:334:6 | iter | |
+| vector.cpp:333:74:333:74 | i | vector.cpp:334:10:334:10 | i | |
+| vector.cpp:334:2:334:2 | call to operator* [post update] | vector.cpp:333:64:333:67 | iter | |
+| vector.cpp:334:2:334:10 | ... = ... | vector.cpp:334:2:334:2 | call to operator* [post update] | |
+| vector.cpp:334:3:334:6 | iter | vector.cpp:334:2:334:2 | call to operator* | TAINT |
+| vector.cpp:334:10:334:10 | i | vector.cpp:334:2:334:2 | call to operator* [post update] | TAINT |
+| vector.cpp:334:10:334:10 | i | vector.cpp:334:2:334:10 | ... = ... | |
+| vector.cpp:337:38:337:38 | b | vector.cpp:372:5:372:5 | b | |
+| vector.cpp:338:22:338:24 | call to vector | vector.cpp:340:34:340:35 | v1 | |
+| vector.cpp:338:22:338:24 | call to vector | vector.cpp:342:7:342:8 | v1 | |
+| vector.cpp:338:22:338:24 | call to vector | vector.cpp:401:1:401:1 | v1 | |
+| vector.cpp:338:30:338:32 | call to vector | vector.cpp:344:38:344:39 | v2 | |
+| vector.cpp:338:30:338:32 | call to vector | vector.cpp:344:56:344:57 | v2 | |
+| vector.cpp:338:30:338:32 | call to vector | vector.cpp:347:7:347:8 | v2 | |
+| vector.cpp:338:30:338:32 | call to vector | vector.cpp:401:1:401:1 | v2 | |
+| vector.cpp:338:38:338:40 | call to vector | vector.cpp:349:15:349:16 | v3 | |
+| vector.cpp:338:38:338:40 | call to vector | vector.cpp:352:7:352:8 | v3 | |
+| vector.cpp:338:38:338:40 | call to vector | vector.cpp:401:1:401:1 | v3 | |
+| vector.cpp:338:46:338:48 | call to vector | vector.cpp:354:38:354:39 | v4 | |
+| vector.cpp:338:46:338:48 | call to vector | vector.cpp:354:56:354:57 | v4 | |
+| vector.cpp:338:46:338:48 | call to vector | vector.cpp:357:7:357:8 | v4 | |
+| vector.cpp:338:46:338:48 | call to vector | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:338:54:338:56 | call to vector | vector.cpp:359:34:359:35 | v5 | |
+| vector.cpp:338:54:338:56 | call to vector | vector.cpp:361:7:361:8 | v5 | |
+| vector.cpp:338:54:338:56 | call to vector | vector.cpp:363:7:363:8 | v5 | |
+| vector.cpp:338:54:338:56 | call to vector | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:338:62:338:64 | call to vector | vector.cpp:365:34:365:35 | v6 | |
+| vector.cpp:338:62:338:64 | call to vector | vector.cpp:367:7:367:8 | v6 | |
+| vector.cpp:338:62:338:64 | call to vector | vector.cpp:368:2:368:3 | v6 | |
+| vector.cpp:338:62:338:64 | call to vector | vector.cpp:369:7:369:8 | v6 | |
+| vector.cpp:338:62:338:64 | call to vector | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:338:70:338:72 | call to vector | vector.cpp:371:34:371:35 | v7 | |
+| vector.cpp:338:70:338:72 | call to vector | vector.cpp:374:8:374:9 | v7 | |
+| vector.cpp:338:70:338:72 | call to vector | vector.cpp:377:8:377:9 | v7 | |
+| vector.cpp:338:70:338:72 | call to vector | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:338:70:338:72 | call to vector | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:338:78:338:80 | call to vector | vector.cpp:381:34:381:35 | v8 | |
+| vector.cpp:338:78:338:80 | call to vector | vector.cpp:383:7:383:8 | v8 | |
+| vector.cpp:338:78:338:80 | call to vector | vector.cpp:385:7:385:8 | v8 | |
+| vector.cpp:338:78:338:80 | call to vector | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:338:86:338:88 | call to vector | vector.cpp:387:34:387:35 | v9 | |
+| vector.cpp:338:86:338:88 | call to vector | vector.cpp:392:7:392:8 | v9 | |
+| vector.cpp:338:86:338:88 | call to vector | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:338:95:338:97 | call to vector | vector.cpp:394:35:394:37 | v10 | |
+| vector.cpp:338:95:338:97 | call to vector | vector.cpp:396:7:396:9 | v10 | |
+| vector.cpp:338:95:338:97 | call to vector | vector.cpp:401:1:401:1 | v10 | |
+| vector.cpp:338:104:338:106 | call to vector | vector.cpp:398:35:398:37 | v11 | |
+| vector.cpp:338:104:338:106 | call to vector | vector.cpp:400:7:400:9 | v11 | |
+| vector.cpp:338:104:338:106 | call to vector | vector.cpp:401:1:401:1 | v11 | |
+| vector.cpp:340:34:340:35 | ref arg v1 | vector.cpp:342:7:342:8 | v1 | |
+| vector.cpp:340:34:340:35 | ref arg v1 | vector.cpp:401:1:401:1 | v1 | |
+| vector.cpp:340:34:340:35 | v1 | vector.cpp:340:37:340:41 | call to begin | TAINT |
+| vector.cpp:340:37:340:41 | call to begin | vector.cpp:341:3:341:4 | i1 | |
+| vector.cpp:341:2:341:2 | call to operator* [post update] | vector.cpp:342:7:342:8 | v1 | |
+| vector.cpp:341:2:341:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v1 | |
+| vector.cpp:341:2:341:15 | ... = ... | vector.cpp:341:2:341:2 | call to operator* [post update] | |
+| vector.cpp:341:3:341:4 | i1 | vector.cpp:341:2:341:2 | call to operator* | TAINT |
+| vector.cpp:341:8:341:13 | call to source | vector.cpp:341:2:341:2 | call to operator* [post update] | TAINT |
+| vector.cpp:341:8:341:13 | call to source | vector.cpp:341:2:341:15 | ... = ... | |
+| vector.cpp:342:7:342:8 | ref arg v1 | vector.cpp:401:1:401:1 | v1 | |
+| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:344:56:344:57 | v2 | |
+| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:347:7:347:8 | v2 | |
+| vector.cpp:344:38:344:39 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | |
+| vector.cpp:344:38:344:39 | v2 | vector.cpp:344:41:344:45 | call to begin | TAINT |
+| vector.cpp:344:41:344:45 | call to begin | vector.cpp:344:50:344:51 | it | |
+| vector.cpp:344:41:344:45 | call to begin | vector.cpp:344:68:344:69 | it | |
+| vector.cpp:344:41:344:45 | call to begin | vector.cpp:345:4:345:5 | it | |
+| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:344:56:344:57 | v2 | |
+| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:347:7:347:8 | v2 | |
+| vector.cpp:344:56:344:57 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | |
+| vector.cpp:344:56:344:57 | v2 | vector.cpp:344:59:344:61 | call to end | TAINT |
+| vector.cpp:344:68:344:69 | it | vector.cpp:344:66:344:66 | call to operator++ | |
+| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:344:50:344:51 | it | |
+| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:344:68:344:69 | it | |
+| vector.cpp:344:68:344:69 | ref arg it | vector.cpp:345:4:345:5 | it | |
+| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:344:56:344:57 | v2 | |
+| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:347:7:347:8 | v2 | |
+| vector.cpp:345:3:345:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v2 | |
+| vector.cpp:345:3:345:16 | ... = ... | vector.cpp:345:3:345:3 | call to operator* [post update] | |
+| vector.cpp:345:4:345:5 | it | vector.cpp:345:3:345:3 | call to operator* | TAINT |
+| vector.cpp:345:9:345:14 | call to source | vector.cpp:345:3:345:3 | call to operator* [post update] | TAINT |
+| vector.cpp:345:9:345:14 | call to source | vector.cpp:345:3:345:16 | ... = ... | |
+| vector.cpp:347:7:347:8 | ref arg v2 | vector.cpp:401:1:401:1 | v2 | |
+| vector.cpp:349:15:349:15 | (__begin) | vector.cpp:349:15:349:15 | call to operator* | TAINT |
+| vector.cpp:349:15:349:15 | (__begin) | vector.cpp:349:15:349:15 | call to operator++ | |
+| vector.cpp:349:15:349:15 | (__end) | vector.cpp:349:15:349:15 | call to iterator | |
+| vector.cpp:349:15:349:15 | (__range) | vector.cpp:349:15:349:15 | call to begin | TAINT |
+| vector.cpp:349:15:349:15 | (__range) | vector.cpp:349:15:349:15 | call to end | TAINT |
+| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | call to begin | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | call to end | vector.cpp:349:15:349:15 | (__end) | |
+| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | ref arg (__begin) | vector.cpp:349:15:349:15 | (__begin) | |
+| vector.cpp:349:15:349:15 | ref arg (__range) | vector.cpp:349:15:349:15 | (__range) | |
+| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | (__range) | |
+| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | (__range) | |
+| vector.cpp:349:15:349:16 | v3 | vector.cpp:349:15:349:15 | call to operator* | TAINT |
+| vector.cpp:350:3:350:14 | ... = ... | vector.cpp:350:3:350:3 | x [post update] | |
+| vector.cpp:350:7:350:12 | call to source | vector.cpp:350:3:350:14 | ... = ... | |
+| vector.cpp:352:7:352:8 | ref arg v3 | vector.cpp:401:1:401:1 | v3 | |
+| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:354:56:354:57 | v4 | |
+| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:357:7:357:8 | v4 | |
+| vector.cpp:354:38:354:39 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:354:38:354:39 | v4 | vector.cpp:354:41:354:45 | call to begin | TAINT |
+| vector.cpp:354:41:354:45 | call to begin | vector.cpp:354:50:354:51 | it | |
+| vector.cpp:354:41:354:45 | call to begin | vector.cpp:354:68:354:69 | it | |
+| vector.cpp:354:41:354:45 | call to begin | vector.cpp:355:32:355:33 | it | |
+| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:354:56:354:57 | v4 | |
+| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:357:7:357:8 | v4 | |
+| vector.cpp:354:56:354:57 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:354:56:354:57 | v4 | vector.cpp:354:59:354:61 | call to end | TAINT |
+| vector.cpp:354:68:354:69 | it | vector.cpp:354:66:354:66 | call to operator++ | |
+| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:354:50:354:51 | it | |
+| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:354:68:354:69 | it | |
+| vector.cpp:354:68:354:69 | ref arg it | vector.cpp:355:32:355:33 | it | |
+| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:354:56:354:57 | v4 | |
+| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:357:7:357:8 | v4 | |
+| vector.cpp:355:32:355:33 | call to iterator [post update] | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:355:32:355:33 | it | vector.cpp:355:32:355:33 | call to iterator | |
+| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:354:56:354:57 | v4 | |
+| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:357:7:357:8 | v4 | |
+| vector.cpp:355:32:355:33 | it [post update] | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:357:7:357:8 | ref arg v4 | vector.cpp:401:1:401:1 | v4 | |
+| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:361:7:361:8 | v5 | |
+| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:363:7:363:8 | v5 | |
+| vector.cpp:359:34:359:35 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:359:34:359:35 | v5 | vector.cpp:359:37:359:41 | call to begin | TAINT |
+| vector.cpp:359:37:359:41 | call to begin | vector.cpp:360:3:360:4 | i5 | |
+| vector.cpp:359:37:359:41 | call to begin | vector.cpp:362:3:362:4 | i5 | |
+| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:361:7:361:8 | v5 | |
+| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v5 | |
+| vector.cpp:360:2:360:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:360:2:360:15 | ... = ... | vector.cpp:360:2:360:2 | call to operator* [post update] | |
+| vector.cpp:360:3:360:4 | i5 | vector.cpp:360:2:360:2 | call to operator* | TAINT |
+| vector.cpp:360:8:360:13 | call to source | vector.cpp:360:2:360:2 | call to operator* [post update] | TAINT |
+| vector.cpp:360:8:360:13 | call to source | vector.cpp:360:2:360:15 | ... = ... | |
+| vector.cpp:361:7:361:8 | ref arg v5 | vector.cpp:363:7:363:8 | v5 | |
+| vector.cpp:361:7:361:8 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:363:7:363:8 | v5 | |
+| vector.cpp:362:2:362:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:362:2:362:8 | ... = ... | vector.cpp:362:2:362:2 | call to operator* [post update] | |
+| vector.cpp:362:3:362:4 | i5 | vector.cpp:362:2:362:2 | call to operator* | TAINT |
+| vector.cpp:362:8:362:8 | 1 | vector.cpp:362:2:362:2 | call to operator* [post update] | TAINT |
+| vector.cpp:362:8:362:8 | 1 | vector.cpp:362:2:362:8 | ... = ... | |
+| vector.cpp:363:7:363:8 | ref arg v5 | vector.cpp:401:1:401:1 | v5 | |
+| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:367:7:367:8 | v6 | |
+| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:368:2:368:3 | v6 | |
+| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | |
+| vector.cpp:365:34:365:35 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:365:34:365:35 | v6 | vector.cpp:365:37:365:41 | call to begin | TAINT |
+| vector.cpp:365:37:365:41 | call to begin | vector.cpp:366:3:366:4 | i6 | |
+| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:367:7:367:8 | v6 | |
+| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:368:2:368:3 | v6 | |
+| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:369:7:369:8 | v6 | |
+| vector.cpp:366:2:366:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:366:2:366:15 | ... = ... | vector.cpp:366:2:366:2 | call to operator* [post update] | |
+| vector.cpp:366:3:366:4 | i6 | vector.cpp:366:2:366:2 | call to operator* | TAINT |
+| vector.cpp:366:8:366:13 | call to source | vector.cpp:366:2:366:2 | call to operator* [post update] | TAINT |
+| vector.cpp:366:8:366:13 | call to source | vector.cpp:366:2:366:15 | ... = ... | |
+| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:368:2:368:3 | v6 | |
+| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | |
+| vector.cpp:367:7:367:8 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:368:2:368:3 | ref arg v6 | vector.cpp:369:7:369:8 | v6 | |
+| vector.cpp:368:2:368:3 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:368:7:368:26 | call to vector | vector.cpp:368:2:368:3 | ref arg v6 | TAINT |
+| vector.cpp:368:7:368:26 | call to vector | vector.cpp:368:5:368:5 | call to operator= | TAINT |
+| vector.cpp:369:7:369:8 | ref arg v6 | vector.cpp:401:1:401:1 | v6 | |
+| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:374:8:374:9 | v7 | |
+| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:377:8:377:9 | v7 | |
+| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:371:34:371:35 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:371:34:371:35 | v7 | vector.cpp:371:37:371:41 | call to begin | TAINT |
+| vector.cpp:371:37:371:41 | call to begin | vector.cpp:373:4:373:5 | i7 | |
+| vector.cpp:371:37:371:41 | call to begin | vector.cpp:376:4:376:5 | i7 | |
+| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:374:8:374:9 | v7 | |
+| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:373:3:373:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:373:3:373:16 | ... = ... | vector.cpp:373:3:373:3 | call to operator* [post update] | |
+| vector.cpp:373:4:373:5 | i7 | vector.cpp:373:3:373:3 | call to operator* | TAINT |
+| vector.cpp:373:9:373:14 | call to source | vector.cpp:373:3:373:3 | call to operator* [post update] | TAINT |
+| vector.cpp:373:9:373:14 | call to source | vector.cpp:373:3:373:16 | ... = ... | |
+| vector.cpp:374:8:374:9 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:374:8:374:9 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:377:8:377:9 | v7 | |
+| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:376:3:376:3 | call to operator* [post update] | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:376:3:376:9 | ... = ... | vector.cpp:376:3:376:3 | call to operator* [post update] | |
+| vector.cpp:376:4:376:5 | i7 | vector.cpp:376:3:376:3 | call to operator* | TAINT |
+| vector.cpp:376:9:376:9 | 1 | vector.cpp:376:3:376:3 | call to operator* [post update] | TAINT |
+| vector.cpp:376:9:376:9 | 1 | vector.cpp:376:3:376:9 | ... = ... | |
+| vector.cpp:377:8:377:9 | ref arg v7 | vector.cpp:379:7:379:8 | v7 | |
+| vector.cpp:377:8:377:9 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:379:7:379:8 | ref arg v7 | vector.cpp:401:1:401:1 | v7 | |
+| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:383:7:383:8 | v8 | |
+| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:385:7:385:8 | v8 | |
+| vector.cpp:381:34:381:35 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:381:34:381:35 | v8 | vector.cpp:381:37:381:41 | call to begin | TAINT |
+| vector.cpp:381:37:381:41 | call to begin | vector.cpp:382:3:382:4 | i8 | |
+| vector.cpp:381:37:381:41 | call to begin | vector.cpp:384:3:384:4 | i8 | |
+| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:383:7:383:8 | v8 | |
+| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:385:7:385:8 | v8 | |
+| vector.cpp:382:2:382:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:382:2:382:15 | ... = ... | vector.cpp:382:2:382:2 | call to operator* [post update] | |
+| vector.cpp:382:3:382:4 | i8 | vector.cpp:382:2:382:2 | call to operator* | TAINT |
+| vector.cpp:382:8:382:13 | call to source | vector.cpp:382:2:382:2 | call to operator* [post update] | TAINT |
+| vector.cpp:382:8:382:13 | call to source | vector.cpp:382:2:382:15 | ... = ... | |
+| vector.cpp:383:7:383:8 | ref arg v8 | vector.cpp:385:7:385:8 | v8 | |
+| vector.cpp:383:7:383:8 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:384:2:384:2 | call to operator* [post update] | vector.cpp:385:7:385:8 | v8 | |
+| vector.cpp:384:2:384:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:384:2:384:8 | ... = ... | vector.cpp:384:2:384:2 | call to operator* [post update] | |
+| vector.cpp:384:3:384:4 | i8 | vector.cpp:384:2:384:2 | call to operator* | TAINT |
+| vector.cpp:384:8:384:8 | 1 | vector.cpp:384:2:384:2 | call to operator* [post update] | TAINT |
+| vector.cpp:384:8:384:8 | 1 | vector.cpp:384:2:384:8 | ... = ... | |
+| vector.cpp:385:7:385:8 | ref arg v8 | vector.cpp:401:1:401:1 | v8 | |
+| vector.cpp:387:34:387:35 | ref arg v9 | vector.cpp:392:7:392:8 | v9 | |
+| vector.cpp:387:34:387:35 | ref arg v9 | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:387:34:387:35 | v9 | vector.cpp:387:37:387:41 | call to begin | TAINT |
+| vector.cpp:387:37:387:41 | call to begin | vector.cpp:389:3:389:4 | i9 | |
+| vector.cpp:387:37:387:41 | call to begin | vector.cpp:390:31:390:32 | i9 | |
+| vector.cpp:389:2:389:2 | call to operator* [post update] | vector.cpp:392:7:392:8 | v9 | |
+| vector.cpp:389:2:389:2 | call to operator* [post update] | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:389:2:389:15 | ... = ... | vector.cpp:389:2:389:2 | call to operator* [post update] | |
+| vector.cpp:389:3:389:4 | i9 | vector.cpp:389:2:389:2 | call to operator* | TAINT |
+| vector.cpp:389:8:389:13 | call to source | vector.cpp:389:2:389:2 | call to operator* [post update] | TAINT |
+| vector.cpp:389:8:389:13 | call to source | vector.cpp:389:2:389:15 | ... = ... | |
+| vector.cpp:390:31:390:32 | call to iterator [post update] | vector.cpp:392:7:392:8 | v9 | |
+| vector.cpp:390:31:390:32 | call to iterator [post update] | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:390:31:390:32 | i9 | vector.cpp:390:31:390:32 | call to iterator | |
+| vector.cpp:390:31:390:32 | i9 [post update] | vector.cpp:392:7:392:8 | v9 | |
+| vector.cpp:390:31:390:32 | i9 [post update] | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:392:7:392:8 | ref arg v9 | vector.cpp:401:1:401:1 | v9 | |
+| vector.cpp:394:35:394:37 | ref arg v10 | vector.cpp:396:7:396:9 | v10 | |
+| vector.cpp:394:35:394:37 | ref arg v10 | vector.cpp:401:1:401:1 | v10 | |
+| vector.cpp:394:35:394:37 | v10 | vector.cpp:394:39:394:43 | call to begin | TAINT |
+| vector.cpp:394:39:394:43 | call to begin | vector.cpp:395:33:395:35 | i10 | |
+| vector.cpp:395:33:395:35 | call to iterator [post update] | vector.cpp:396:7:396:9 | v10 | |
+| vector.cpp:395:33:395:35 | call to iterator [post update] | vector.cpp:401:1:401:1 | v10 | |
+| vector.cpp:395:33:395:35 | i10 | vector.cpp:395:33:395:35 | call to iterator | |
+| vector.cpp:395:33:395:35 | i10 [post update] | vector.cpp:396:7:396:9 | v10 | |
+| vector.cpp:395:33:395:35 | i10 [post update] | vector.cpp:401:1:401:1 | v10 | |
+| vector.cpp:396:7:396:9 | ref arg v10 | vector.cpp:401:1:401:1 | v10 | |
+| vector.cpp:398:35:398:37 | ref arg v11 | vector.cpp:400:7:400:9 | v11 | |
+| vector.cpp:398:35:398:37 | ref arg v11 | vector.cpp:401:1:401:1 | v11 | |
+| vector.cpp:398:35:398:37 | v11 | vector.cpp:398:39:398:43 | call to begin | TAINT |
+| vector.cpp:398:39:398:43 | call to begin | vector.cpp:399:33:399:35 | i11 | |
+| vector.cpp:399:33:399:35 | call to iterator [post update] | vector.cpp:400:7:400:9 | v11 | |
+| vector.cpp:399:33:399:35 | call to iterator [post update] | vector.cpp:401:1:401:1 | v11 | |
+| vector.cpp:399:33:399:35 | i11 | vector.cpp:399:33:399:35 | call to iterator | |
+| vector.cpp:399:33:399:35 | i11 [post update] | vector.cpp:400:7:400:9 | v11 | |
+| vector.cpp:399:33:399:35 | i11 [post update] | vector.cpp:401:1:401:1 | v11 | |
+| vector.cpp:400:7:400:9 | ref arg v11 | vector.cpp:401:1:401:1 | v11 | |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp
new file mode 100644
index 000000000000..6b4d32f074f4
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp
@@ -0,0 +1,438 @@
+
+#include "stl.h"
+
+using namespace std;
+
+char *source();
+
+void sink(char *);
+void sink(const char *);
+void sink(bool);
+void sink(std::pair);
+void sink(std::map);
+void sink(std::map::iterator);
+void sink(std::unordered_map);
+void sink(std::unordered_map::iterator);
+void sink(std::unordered_map >);
+void sink(std::unordered_map >::iterator);
+
+void test_pair()
+{
+ std::pair a, b, c;
+
+ a.first = "123";
+ sink(a.first);
+ sink(a.second);
+ sink(a);
+
+ b.first = source();
+ sink(b.first); // tainted
+ sink(b.second);
+ sink(b); // tainted [NOT DETECTED]
+
+ c.second = source();
+ sink(c.first);
+ sink(c.second); // tainted
+ sink(c); // tainted [NOT DETECTED]
+
+ std::pair d("123", "456");
+ sink(d.first);
+ sink(d.second);
+ sink(d);
+
+ std::pair e(source(), "456");
+ sink(e.first); // tainted
+ sink(e.second);
+ sink(e); // tainted [NOT DETECTED]
+
+ std::pair f("123", source());
+ sink(f.first); // [FALSE POSITIVE]
+ sink(f.second); // tainted
+ sink(f); // tainted
+
+ std::pair g(f);
+ sink(g.first); // [FALSE POSITIVE]
+ sink(g.second); // tainted
+ sink(g); // tainted
+
+ std::pair h;
+ h = f;
+ sink(h.first); // [FALSE POSITIVE]
+ sink(h.second); // tainted
+ sink(h); // tainted
+
+ std::pair i("123", "456");
+ std::pair j("123", source());
+ std::pair k("123", source());
+ std::pair l("123", "456");
+ i.swap(j);
+ k.swap(l);
+ sink(i.first); // [FALSE POSITIVE]
+ sink(i.second); // tainted
+ sink(i); // tainted
+ sink(j.first); // [FALSE POSITIVE]
+ sink(j.second); // [FALSE POSITIVE]
+ sink(j); // [FALSE POSITIVE]
+ sink(k.first); // [FALSE POSITIVE]
+ sink(k.second); // [FALSE POSITIVE]
+ sink(k); // [FALSE POSITIVE]
+ sink(l.first); // [FALSE POSITIVE]
+ sink(l.second); // tainted
+ sink(l); // tainted
+
+ sink(make_pair("123", "456"));
+ sink(make_pair("123", "456").first);
+ sink(make_pair("123", "456").second);
+ sink(make_pair(source(), "456")); // tainted [NOT DETECTED]
+ sink(make_pair(source(), "456").first); // tainted [NOT DETECTED]
+ sink(make_pair(source(), "456").second);
+ sink(make_pair("123", source())); // tainted
+ sink(make_pair("123", source()).first); // [FALSE POSITIVE]
+ sink(make_pair("123", source()).second); // tainted
+
+ std::pair, char *> m;
+ m = make_pair(make_pair("123", source()), "789");
+ sink(m); // tainted [NOT DETECTED]
+ sink(m.first); // tainted [NOT DETECTED]
+ sink(m.first.first);
+ sink(m.first.second); // tainted [NOT DETECTED]
+ sink(m.second);
+}
+
+void test_map()
+{
+ // insert
+ std::map m1, m2, m3, m4, m5, m6;
+
+ sink(m1.insert(std::make_pair("abc", "def")).first);
+ sink(m2.insert(std::make_pair("abc", source())).first); // tainted
+ sink(m3.insert(std::make_pair(source(), "def")).first); // tainted [NOT DETECTED]
+ sink(m4.insert(m4.begin(), std::pair("abc", source()))); // tainted
+ sink(m5.insert_or_assign("abc", source()).first); // tainted
+ sink(m6.insert_or_assign(m6.begin(), "abc", source())); // tainted
+ sink(m1);
+ sink(m2); // tainted
+ sink(m3); // tainted [NOT DETECTED]
+ sink(m4); // tainted
+ sink(m5); // tainted
+ sink(m6); // tainted
+ sink(m1.find("abc"));
+ sink(m2.find("abc")); // tainted
+ sink(m3.find("abc"));
+ sink(m4.find("abc")); // tainted
+ sink(m5.find("abc")); // tainted
+ sink(m6.find("abc")); // tainted
+ sink(m1.find("def"));
+ sink(m2.find("def")); // [FALSE POSITIVE]
+ sink(m3.find("def"));
+ sink(m4.find("def")); // [FALSE POSITIVE]
+ sink(m5.find("def")); // [FALSE POSITIVE]
+ sink(m6.find("def")); // [FALSE POSITIVE]
+
+ // copy constructors and assignment
+ std::map m7(m2);
+ std::map m8 = m2;
+ std::map m9;
+ m9 = m2;
+ sink(m7); // tainted
+ sink(m8); // tainted
+ sink(m9); // tainted
+ sink(m7.find("abc")); // tainted
+ sink(m8.find("abc")); // tainted
+ sink(m9.find("abc")); // tainted
+
+ // iterators
+ std::map::iterator i1, i2, i3;
+ for (i1 = m1.begin(); i1 != m1.end(); i1++)
+ {
+ sink(*i1);
+ sink(i1->first);
+ sink(i1->second);
+ }
+ for (i2 = m2.begin(); i2 != m2.end(); i2++)
+ {
+ sink(*i2); // tainted
+ sink(i2->first); // [FALSE POSITIVE]
+ sink(i2->second); // tainted
+ }
+ for (i3 = m3.begin(); i3 != m3.end(); i3++)
+ {
+ sink(*i3); // tainted [NOT DETECTED]
+ sink(i2->first); // tainted
+ sink(i2->second); // [FALSE POSITIVE]
+ }
+
+ // array-like access
+ std::map m10, m11, m12, m13;
+ sink(m10["abc"] = "def");
+ sink(m11["abc"] = source()); // tainted
+ sink(m12.at("abc") = "def");
+ sink(m13.at("abc") = source()); // tainted
+ sink(m10["abc"]);
+ sink(m11["abc"]); // tainted
+ sink(m12["abc"]);
+ sink(m13["abc"]); // tainted
+
+ // ranges
+ std::map m14;
+ m14.insert(std::make_pair("a", "a"));
+ m14.insert(std::make_pair("b", source()));
+ m14.insert(std::make_pair("c", source()));
+ m14.insert(std::make_pair("d", "d"));
+ sink(m2.lower_bound("b")); // tainted
+ sink(m2.upper_bound("b")); // tainted
+ sink(m2.equal_range("b").first); // tainted
+ sink(m2.equal_range("b").second); // tainted
+ sink(m2.upper_bound("c")); // [FALSE POSITIVE]
+ sink(m2.equal_range("c").second); // [FALSE POSITIVE]
+
+ // swap
+ std::map m15, m16, m17, m18;
+ m15.insert(std::pair(source(), source()));
+ m18.insert(std::pair(source(), source()));
+ sink(m15); // tainted
+ sink(m16);
+ sink(m17);
+ sink(m18); // tainted
+ m15.swap(m16);
+ m17.swap(m18);
+ sink(m15); // [FALSE POSITIVE]
+ sink(m16); // tainted
+ sink(m17); // tainted
+ sink(m18); // [FALSE POSITIVE]
+
+ // merge
+ std::map m19, m20, m21, m22;
+ m19.insert(std::pair(source(), source()));
+ m20.insert(std::pair("abc", "def"));
+ m21.insert(std::pair("abc", "def"));
+ m22.insert(std::pair(source(), source()));
+ sink(m19); // tainted
+ sink(m20);
+ sink(m21);
+ sink(m22); // tainted
+ m19.merge(m20);
+ m21.merge(m22);
+ sink(m19); // tainted
+ sink(m20);
+ sink(m21); // tainted
+ sink(m22); // tainted
+
+ // erase, clear
+ std::map m23;
+ m23.insert(std::pair(source(), source()));
+ m23.insert(std::pair(source(), source()));
+ sink(m23); // tainted
+ sink(m23.erase(m23.begin())); // tainted
+ sink(m23); // tainted
+ m23.clear();
+ sink(m23); // [FALSE POSITIVE]
+
+ // emplace, emplace_hint
+ std::map m24, m25;
+ sink(m24.emplace("abc", "def").first);
+ sink(m24);
+ sink(m24.emplace("abc", source()).first); // tainted
+ sink(m24); // tainted
+ sink(m25.emplace_hint(m25.begin(), "abc", "def"));
+ sink(m25);
+ sink(m25.emplace_hint(m25.begin(), "abc", source())); // tainted
+ sink(m25); // tainted
+
+ // try_emplace
+ std::map m26, m27;
+ sink(m26.try_emplace("abc", "def").first);
+ sink(m26);
+ sink(m26.try_emplace("abc", source()).first); // tainted
+ sink(m26); // tainted
+ sink(m27.try_emplace(m27.begin(), "abc", "def"));
+ sink(m27);
+ sink(m27.try_emplace(m27.begin(), "abc", source())); // tainted
+ sink(m27); // tainted
+}
+
+void test_unordered_map()
+{
+ // insert
+ std::unordered_map m1, m2, m3, m4, m5, m6;
+
+ sink(m1.insert(std::make_pair("abc", "def")).first);
+ sink(m2.insert(std::make_pair("abc", source())).first); // tainted
+ sink(m3.insert(std::make_pair(source(), "def")).first); // tainted [NOT DETECTED]
+ sink(m4.insert(m4.begin(), std::pair("abc", source()))); // tainted
+ sink(m5.insert_or_assign("abc", source()).first); // tainted
+ sink(m6.insert_or_assign(m6.begin(), "abc", source())); // tainted
+ sink(m1);
+ sink(m2); // tainted
+ sink(m3); // tainted [NOT DETECTED]
+ sink(m4); // tainted
+ sink(m5); // tainted
+ sink(m6); // tainted
+ sink(m1.find("abc"));
+ sink(m2.find("abc")); // tainted
+ sink(m3.find("abc"));
+ sink(m4.find("abc")); // tainted
+ sink(m5.find("abc")); // tainted
+ sink(m6.find("abc")); // tainted
+ sink(m1.find("def"));
+ sink(m2.find("def")); // [FALSE POSITIVE]
+ sink(m3.find("def"));
+ sink(m4.find("def")); // [FALSE POSITIVE]
+ sink(m5.find("def")); // [FALSE POSITIVE]
+ sink(m6.find("def")); // [FALSE POSITIVE]
+
+ // copy constructors and assignment
+ std::unordered_map m7(m2);
+ std::unordered_map m8 = m2;
+ std::unordered_map m9;
+ m9 = m2;
+ sink(m7); // tainted
+ sink(m8); // tainted
+ sink(m9); // tainted
+ sink(m7.find("abc")); // tainted
+ sink(m8.find("abc")); // tainted
+ sink(m9.find("abc")); // tainted
+
+ // iterators
+ std::unordered_map::iterator i1, i2, i3;
+ for (i1 = m1.begin(); i1 != m1.end(); i1++)
+ {
+ sink(*i1);
+ sink(i1->first);
+ sink(i1->second);
+ }
+ for (i2 = m2.begin(); i2 != m2.end(); i2++)
+ {
+ sink(*i2); // tainted
+ sink(i2->first); // [FALSE POSITIVE]
+ sink(i2->second); // tainted
+ }
+ for (i3 = m3.begin(); i3 != m3.end(); i3++)
+ {
+ sink(*i3); // tainted [NOT DETECTED]
+ sink(i2->first); // tainted
+ sink(i2->second); // [FALSE POSITIVE]
+ }
+
+ // array-like access
+ std::unordered_map m10, m11, m12, m13;
+ sink(m10["abc"] = "def");
+ sink(m11["abc"] = source()); // tainted
+ sink(m12.at("abc") = "def");
+ sink(m13.at("abc") = source()); // tainted
+ sink(m10["abc"]);
+ sink(m11["abc"]); // tainted
+ sink(m12["abc"]);
+ sink(m13["abc"]); // tainted
+
+ // ranges
+ std::unordered_map m14;
+ m14.insert(std::make_pair("a", "a"));
+ m14.insert(std::make_pair("b", source()));
+ m14.insert(std::make_pair("c", source()));
+ m14.insert(std::make_pair("d", "d"));
+ sink(m2.equal_range("b").first); // tainted
+ sink(m2.equal_range("b").second); // tainted
+ sink(m2.equal_range("c").second); // [FALSE POSITIVE]
+
+ // swap
+ std::unordered_map m15, m16, m17, m18;
+ m15.insert(std::pair(source(), source()));
+ m18.insert(std::pair(source(), source()));
+ sink(m15); // tainted
+ sink(m16);
+ sink(m17);
+ sink(m18); // tainted
+ m15.swap(m16);
+ m17.swap(m18);
+ sink(m15); // [FALSE POSITIVE]
+ sink(m16); // tainted
+ sink(m17); // tainted
+ sink(m18); // [FALSE POSITIVE]
+
+ // merge
+ std::unordered_map m19, m20, m21, m22;
+ m19.insert(std::pair(source(), source()));
+ m20.insert(std::pair("abc", "def"));
+ m21.insert(std::pair("abc", "def"));
+ m22.insert(std::pair(source(), source()));
+ sink(m19); // tainted
+ sink(m20);
+ sink(m21);
+ sink(m22); // tainted
+ m19.merge(m20);
+ m21.merge(m22);
+ sink(m19); // tainted
+ sink(m20);
+ sink(m21); // tainted
+ sink(m22); // tainted
+
+ // erase, clear
+ std::unordered_map m23;
+ m23.insert(std::pair(source(), source()));
+ m23.insert(std::pair(source(), source()));
+ sink(m23); // tainted
+ sink(m23.erase(m23.begin())); // tainted
+ sink(m23); // tainted
+ m23.clear();
+ sink(m23); // [FALSE POSITIVE]
+
+ // emplace, emplace_hint
+ std::unordered_map m24, m25;
+ sink(m24.emplace("abc", "def").first);
+ sink(m24);
+ sink(m24.emplace("abc", source()).first); // tainted
+ sink(m24); // tainted
+ sink(m25.emplace_hint(m25.begin(), "abc", "def"));
+ sink(m25);
+ sink(m25.emplace_hint(m25.begin(), "abc", source())); // tainted
+ sink(m25); // tainted
+
+ // try_emplace
+ std::unordered_map m26, m27, m28;
+ sink(m26.try_emplace("abc", "def").first);
+ sink(m26.try_emplace("abc", "def").second);
+ sink(m26);
+ sink(m26.try_emplace("abc", source()).first); // tainted
+ sink(m26.try_emplace("abc", source()).second); // [FALSE POSITIVE]
+ sink(m26); // tainted
+ sink(m27.try_emplace(m27.begin(), "abc", "def"));
+ sink(m27);
+ sink(m27.try_emplace(m27.begin(), "abc", source())); // tainted
+ sink(m27); // tainted
+ sink(m28.try_emplace(m28.begin(), "abc", "def"));
+ sink(m28);
+ sink(m28.try_emplace(m28.begin(), source(), "def")); // tainted [NOT DETECTED]
+ sink(m28); // tainted [NOT DETECTED]
+
+ // additional try_emplace test cases
+ std::unordered_map> m29, m30, m31, m32;
+ sink(m29.try_emplace("abc", 1, 2));
+ sink(m29);
+ sink(m29["abc"]);
+ sink(m30.try_emplace(source(), 1, 2)); // tainted [NOT DETECTED]
+ sink(m30); // tainted [NOT DETECTED]
+ sink(m30["abc"]);
+ sink(m31.try_emplace("abc", source(), 2)); // tainted
+ sink(m31); // tainted
+ sink(m31["abc"]); // tainted
+ sink(m32.try_emplace("abc", 1, source())); // tainted
+ sink(m32); // tainted
+ sink(m32["abc"]); // tainted
+
+ // additional emplace test cases
+ std::unordered_map m33;
+ sink(m33.emplace(source(), "def").first); // tainted [NOT DETECTED]
+ sink(m33); // tainted [NOT DETECTED]
+
+ std::unordered_map m34, m35;
+ sink(m34.emplace(std::pair("abc", "def")).first);
+ sink(m34);
+ sink(m34.emplace(std::pair("abc", source())).first); // tainted
+ sink(m34); // tainted
+ sink(m34.emplace_hint(m34.begin(), "abc", "def")); // tainted
+ sink(m35.emplace().first);
+ sink(m35);
+ sink(m35.emplace(std::pair(source(), "def")).first); // tainted [NOT DETECTED]
+ sink(m35); // tainted [NOT DETECTED]
+}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp
new file mode 100644
index 000000000000..c0003388307a
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/movableclass.cpp
@@ -0,0 +1,67 @@
+
+int source();
+void sink(...) {};
+
+class MyMovableClass {
+public:
+ MyMovableClass() {} // Constructor
+ MyMovableClass(int _v) : v(_v) {} // ConversionConstructor
+ MyMovableClass(MyMovableClass &&other) noexcept { // ConversionConstructor, MoveConstructor
+ v = other.v;
+ other.v = 0;
+ }
+ MyMovableClass &operator=(MyMovableClass &&other) noexcept { // MoveAssignmentOperator
+ v = other.v;
+ other.v = 0;
+ return *this;
+ }
+
+ int v;
+};
+
+MyMovableClass &&getUnTainted() { return MyMovableClass(1); }
+MyMovableClass &&getTainted() { return MyMovableClass(source()); }
+
+void test_copyableclass()
+{
+ {
+ MyMovableClass s1(1);
+ MyMovableClass s2 = 1;
+ MyMovableClass s3;
+ s3 = 1;
+
+ sink(s1);
+ sink(s2);
+ sink(s3);
+ }
+
+ {
+ MyMovableClass s1(source());
+ MyMovableClass s2 = source();
+ MyMovableClass s3;
+ s3 = source();
+
+ sink(s1); // tainted
+ sink(s2); // tainted
+ sink(s3); // tainted
+ }
+
+ {
+ MyMovableClass s1 = MyMovableClass(source());
+ MyMovableClass s2;
+ s2 = MyMovableClass(source());
+
+ sink(s1); // tainted
+ sink(s2); // tainted
+ }
+
+ {
+ MyMovableClass s1(getUnTainted());
+ MyMovableClass s2(getTainted());
+ MyMovableClass s3;
+
+ sink(s1);
+ sink(s2); // tainted
+ sink(s3 = source()); // tainted
+ }
+}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/set.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/set.cpp
new file mode 100644
index 000000000000..bcc764e35730
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/set.cpp
@@ -0,0 +1,238 @@
+
+#include "stl.h"
+
+using namespace std;
+
+char *source();
+
+void sink(char *);
+void sink(std::set);
+void sink(std::set::iterator);
+void sink(std::unordered_set);
+void sink(std::unordered_set::iterator);
+
+void test_set()
+{
+ // insert, find
+ std::set s1, s2, s3, s4, s5, s6;
+
+ sink(s1.insert("abc").first);
+ sink(s2.insert(source()).first); // tainted
+ sink(s3.insert(s3.begin(), "abc"));
+ sink(s4.insert(s4.begin(), source())); // tainted
+ s5.insert(s1.begin(), s1.end());
+ s6.insert(s2.begin(), s2.end());
+ sink(s1);
+ sink(s2); // tainted
+ sink(s3);
+ sink(s4); // tainted
+ sink(s5);
+ sink(s6); // tainted
+ sink(s1.find("abc"));
+ sink(s2.find("abc")); // tainted
+ sink(s3.find("abc"));
+ sink(s4.find("abc")); // tainted
+ sink(s5.find("abc"));
+ sink(s6.find("abc")); // tainted
+
+ // copy constructors and assignment
+ std::set s7(s2);
+ std::set s8 = s2;
+ std::set s9(s2.begin(), s2.end());
+ std::set s10;
+ s10 = s2;
+ sink(s7); // tainted
+ sink(s8); // tainted
+ sink(s9); // tainted
+ sink(s10); // tainted
+ sink(s7.find("abc")); // tainted
+ sink(s8.find("abc")); // tainted
+ sink(s9.find("abc")); // tainted
+ sink(s10.find("abc")); // tainted
+
+ // iterators
+ std::set::iterator i1, i2;
+ for (i1 = s1.begin(); i1 != s1.end(); i1++)
+ {
+ sink(*i1);
+ }
+ for (i2 = s2.begin(); i2 != s2.end(); i2++)
+ {
+ sink(*i2); // tainted
+ }
+
+ // ranges
+ std::set s11;
+ s11.insert("a");
+ s11.insert(source());
+ s11.insert("c");
+ sink(s11.lower_bound("b")); // tainted
+ sink(s11.upper_bound("b")); // tainted
+ sink(s11.equal_range("b").first); // tainted
+ sink(s11.equal_range("b").second); // tainted
+
+ // swap
+ std::set s12, s13, s14, s15;
+ s12.insert(source());
+ s15.insert(source());
+ sink(s12); // tainted
+ sink(s13);
+ sink(s14);
+ sink(s15); // tainted
+ s12.swap(s13);
+ s14.swap(s15);
+ sink(s12); // [FALSE POSITIVE]
+ sink(s13); // tainted
+ sink(s14); // tainted
+ sink(s15); // [FALSE POSITIVE]
+
+ // merge
+ std::set s16, s17, s18, s19;
+ s16.insert(source());
+ s17.insert("abc");
+ s18.insert("def");
+ s19.insert(source());
+ sink(s16); // tainted
+ sink(s17);
+ sink(s18);
+ sink(s19); // tainted
+ s16.merge(s17);
+ s18.merge(s19);
+ sink(s16); // tainted
+ sink(s17);
+ sink(s18); // tainted
+ sink(s19); // tainted
+
+ // erase, clear
+ std::set s20;
+ s20.insert(source());
+ s20.insert(source());
+ sink(s20); // tainted
+ sink(s20.erase(s20.begin())); // tainted
+ sink(s20); // tainted
+ s20.clear();
+ sink(s20); // [FALSE POSITIVE]
+
+ // emplace, emplace_hint
+ std::set s21, s22;
+ sink(s21.emplace("abc").first);
+ sink(s21);
+ sink(s21.emplace(source()).first); // tainted
+ sink(s21); // tainted
+ sink(s22.emplace_hint(s22.begin(), "abc"));
+ sink(s22);
+ sink(s22.emplace_hint(s22.begin(), source())); // tainted
+ sink(s22); // tainted
+}
+
+void test_unordered_set()
+{
+ // insert, find
+ std::unordered_set s1, s2, s3, s4, s5, s6;
+
+ sink(s1.insert("abc").first);
+ sink(s2.insert(source()).first); // tainted
+ sink(s3.insert(s3.begin(), "abc"));
+ sink(s4.insert(s4.begin(), source())); // tainted
+ s5.insert(s1.begin(), s1.end());
+ s6.insert(s2.begin(), s2.end());
+ sink(s1);
+ sink(s2); // tainted
+ sink(s3);
+ sink(s4); // tainted
+ sink(s5);
+ sink(s6); // tainted
+ sink(s1.find("abc"));
+ sink(s2.find("abc")); // tainted
+ sink(s3.find("abc"));
+ sink(s4.find("abc")); // tainted
+ sink(s5.find("abc"));
+ sink(s6.find("abc")); // tainted
+
+ // copy constructors and assignment
+ std::unordered_set s7(s2);
+ std::unordered_set s8 = s2;
+ std::unordered_set s9(s2.begin(), s2.end());
+ std::unordered_set s10;
+ s10 = s2;
+ sink(s7); // tainted
+ sink(s8); // tainted
+ sink(s9); // tainted
+ sink(s10); // tainted
+ sink(s7.find("abc")); // tainted
+ sink(s8.find("abc")); // tainted
+ sink(s9.find("abc")); // tainted
+ sink(s10.find("abc")); // tainted
+
+ // iterators
+ std::unordered_set::iterator i1, i2;
+ for (i1 = s1.begin(); i1 != s1.end(); i1++)
+ {
+ sink(*i1);
+ }
+ for (i2 = s2.begin(); i2 != s2.end(); i2++)
+ {
+ sink(*i2); // tainted
+ }
+
+ // ranges
+ std::unordered_set s11;
+ s11.insert("a");
+ s11.insert(source());
+ s11.insert("c");
+ sink(s11.equal_range("b").first); // tainted
+ sink(s11.equal_range("b").second); // tainted
+
+ // swap
+ std::unordered_set s12, s13, s14, s15;
+ s12.insert(source());
+ s15.insert(source());
+ sink(s12); // tainted
+ sink(s13);
+ sink(s14);
+ sink(s15); // tainted
+ s12.swap(s13);
+ s14.swap(s15);
+ sink(s12); // [FALSE POSITIVE]
+ sink(s13); // tainted
+ sink(s14); // tainted
+ sink(s15); // [FALSE POSITIVE]
+
+ // merge
+ std::unordered_set s16, s17, s18, s19;
+ s16.insert(source());
+ s17.insert("abc");
+ s18.insert("def");
+ s19.insert(source());
+ sink(s16); // tainted
+ sink(s17);
+ sink(s18);
+ sink(s19); // tainted
+ s16.merge(s17);
+ s18.merge(s19);
+ sink(s16); // tainted
+ sink(s17);
+ sink(s18); // tainted
+ sink(s19); // tainted
+
+ // erase, clear
+ std::unordered_set s20;
+ s20.insert(source());
+ s20.insert(source());
+ sink(s20); // tainted
+ sink(s20.erase(s20.begin())); // tainted
+ sink(s20); // tainted
+ s20.clear();
+ sink(s20); // [FALSE POSITIVE]
+
+ // emplace, emplace_hint
+ std::unordered_set s21, s22;
+ sink(s21.emplace("abc").first);
+ sink(s21);
+ sink(s21.emplace(source()).first); // tainted
+ sink(s21); // tainted
+ sink(s22.emplace_hint(s22.begin(), "abc"));
+ sink(s22);
+ sink(s22.emplace_hint(s22.begin(), source())); // tainted
+ sink(s22); // tainted
+}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp
new file mode 100644
index 000000000000..e6a5a1a0847b
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/smart_pointer.cpp
@@ -0,0 +1,68 @@
+#include "stl.h"
+
+int source();
+void sink(int);
+void sink(int*);
+
+template void sink(std::shared_ptr&);
+template void sink(std::unique_ptr&);
+
+void test_make_shared() {
+ std::shared_ptr p = std::make_shared(source());
+ sink(*p); // tainted
+ sink(p); // tainted
+}
+
+void test_make_shared_array() {
+ std::shared_ptr p = std::make_shared(source());
+ sink(*p); // not tainted
+ sink(p); // not tainted
+}
+
+void test_make_unique() {
+ std::unique_ptr p = std::make_unique(source());
+ sink(*p); // tainted
+ sink(p); // tainted
+}
+
+void test_make_unique_array() {
+ std::unique_ptr p = std::make_unique(source());
+ sink(*p); // not tainted
+ sink(p); // not tainted
+}
+
+void test_reverse_taint_shared() {
+ std::shared_ptr p = std::make_shared();
+
+ *p = source();
+ sink(p); // tainted [NOT DETECTED]
+ sink(*p); // tainted [NOT DETECTED]
+}
+
+void test_reverse_taint_unique() {
+ std::unique_ptr p = std::unique_ptr();
+
+ *p = source();
+ sink(p); // tainted [NOT DETECTED]
+ sink(*p); // tainted [NOT DETECTED]
+}
+
+void test_shared_get() {
+ std::shared_ptr p = std::make_shared(source());
+ sink(p.get()); // tainted
+}
+
+void test_unique_get() {
+ std::unique_ptr p = std::make_unique(source());
+ sink(p.get()); // tainted
+}
+
+struct A {
+ int x, y;
+};
+
+void test_shared_field_member() {
+ std::unique_ptr p = std::make_unique(source(), 0);
+ sink(p->x); // tainted [NOT DETECTED]
+ sink(p->y); // not tainted
+}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/standalone_iterators.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/standalone_iterators.cpp
new file mode 100644
index 000000000000..60c36412a910
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/standalone_iterators.cpp
@@ -0,0 +1,55 @@
+#include "stl.h"
+
+using namespace std;
+
+void sink(int);
+
+class int_iterator_by_typedefs {
+public:
+ typedef input_iterator_tag iterator_category;
+ typedef int value_type;
+ typedef size_t difference_type;
+ typedef int * pointer;
+ typedef int & reference;
+
+ int_iterator_by_typedefs &operator++();
+ int_iterator_by_typedefs operator++(int);
+ int operator*() const;
+};
+
+class int_iterator_by_trait {
+public:
+ int_iterator_by_trait &operator++();
+ int_iterator_by_trait operator++(int);
+ int operator*() const;
+};
+
+template<>
+struct std::iterator_traits {
+ typedef input_iterator_tag iterator_category;
+};
+
+class non_iterator {
+public:
+ non_iterator &operator++();
+ non_iterator operator++(int);
+ int operator*() const;
+};
+
+void test_typedefs(int_iterator_by_typedefs source1) {
+ sink(*source1); // tainted
+ sink(*(source1++)); // tainted
+ sink(*(++source1)); // tainted
+}
+
+void test_trait(int_iterator_by_trait source1) {
+ sink(*source1); // tainted
+ sink(*(source1++)); // tainted
+ sink(*(++source1)); // tainted
+}
+
+void test_non_iterator(non_iterator source1) {
+ sink(*source1);
+ sink(*(source1++));
+ sink(*(++source1));
+}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h
new file mode 100644
index 000000000000..d767218c1007
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stl.h
@@ -0,0 +1,589 @@
+
+typedef unsigned long size_t;
+
+template
+struct remove_const { typedef T type; };
+
+template
+struct remove_const { typedef T type; };
+
+// `remove_const_t` removes any `const` specifier from `T`
+template
+using remove_const_t = typename remove_const::type;
+
+template
+struct remove_reference { typedef T type; };
+
+template
+struct remove_reference { typedef T type; };
+
+template
+struct remove_reference { typedef T type; };
+
+// `remove_reference_t` removes any `&` from `T`
+template
+using remove_reference_t = typename remove_reference::type;
+
+namespace std
+{
+ template constexpr T&& forward(remove_reference_t& t) noexcept;
+ template constexpr T&& forward(remove_reference_t&& t) noexcept;
+}
+
+// --- iterator ---
+
+namespace std {
+ struct ptrdiff_t;
+
+ template struct iterator_traits;
+
+ template
+ struct iterator {
+ typedef Category iterator_category;
+
+ iterator();
+ iterator(iterator > const &other); // non-const -> const conversion constructor
+
+ iterator &operator++();
+ iterator operator++(int);
+ iterator &operator--();
+ iterator operator--(int);
+ bool operator==(iterator other) const;
+ bool operator!=(iterator other) const;
+ reference_type operator*() const;
+ pointer_type operator->() const;
+ iterator operator+(int);
+ iterator operator-(int);
+ iterator &operator+=(int);
+ iterator &operator-=(int);
+ int operator-(iterator);
+ reference_type operator[](int);
+ };
+
+ struct input_iterator_tag {};
+ struct forward_iterator_tag : public input_iterator_tag {};
+ struct bidirectional_iterator_tag : public forward_iterator_tag {};
+ struct random_access_iterator_tag : public bidirectional_iterator_tag {};
+}
+
+// --- string ---
+
+namespace std
+{
+ template struct char_traits;
+
+ typedef size_t streamsize;
+
+ template class allocator {
+ public:
+ allocator() throw();
+ typedef size_t size_type;
+ };
+
+ template, class Allocator = allocator >
+ class basic_string {
+ public:
+ using value_type = charT;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ typedef typename Allocator::size_type size_type;
+ static const size_type npos = -1;
+
+ explicit basic_string(const Allocator& a = Allocator());
+ basic_string(const charT* s, const Allocator& a = Allocator());
+ template basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator());
+
+ const charT* c_str() const;
+ charT* data() noexcept;
+ size_t length() const;
+
+ typedef std::iterator iterator;
+ typedef std::iterator const_iterator;
+
+ iterator begin();
+ iterator end();
+ const_iterator begin() const;
+ const_iterator end() const;
+ const_iterator cbegin() const;
+ const_iterator cend() const;
+
+ void push_back(charT c);
+
+ const charT& front() const;
+ charT& front();
+ const charT& back() const;
+ charT& back();
+
+ const_reference operator[](size_type pos) const;
+ reference operator[](size_type pos);
+ const_reference at(size_type n) const;
+ reference at(size_type n);
+ template basic_string& operator+=(const T& t);
+ basic_string& operator+=(const charT* s);
+ basic_string& append(const basic_string& str);
+ basic_string& append(const charT* s);
+ basic_string& append(size_type n, charT c);
+ template basic_string& append(InputIterator first, InputIterator last);
+ basic_string& assign(const basic_string& str);
+ basic_string& assign(size_type n, charT c);
+ template basic_string& assign(InputIterator first, InputIterator last);
+ basic_string& insert(size_type pos, const basic_string& str);
+ basic_string& insert(size_type pos, size_type n, charT c);
+ basic_string& insert(size_type pos, const charT* s);
+ iterator insert(const_iterator p, size_type n, charT c);
+ template iterator insert(const_iterator p, InputIterator first, InputIterator last);
+ basic_string& replace(size_type pos1, size_type n1, const basic_string& str);
+ basic_string& replace(size_type pos1, size_type n1, size_type n2, charT c);
+ size_type copy(charT* s, size_type n, size_type pos = 0) const;
+ void clear() noexcept;
+ basic_string substr(size_type pos = 0, size_type n = npos) const;
+ void swap(basic_string& s) noexcept/*(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value)*/;
+ };
+
+ template basic_string operator+(const basic_string& lhs, const basic_string& rhs);
+ template basic_string operator+(const basic_string& lhs, const charT* rhs);
+
+ typedef basic_string string;
+}
+
+// --- istring / ostream / stringstream ---
+
+namespace std
+{
+ template >
+ class basic_istream /*: virtual public basic_ios - not needed for this test */ {
+ public:
+ using char_type = charT;
+ using int_type = int; //typename traits::int_type;
+
+ basic_istream& operator>>(int& n);
+
+ int_type get();
+ basic_istream& get(char_type& c);
+ basic_istream& get(char_type* s, streamsize n);
+ int_type peek();
+ basic_istream& read (char_type* s, streamsize n);
+ streamsize readsome(char_type* s, streamsize n);
+ basic_istream& putback(char_type c);
+ basic_istream& unget();
+
+ basic_istream& getline(char_type* s, streamsize n);
+ basic_istream& getline(char_type* s, streamsize n, char_type delim);
+ };
+
+ template