diff --git a/.codeqlmanifest.json b/.codeqlmanifest.json
new file mode 100644
index 00000000000..421284bc4b9
--- /dev/null
+++ b/.codeqlmanifest.json
@@ -0,0 +1,6 @@
+{ "provide": [ "*/ql/src/qlpack.yml",
+ "*/ql/test/qlpack.yml",
+ "*/upgrades/qlpack.yml",
+ "misc/legacy-support/*/qlpack.yml",
+ "misc/suite-helpers/qlpack.yml",
+ "codeql/.codeqlmanifest.json" ] }
diff --git a/.github/ISSUE_TEMPLATE/ql---general.md b/.github/ISSUE_TEMPLATE/ql---general.md
new file mode 100644
index 00000000000..1d5b7d244f6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/ql---general.md
@@ -0,0 +1,14 @@
+---
+name: General issue
+about: Tell us if you think something is wrong or if you have a question
+title: General issue
+labels: question
+assignees: ''
+
+---
+
+**Description of the issue**
+
+
+
diff --git a/.gitignore b/.gitignore
index b5c88ce04ab..459fe556c7f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,6 @@
# Visual studio temporaries, except a file used by QL4VS
.vs/*
!.vs/VSWorkspaceSettings.json
+
+# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
+/codeql/
diff --git a/CODEOWNERS b/CODEOWNERS
index 45bb2c05818..8d4e4fd9b06 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1,10 +1,11 @@
+/cpp/ @Semmle/cpp-analysis
/csharp/ @Semmle/cs
/java/ @Semmle/java
/javascript/ @Semmle/js
-/cpp/ @Semmle/cpp-analysis
-/cpp/**/*.qhelp @semmledocs-ac
+/python/ @Semmle/python
+/cpp/**/*.qhelp @hubwriter
/csharp/**/*.qhelp @jf205
-/java/**/*.qhelp @felicity-semmle
-/javascript/**/*.qhelp @mc-semmle
-/python/**/*.qhelp @felicity-semmle
-/docs/language/ @felicity-semmle @jf205
+/java/**/*.qhelp @felicitymay
+/javascript/**/*.qhelp @mchammer01
+/python/**/*.qhelp @felicitymay
+/docs/language/ @shati-patel @jf205
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6c0714571fc..e074b1c2079 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,4 +1,4 @@
-# Contributing to QL
+# Contributing to CodeQL
We welcome contributions to our standard library and standard checks. Got an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request!
@@ -9,13 +9,13 @@ Before we accept your pull request, we require that you have agreed to our Contr
If you have an idea for a query that you would like to share with other Semmle users, please open a pull request to add it to this repository.
Follow the steps below to help other users understand what your query does, and to ensure that your query is consistent with the other Semmle queries.
-1. **Consult the QL documentation for query writers**
+1. **Consult the documentation for query writers**
- There is lots of useful documentation to help you write QL, ranging from information about query file structure to language-specific tutorials. For more information on the documentation available, see [Writing QL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
+ There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [Writing CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
-2. **Format your QL correctly**
+2. **Format your code correctly**
- All of Semmle's standard QL queries and libraries are uniformly formatted for clarity and consistency, so we strongly recommend that all QL contributions follow the same formatting guidelines. If you use QL for Eclipse, you can auto-format your query in the [QL editor](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/ql-editor.html). For more information, see the [QL style guide](https://github.com/Semmle/ql/blob/master/docs/ql-style-guide.md).
+ All of Semmle's standard queries and libraries are uniformly formatted for clarity and consistency, so we strongly recommend that all contributions follow the same formatting guidelines. If you use QL for Eclipse, you can auto-format your query in the [QL editor](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/ql-editor.html). For more information, see the [CodeQL style guide](https://github.com/Semmle/ql/blob/master/docs/ql-style-guide.md).
3. **Make sure your query has the correct metadata**
@@ -29,7 +29,7 @@ Follow the steps below to help other users understand what your query does, and
The `select` statement of your query must be compatible with the query type (determined by the `@kind` metadata property) for alert or path results to be displayed correctly in LGTM and QL for Eclipse.
For more information on `select` statement format, see [Introduction to query files](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
-5. **Save your query in a `.ql` file in correct language directory in this repository**
+5. **Save your query in a `.ql` file in the correct language directory in this repository**
There are five language-specific directories in this repository:
@@ -54,7 +54,7 @@ repositories, which might be made 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 QL product.
+on the basis of our legitimate interest in creating the CodeQL product.
Please do get in touch (privacy@semmle.com) if you have any questions about
this or our data protection policies.
diff --git a/README.md b/README.md
index b483d29cae2..c7cafeea95b 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,16 @@
-# Semmle QL
+# CodeQL
-This open source repository contains the standard QL libraries and queries that power [LGTM](https://lgtm.com), and the other products that [Semmle](https://semmle.com) makes available to its customers worldwide.
+This open source repository contains the standard CodeQL libraries and queries that power [LGTM](https://lgtm.com), and the other products that [Semmle](https://semmle.com) makes available to its customers worldwide.
-## How do I learn QL and run queries?
+## How do I learn CodeQL and run queries?
-There is [extensive documentation](https://help.semmle.com/QL/learn-ql/) on getting started with writing QL.
-You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [QL for Eclipse](https://lgtm.com/help/lgtm/running-queries-ide) plugin to try out your queries on any open-source project that's currently being analyzed.
+There is [extensive documentation](https://help.semmle.com/QL/learn-ql/) on getting started with writing CodeQL.
+You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode.html) extension to try out your queries on any open source project that's currently being analyzed.
## 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/Semmle/ql/tree/master/docs) to learn how to format your QL 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/Semmle/ql/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.
## License
-The QL queries in this repository are licensed under [Apache License 2.0](LICENSE) by [Semmle](https://semmle.com).
+The code in this repository is licensed under [Apache License 2.0](LICENSE) by [Semmle](https://semmle.com).
diff --git a/change-notes/1.22/analysis-javascript.md b/change-notes/1.22/analysis-javascript.md
index fd211197157..fd09522fc25 100644
--- a/change-notes/1.22/analysis-javascript.md
+++ b/change-notes/1.22/analysis-javascript.md
@@ -36,6 +36,7 @@
| Shift out of range (`js/shift-out-of-range`| Fewer false positive results | This rule now correctly handles BigInt shift operands. |
| Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer false-positive results. | This rule no longer flags calls to placeholder functions that trivially throw an exception. |
| Undocumented parameter (`js/jsdoc/missing-parameter`) | No changes to results | This rule is now run on LGTM, although its results are still not shown by default. |
+| Missing space in string concatenation (`js/missing-space-in-concatenation`) | Fewer false positive results | The rule now requires a word-like part exists in the string concatenation. |
## Changes to QL libraries
diff --git a/change-notes/1.23/analysis-cpp.md b/change-notes/1.23/analysis-cpp.md
index ce317236f93..4c7b1d0a26c 100644
--- a/change-notes/1.23/analysis-cpp.md
+++ b/change-notes/1.23/analysis-cpp.md
@@ -9,6 +9,8 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | reliability, japanese-era | This query is a combination of two old queries that were identical in purpose but separate as an implementation detail. This new query replaces Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) and Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`). |
+| Signed overflow check (`cpp/signed-overflow-check`) | correctness, security | Finds overflow checks that rely on signed integer addition to overflow, which has undefined behavior. Example: `a + b < a`. |
+| Pointer overflow check (`cpp/pointer-overflow-check`) | correctness, security | Finds overflow checks that rely on pointer addition to overflow, which has undefined behavior. Example: `ptr + a < ptr`. |
## Changes to existing queries
@@ -18,8 +20,15 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
| Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
| Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | More correct results | This query now checks for the beginning date of the Reiwa era (1st May 2019). |
+| Sign check of bitwise operation (`cpp/bitwise-sign-check`) | Fewer false positive results | Results involving `>=` or `<=` are no longer reported. |
+| Too few arguments to formatting function (`cpp/wrong-number-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
+| Too many arguments to formatting function (`cpp/too-many-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
+| Unclear comparison precedence (`cpp/comparison-precedence`) | Fewer false positive results | False positives involving template classes and functions have been fixed. |
+| Comparison of narrow type with wide type in loop condition (`cpp/comparison-with-wider-type`) | Higher precision | The precision of this query has been increased to "high" as the alerts from this query have proved to be valuable on real-world projects. With this precision, results are now displayed by default in LGTM. |
+| Non-constant format string (`cpp/non-constant-format`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. |
+| Wrong type of arguments to formatting function (`cpp/wrong-type-format-argument`) | More correct results and fewer false positive results | This query now understands explicitly specified argument numbers in format strings, such as the `1$` in `%1$s`. |
-## Changes to QL libraries
+## Changes to libraries
* The data-flow library has been extended with a new feature to aid debugging.
Instead of specifying `isSink(Node n) { any() }` on a configuration to
@@ -28,10 +37,31 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
picture of the partial flow paths from a given source. The feature is
disabled by default and can be enabled for individual configurations by
overriding `int explorationLimit()`.
+* The data-flow library now supports flow out of C++ reference parameters.
+* The data-flow library now allows flow through the address-of operator (`&`).
* The `DataFlow::DefinitionByReferenceNode` class now considers `f(x)` to be a
definition of `x` when `x` is a variable of pointer type. It no longer
considers deep paths such as `f(&x.myField)` to be definitions of `x`. These
changes are in line with the user expectations we've observed.
+* The data-flow library now makes it easier to specify barriers/sanitizers
+ arising from guards by overriding the predicate
+ `isBarrierGuard`/`isSanitizerGuard` on data-flow and taint-tracking
+ configurations respectively.
* There is now a `DataFlow::localExprFlow` predicate and a
`TaintTracking::localExprTaint` predicate to make it easy to use the most
common case of local data flow and taint: from one `Expr` to another.
+* The member predicates of the `FunctionInput` and `FunctionOutput` classes have been renamed for
+ clarity (e.g. `isOutReturnPointer()` to `isReturnValueDeref()`). The existing member predicates
+ have been deprecated, and will be removed in a future release. Code that uses the old member
+ predicates should be updated to use the corresponding new member predicate.
+* The predicates `Declaration.hasStdName()` and `Declaration.hasGlobalOrStdName`
+ have been added, simplifying handling of C++ standard library functions.
+* The control-flow graph is now computed in QL, not in the extractor. This can
+ lead to regressions (or improvements) in how queries are optimized because
+ optimization in QL relies on static size estimates, and the control-flow edge
+ relations will now have different size estimates than before.
+* Support has been added for non-type template arguments. This means that the
+ return type of `Declaration::getTemplateArgument()` and
+ `Declaration::getATemplateArgument` have changed to `Locatable`. See the
+ documentation for `Declaration::getTemplateArgument()` and
+ `Declaration::getTemplateArgumentKind()` for details.
diff --git a/change-notes/1.23/analysis-csharp.md b/change-notes/1.23/analysis-csharp.md
index d22f7ea567a..c5bdae38dcd 100644
--- a/change-notes/1.23/analysis-csharp.md
+++ b/change-notes/1.23/analysis-csharp.md
@@ -8,13 +8,18 @@ The following changes in version 1.23 affect C# analysis in all applications.
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
+| Deserialized delegate (`cs/deserialized-delegate`) | security, external/cwe/cwe-502 | Finds unsafe deserialization of delegate types. |
+| Deserialization of untrusted data (`cs/unsafe-deserialization-untrusted-input`) | security, external/cwe/cwe-502 | Finds flow of untrusted input to calls to unsafe deserializers. |
| Unsafe year argument for 'DateTime' constructor (`cs/unsafe-year-construction`) | reliability, date-time | Finds incorrect manipulation of `DateTime` values, which could lead to invalid dates. |
+| Unsafe deserializer (`cs/unsafe-deserialization`) | security, external/cwe/cwe-502 | Finds calls to unsafe deserializers. |
| Mishandling the Japanese era start date (`cs/mishandling-japanese-era`) | reliability, date-time | Finds hard-coded Japanese era start dates that could be invalid. |
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
+| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | Fewer false positive results | More `null` checks are now taken into account, including `null` checks for `dynamic` expressions and `null` checks such as `object alwaysNull = null; if (x != alwaysNull) ...`. |
+| Missing Dispose call on local IDisposable (`cs/local-not-disposed`) | Fewer false positive results | The query has been rewritten in order to identify more dispose patterns. For example, a local `IDisposable` that is disposed of by passing through a fluent API is no longer reported. |
## Removal of old queries
@@ -22,7 +27,7 @@ The following changes in version 1.23 affect C# analysis in all applications.
* `nameof` expressions are now extracted correctly when the name is a namespace.
-## Changes to QL libraries
+## Changes to libraries
* The new class `NamespaceAccess` models accesses to namespaces, for example in `nameof` expressions.
* The data-flow library now makes it easier to specify barriers/sanitizers
@@ -37,5 +42,11 @@ The following changes in version 1.23 affect C# analysis in all applications.
disabled by default and can be enabled for individual configurations by
overriding `int explorationLimit()`.
* `foreach` statements where the body is guaranteed to be executed at least once, such as `foreach (var x in new string[]{ "a", "b", "c" }) { ... }`, are now recognized by all analyses based on the control flow graph (such as SSA, data flow and taint tracking).
+* Fixed the control flow graph for `switch` statements where the `default` case was not the last case. This had caused the remaining cases to be unreachable. `SwitchStmt.getCase(int i)` now puts the `default` case last.
+* There is now a `DataFlow::localExprFlow` predicate and a
+ `TaintTracking::localExprTaint` predicate to make it easy to use the most
+ common case of local data flow and taint: from one `Expr` to another.
+* Data is now tracked through null-coalescing expressions (`??`).
+* A new library `semmle.code.csharp.Unification` has been added. This library exposes two predicates `unifiable` and `subsumes` for calculating type unification and type subsumption, respectively.
## Changes to autobuilder
diff --git a/change-notes/1.23/analysis-java.md b/change-notes/1.23/analysis-java.md
index 76085832c01..8c38f57e9d2 100644
--- a/change-notes/1.23/analysis-java.md
+++ b/change-notes/1.23/analysis-java.md
@@ -2,15 +2,24 @@
The following changes in version 1.23 affect Java analysis in all applications.
+## New queries
+
+| **Query** | **Tags** | **Purpose** |
+|-----------------------------|-----------|--------------------------------------------------------------------|
+| Continue statement that does not continue (`java/continue-in-false-loop`) | correctness | Finds `continue` statements in `do { ... } while (false)` loops. |
+
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
+| Dereferenced variable may be null (`java/dereferenced-value-may-be-null`) | Fewer false positives | Certain indirect null guards involving two auxiliary variables known to be equal can now be detected. |
+| Non-synchronized override of synchronized method (`java/non-sync-override`) | Fewer false positives | Results are now only reported if the immediately overridden method is synchronized. |
| Query built from user-controlled sources (`java/sql-injection`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
| Query built from local-user-controlled sources (`java/sql-injection-local`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
| Query built without neutralizing special characters (`java/concatenated-sql-query`) | More results | The query now identifies arguments to `Statement.executeLargeUpdate` and `Connection.prepareCall` as SQL expressions sinks. |
+| Useless comparison test (`java/constant-comparison`) | Fewer false positives | Additional overflow check patterns are now recognized and no longer reported. |
-## Changes to QL libraries
+## Changes to libraries
* The data-flow library has been extended with a new feature to aid debugging.
Instead of specifying `isSink(Node n) { any() }` on a configuration to
diff --git a/change-notes/1.23/analysis-javascript.md b/change-notes/1.23/analysis-javascript.md
index 3b570c12270..e815f5e8ba0 100644
--- a/change-notes/1.23/analysis-javascript.md
+++ b/change-notes/1.23/analysis-javascript.md
@@ -2,30 +2,81 @@
## General improvements
+* Automatic classification of generated and minified files has been improved, in particular files generated by Doxygen are now recognized.
+
+* Support for `globalThis` has been added.
+
* Support for the following frameworks and libraries has been improved:
- [firebase](https://www.npmjs.com/package/firebase)
+ - [get-them-args](https://www.npmjs.com/package/get-them-args)
+ - [minimist](https://www.npmjs.com/package/minimist)
- [mongodb](https://www.npmjs.com/package/mongodb)
- [mongoose](https://www.npmjs.com/package/mongoose)
+ - [optimist](https://www.npmjs.com/package/optimist)
+ - [parse-torrent](https://www.npmjs.com/package/parse-torrent)
+ - [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible)
+ - [yargs](https://www.npmjs.com/package/yargs)
* The call graph has been improved to resolve method calls in more cases. This may produce more security alerts.
+* TypeScript 3.6 and 3.7 features are now supported.
+
## New queries
| **Query** | **Tags** | **Purpose** |
|---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Unused index variable (`js/unused-index-variable`) | correctness | Highlights loops that iterate over an array, but do not use the index variable to access array elements, indicating a possible typo or logic error. |
-
+| Ignoring result from pure array method (`js/ignore-array-result`) | maintainability, correctness | Highlights calls to array methods without side effects where the return value is ignored. Results are shown on LGTM by default. |
+| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | security, correctness, external/cwe/cwe-020 | Highlights checks for `javascript:` URLs that do not take `data:` or `vbscript:` URLs into account. Results are shown on LGTM by default. |
+| Loop bound injection (`js/loop-bound-injection`) | security, external/cwe/cwe-834 | Highlights loops where a user-controlled object with an arbitrary .length value can trick the server to loop indefinitely. Results are shown on LGTM by default. |
+| Shell command built from environment values (`js/shell-command-injection-from-environment`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights shell commands that may change behavior inadvertently depending on the execution environment, indicating a possible violation of [CWE-78](https://cwe.mitre.org/data/definitions/78.html). Results are shown on LGTM by default.|
+| Suspicious method name (`js/suspicious-method-name-declaration`) | correctness, typescript, methods | Highlights suspiciously named methods where the developer likely meant to write a constructor or function. Results are shown on LGTM by default. |
+| Unreachable method overloads (`js/unreachable-method-overloads`) | correctness, typescript | Highlights method overloads that are impossible to use from client code. Results are shown on LGTM by default. |
+| Unused index variable (`js/unused-index-variable`) | correctness | Highlights loops that iterate over an array, but do not use the index variable to access array elements, indicating a possible typo or logic error. Results are shown on LGTM by default. |
+| Use of returnless function (`js/use-of-returnless-function`) | maintainability, correctness | Highlights calls where the return value is used, but the callee never returns a value. Results are shown on LGTM by default. |
+| Useless regular expression character escape (`js/useless-regexp-character-escape`) | correctness, security, external/cwe/cwe-20 | Highlights regular expression strings with useless character escapes, indicating a possible violation of [CWE-20](https://cwe.mitre.org/data/definitions/20.html). Results are shown on LGTM by default. |
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
| Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false-positive results | This rule now recognizes additional ways delimiters can be stripped away. |
-| Client-side cross-site scripting (`js/xss`) | More results | More potential vulnerabilities involving functions that manipulate DOM attributes are now recognized. |
+| Client-side cross-site scripting (`js/xss`) | More results, fewer false-positive results | More potential vulnerabilities involving functions that manipulate DOM attributes are now recognized, and more sanitizers are detected. |
| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving functions that manipulate DOM event handler attributes are now recognized. |
-| Prototype pollution (`js/prototype-pollution`) | More results | The query now highlights vulnerable uses of jQuery and Angular, and the results are shown on LGTM by default. |
+| Hard-coded credentials (`js/hardcoded-credentials`) | Fewer false-positive results | This rule now flags fewer password examples. |
+| Illegal invocation (`js/illegal-invocation`) | Fewer false-positive results | This rule now correctly handles methods named `call` and `apply`. |
| Incorrect suffix check (`js/incorrect-suffix-check`) | Fewer false-positive results | The query recognizes valid checks in more cases. |
+| Network data written to file (`js/http-to-file-access`) | Fewer false-positive results | This query has been renamed to better match its intended purpose, and now only considers network data untrusted. |
+| Password in configuration file (`js/password-in-configuration-file`) | Fewer false-positive results | This rule now flags fewer password examples. |
+| Prototype pollution (`js/prototype-pollution`) | More results | The query now highlights vulnerable uses of jQuery and Angular, and the results are shown on LGTM by default. |
+| Reflected cross-site scripting (`js/reflected-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. |
+| Stored cross-site scripting (`js/stored-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. |
+| Uncontrolled command line (`js/command-line-injection`) | More results | This query now treats responses from servers as untrusted. |
+| Uncontrolled data used in path expression (`js/path-injection`) | Fewer false-positive results | This query now recognizes calls to Express `sendFile` as safe in some cases. |
+| Unknown directive (`js/unknown-directive`) | Fewer false positive results | This query no longer flags uses of ":", which is sometimes used like a directive. |
-## Changes to QL libraries
+## Changes to libraries
* `Expr.getDocumentation()` now handles chain assignments.
+* String literals are now parsed as regular expressions.
+ Consequently, a `RegExpTerm` may occur as part of a string literal or
+ as a regular expression literal. Queries that search for regular expressions may need to
+ use `RegExpTerm.isPartOfRegExpLiteral` or `RegExpTerm.isUsedAsRegExp` to restrict the search.
+ A regular expression AST can be obtained from a string literal using `StringLiteral.asRegExp`.
+
+## Removal of deprecated queries
+
+The following queries (deprecated since 1.17) are no longer available in the distribution:
+
+* Builtin redefined (js/builtin-redefinition)
+* Inefficient method definition (js/method-definition-in-constructor)
+* Bad parity check (js/incomplete-parity-check)
+* Potentially misspelled property or variable name (js/wrong-capitalization)
+* Unknown JSDoc tag (js/jsdoc/unknown-tag-type)
+* Invalid JSLint directive (js/jslint/invalid-directive)
+* Malformed JSLint directive (js/jslint/malformed-directive)
+* Use of HTML comments (js/html-comment)
+* Multi-line string literal (js/multi-line-string)
+* Octal literal (js/octal-literal)
+* Reserved word used as variable name (js/use-of-reserved-word)
+* Trailing comma in array or object expressions (js/trailing-comma-in-array-or-object)
+* Call to parseInt without radix (js/parseint-without-radix)
diff --git a/change-notes/1.23/analysis-python.md b/change-notes/1.23/analysis-python.md
index 3319697fcfb..6cea1745284 100644
--- a/change-notes/1.23/analysis-python.md
+++ b/change-notes/1.23/analysis-python.md
@@ -11,4 +11,17 @@
|-----------|----------|-------------|
| Clear-text logging of sensitive information (`py/clear-text-logging-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is logged without encryption or hashing. Results are shown on LGTM by default. |
| Clear-text storage of sensitive information (`py/clear-text-storage-sensitive-data`) | security, external/cwe/cwe-312 | Finds instances where sensitive information is stored without encryption or hashing. Results are shown on LGTM by default. |
+| Binding a socket to all network interfaces (`py/bind-socket-all-network-interfaces`) | security | Finds instances where a socket is bound to all network interfaces. Results are shown on LGTM by default. |
+
+## Changes to existing queries
+
+| **Query** | **Expected impact** | **Change** |
+|----------------------------|------------------------|------------|
+| Unreachable code | Fewer false positives | Analysis now accounts for uses of `contextlib.suppress` to suppress exceptions. |
+| `__iter__` method returns a non-iterator | Better alert message | Alert now highlights which class is expected to be an iterator. |
+
+
+## Changes to QL libraries
+
+* Django library now recognizes positional arguments from a `django.conf.urls.url` regex (Django version 1.x)
diff --git a/change-notes/1.23/extractor-javascript.md b/change-notes/1.23/extractor-javascript.md
index ed9664ce812..8b7a35f5b4f 100644
--- a/change-notes/1.23/extractor-javascript.md
+++ b/change-notes/1.23/extractor-javascript.md
@@ -5,3 +5,19 @@
## Changes to code extraction
* Asynchronous generator methods are now parsed correctly and no longer cause a spurious syntax error.
+* Files in `node_modules` and `bower_components` folders are no longer extracted by default. If you still want to extract files from these folders, you can add the following filters to your `lgtm.yml` file (or add them to existing filters):
+
+```yaml
+extraction:
+ javascript:
+ index:
+ filters:
+ - include: "**/node_modules"
+ - include: "**/bower_components"
+```
+
+* Recognition of CommonJS modules has improved. As a result, some files that were previously extracted as
+ global scripts are now extracted as modules.
+* Top-level `await` is now supported.
+* A bug was fixed in how the TypeScript extractor handles default-exported anonymous classes.
+* A bug was fixed in how the TypeScript extractor handles computed instance field names.
diff --git a/change-notes/support/README.md b/change-notes/support/README.md
index 181d5152126..f111a437183 100644
--- a/change-notes/support/README.md
+++ b/change-notes/support/README.md
@@ -1,6 +1,6 @@
# Files moved to ``docs`` directory
-Now that all of the QL documentation is in this repository,
+Now that all of the CodeQL documentation is in this repository,
notes on the languages, compilers, and frameworks supported have moved.
They're now stored as part of the Sphinx ``support`` project with the other documentation:
-``docs/ql-documentation/support``.
+``docs/language/support``.
diff --git a/config/identical-files.json b/config/identical-files.json
index 6a216ac256d..4fdac5c7fea 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -47,31 +47,40 @@
"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/raw/Instruction.qll",
+ "csharp/ql/src/semmle/code/csharp/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/raw/IRBlock.qll",
+ "csharp/ql/src/semmle/code/csharp/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/raw/IRVariable.qll",
+ "csharp/ql/src/semmle/code/csharp/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/raw/IRFunction.qll",
+ "csharp/ql/src/semmle/code/csharp/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/raw/Operand.qll",
+ "csharp/ql/src/semmle/code/csharp/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"
],
"IR Operand Tag": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
@@ -85,19 +94,22 @@
"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/raw/IR.qll",
+ "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll"
],
"IR IRSanity": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll",
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll"
+ "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll",
+ "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.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/raw/PrintIR.qll",
+ "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/PrintIR.qll"
],
"IR IntegerConstant": [
"cpp/ql/src/semmle/code/cpp/ir/internal/IntegerConstant.qll",
@@ -161,22 +173,39 @@
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintIRImports.qll"
],
+ "C++ SSA SSAConstructionImports": [
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll",
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll"
+ ],
"C++ 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"
],
- "C++ SSA SSAConstruction": [
+ "C++ IR ValueNumberingImports": [
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
+ "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
+ ],
+ "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"
+ ],
+ "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"
+ "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"
],
- "C++ SSA PrintSSA": [
+ "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"
+ "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"
],
- "C++ IR ValueNumber": [
+ "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"
+ "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"
],
"C++ IR ConstantAnalysis": [
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
@@ -205,21 +234,31 @@
"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/raw/internal/InstructionImports.qll",
+ "csharp/ql/src/semmle/code/csharp/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/raw/internal/IRImports.qll",
+ "csharp/ql/src/semmle/code/csharp/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/raw/internal/IRBlockImports.qll",
+ "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
],
"C# IR IRVariableImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll"
+ "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"
],
"C# IR OperandImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll"
+ "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"
],
"C# IR PrintIRImports": [
- "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll"
+ "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"
+ ],
+ "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"
]
}
diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql
index 26cf42521e5..3ad43998d18 100644
--- a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql
+++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql
@@ -21,6 +21,7 @@ from Variable v
where
v.isStatic() and
v.hasDefinition() and
+ not v.isConstexpr() and
not exists(VariableAccess a | a.getTarget() = v) and
not v instanceof MemberVariable and
not declarationHasSideEffects(v) and
diff --git a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql
index 9c93e066119..47401c6eea5 100644
--- a/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql
+++ b/cpp/ql/src/Critical/DescriptorMayNotBeClosed.ql
@@ -13,7 +13,7 @@ import semmle.code.cpp.pointsto.PointsTo
import Negativity
predicate closeCall(FunctionCall fc, Variable v) {
- fc.getTarget().hasGlobalName("close") and v.getAnAccess() = fc.getArgument(0)
+ fc.getTarget().hasGlobalOrStdName("close") and v.getAnAccess() = fc.getArgument(0)
or
exists(FunctionCall midcall, Function mid, int arg |
fc.getArgument(arg) = v.getAnAccess() and
diff --git a/cpp/ql/src/Critical/DescriptorNeverClosed.ql b/cpp/ql/src/Critical/DescriptorNeverClosed.ql
index b52af1bf2a3..f06708f4ae3 100644
--- a/cpp/ql/src/Critical/DescriptorNeverClosed.ql
+++ b/cpp/ql/src/Critical/DescriptorNeverClosed.ql
@@ -13,7 +13,7 @@ import semmle.code.cpp.pointsto.PointsTo
predicate closed(Expr e) {
exists(FunctionCall fc |
- fc.getTarget().hasGlobalName("close") and
+ fc.getTarget().hasGlobalOrStdName("close") and
fc.getArgument(0) = e
)
}
diff --git a/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql b/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql
index 9bba8e9896d..2bede681912 100644
--- a/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql
+++ b/cpp/ql/src/Critical/MemoryMayNotBeFreed.ql
@@ -53,7 +53,7 @@ predicate allocCallOrIndirect(Expr e) {
* can cause memory leaks.
*/
predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode verified) {
- reallocCall.getTarget().hasGlobalName("realloc") and
+ reallocCall.getTarget().hasGlobalOrStdName("realloc") and
reallocCall.getArgument(0) = v.getAnAccess() and
(
exists(Variable newV, ControlFlowNode node |
@@ -79,7 +79,7 @@ predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode
predicate freeCallOrIndirect(ControlFlowNode n, Variable v) {
// direct free call
freeCall(n, v.getAnAccess()) and
- not n.(FunctionCall).getTarget().hasGlobalName("realloc")
+ not n.(FunctionCall).getTarget().hasGlobalOrStdName("realloc")
or
// verified realloc call
verifiedRealloc(_, v, n)
diff --git a/cpp/ql/src/Critical/OverflowCalculated.ql b/cpp/ql/src/Critical/OverflowCalculated.ql
index 36ee0140cf7..fbca4a20a8f 100644
--- a/cpp/ql/src/Critical/OverflowCalculated.ql
+++ b/cpp/ql/src/Critical/OverflowCalculated.ql
@@ -13,10 +13,7 @@
import cpp
class MallocCall extends FunctionCall {
- MallocCall() {
- this.getTarget().hasGlobalName("malloc") or
- this.getTarget().hasQualifiedName("std", "malloc")
- }
+ MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
Expr getAllocatedSize() {
if this.getArgument(0) instanceof VariableAccess
@@ -36,12 +33,12 @@ predicate spaceProblem(FunctionCall append, string msg) {
malloc.getAllocatedSize() = add and
buffer.getAnAccess() = strlen.getStringExpr() and
(
- insert.getTarget().hasGlobalName("strcpy") or
- insert.getTarget().hasGlobalName("strncpy")
+ insert.getTarget().hasGlobalOrStdName("strcpy") or
+ insert.getTarget().hasGlobalOrStdName("strncpy")
) and
(
- append.getTarget().hasGlobalName("strcat") or
- append.getTarget().hasGlobalName("strncat")
+ append.getTarget().hasGlobalOrStdName("strcat") or
+ append.getTarget().hasGlobalOrStdName("strncat")
) and
malloc.getASuccessor+() = insert and
insert.getArgument(1) = buffer.getAnAccess() and
diff --git a/cpp/ql/src/Critical/OverflowDestination.ql b/cpp/ql/src/Critical/OverflowDestination.ql
index d7b02b5d8d5..ad925daed62 100644
--- a/cpp/ql/src/Critical/OverflowDestination.ql
+++ b/cpp/ql/src/Critical/OverflowDestination.ql
@@ -25,7 +25,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().hasGlobalName(name)
+ fc.getTarget().hasGlobalOrStdName(name)
) and
exists(Expr dest, Expr size, Variable v |
fc.getArgument(0) = dest and
diff --git a/cpp/ql/src/Critical/OverflowStatic.ql b/cpp/ql/src/Critical/OverflowStatic.ql
index 1892d5acff1..82ffc879331 100644
--- a/cpp/ql/src/Critical/OverflowStatic.ql
+++ b/cpp/ql/src/Critical/OverflowStatic.ql
@@ -60,19 +60,19 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
predicate bufferAndSizeFunction(Function f, int buf, int size) {
f.hasGlobalName("read") and buf = 1 and size = 2
or
- f.hasGlobalName("fgets") and buf = 0 and size = 1
+ f.hasGlobalOrStdName("fgets") and buf = 0 and size = 1
or
- f.hasGlobalName("strncpy") and buf = 0 and size = 2
+ f.hasGlobalOrStdName("strncpy") and buf = 0 and size = 2
or
- f.hasGlobalName("strncat") and buf = 0 and size = 2
+ f.hasGlobalOrStdName("strncat") and buf = 0 and size = 2
or
- f.hasGlobalName("memcpy") and buf = 0 and size = 2
+ f.hasGlobalOrStdName("memcpy") and buf = 0 and size = 2
or
- f.hasGlobalName("memmove") and buf = 0 and size = 2
+ f.hasGlobalOrStdName("memmove") and buf = 0 and size = 2
or
- f.hasGlobalName("snprintf") and buf = 0 and size = 1
+ f.hasGlobalOrStdName("snprintf") and buf = 0 and size = 1
or
- f.hasGlobalName("vsnprintf") and buf = 0 and size = 1
+ f.hasGlobalOrStdName("vsnprintf") and buf = 0 and size = 1
}
class CallWithBufferSize extends FunctionCall {
diff --git a/cpp/ql/src/Critical/SizeCheck.ql b/cpp/ql/src/Critical/SizeCheck.ql
index da841b73c9b..313763ba56c 100644
--- a/cpp/ql/src/Critical/SizeCheck.ql
+++ b/cpp/ql/src/Critical/SizeCheck.ql
@@ -17,12 +17,12 @@ import cpp
class Allocation extends FunctionCall {
Allocation() {
exists(string name |
- this.getTarget().hasGlobalName(name) and
+ this.getTarget().hasGlobalOrStdName(name) and
(name = "malloc" or name = "calloc" or name = "realloc")
)
}
- private string getName() { this.getTarget().hasGlobalName(result) }
+ private string getName() { this.getTarget().hasGlobalOrStdName(result) }
int getSize() {
this.getName() = "malloc" and
diff --git a/cpp/ql/src/Critical/SizeCheck2.ql b/cpp/ql/src/Critical/SizeCheck2.ql
index 3cb5d1d28b0..1b716d79d49 100644
--- a/cpp/ql/src/Critical/SizeCheck2.ql
+++ b/cpp/ql/src/Critical/SizeCheck2.ql
@@ -17,12 +17,12 @@ import cpp
class Allocation extends FunctionCall {
Allocation() {
exists(string name |
- this.getTarget().hasGlobalName(name) and
+ this.getTarget().hasGlobalOrStdName(name) and
(name = "malloc" or name = "calloc" or name = "realloc")
)
}
- private string getName() { this.getTarget().hasGlobalName(result) }
+ private string getName() { this.getTarget().hasGlobalOrStdName(result) }
int getSize() {
this.getName() = "malloc" and
diff --git a/cpp/ql/src/Critical/UseAfterFree.ql b/cpp/ql/src/Critical/UseAfterFree.ql
index db78c206ea1..9efbb6c3b44 100644
--- a/cpp/ql/src/Critical/UseAfterFree.ql
+++ b/cpp/ql/src/Critical/UseAfterFree.ql
@@ -16,7 +16,7 @@ import semmle.code.cpp.controlflow.LocalScopeVariableReachability
predicate isFreeExpr(Expr e, LocalScopeVariable v) {
exists(VariableAccess va | va.getTarget() = v |
exists(FunctionCall fc | fc = e |
- fc.getTarget().hasGlobalName("free") and
+ fc.getTarget().hasGlobalOrStdName("free") and
va = fc.getArgument(0)
)
or
diff --git a/cpp/ql/src/DefaultOptions.qll b/cpp/ql/src/DefaultOptions.qll
index 27e5584369e..3e03ec9ee65 100644
--- a/cpp/ql/src/DefaultOptions.qll
+++ b/cpp/ql/src/DefaultOptions.qll
@@ -59,7 +59,7 @@ class Options extends string {
predicate exits(Function f) {
f.getAnAttribute().hasName("noreturn")
or
- exists(string name | f.hasGlobalName(name) |
+ exists(string name | f.hasGlobalOrStdName(name) |
name = "exit" or
name = "_exit" or
name = "abort" or
@@ -91,7 +91,7 @@ class Options extends string {
* By default holds only for `fgets`.
*/
predicate alwaysCheckReturnValue(Function f) {
- f.hasGlobalName("fgets") or
+ f.hasGlobalOrStdName("fgets") or
CustomOptions::alwaysCheckReturnValue(f) // old Options.qll
}
diff --git a/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.ql b/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.ql
index a2f3ec1d119..7a7f328bd4e 100644
--- a/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.ql
+++ b/cpp/ql/src/Likely Bugs/AmbiguouslySignedBitField.ql
@@ -28,7 +28,8 @@ where
not bf.getType().hasName("BOOL") and
// If this is true, then there cannot be unsigned sign extension or overflow.
not bf.getDeclaredNumBits() = bf.getType().getSize() * 8 and
- not bf.isAnonymous()
+ not bf.isAnonymous() and
+ not bf.isFromUninstantiatedTemplate(_)
select bf,
"Bit field " + bf.getName() + " of type " + bf.getUnderlyingType().getName() +
" should have explicitly unsigned integral, explicitly signed integral, or enumeration type."
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qhelp
index a8c2b1567a7..0af74559dff 100644
--- a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qhelp
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qhelp
@@ -2,36 +2,39 @@
"-//Semmle//qhelp//EN"
"qhelp.dtd">
-
-
- Checking for overflow of integer addition needs to be done with
- care, because automatic type promotion can prevent the check
- from working correctly.
-
-
-
-
- Use an explicit cast to make sure that the result of the addition is
- not implicitly converted to a larger type.
-
-
-
-
-
- On a typical architecture where short is 16 bits
- and int is 32 bits, the operands of the addition are
- automatically promoted to int, so it cannot overflow
- and the result of the comparison is always false.
-
-
- The code below implements the check correctly, by using an
- explicit cast to make sure that the result of the addition
- is unsigned short.
-
-
-
-
- Preserving Rules
- Understand integer conversion rules
-
+
+
+
+Checking for overflow of integer addition needs to be done with
+care, because automatic type promotion can prevent the check
+from working as intended, with the same value (true
+or false) always being returned.
+
+
+
+
+Use an explicit cast to make sure that the result of the addition is
+not implicitly converted to a larger type.
+
+
+
+
+
+On a typical architecture where short is 16 bits
+and int is 32 bits, the operands of the addition are
+automatically promoted to int, so it cannot overflow
+and the result of the comparison is always false.
+
+
+The code below implements the check correctly, by using an
+explicit cast to make sure that the result of the addition
+is unsigned short (which may overflow, in which case
+the comparison would evaluate to true).
+
+
+
+
+Preserving Rules
+Understand integer conversion rules
+
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample1.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample1.cpp
index 4da403cdf51..0e1e539ccb9 100644
--- a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample1.cpp
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample1.cpp
@@ -1,3 +1,4 @@
bool checkOverflow(unsigned short x, unsigned short y) {
- return (x + y < x); // BAD: x and y are automatically promoted to int.
+ // BAD: comparison is always false due to type promotion
+ return (x + y < x);
}
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.ql b/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.ql
index 1e29e77742f..fe1b2640058 100644
--- a/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.ql
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/BitwiseSignCheck.ql
@@ -14,9 +14,16 @@ import cpp
from RelationalOperation e, BinaryBitwiseOperation lhs
where
- lhs = e.getGreaterOperand() and
- lhs.getActualType().(IntegralType).isSigned() and
- forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and
+ // `lhs > 0` (or `0 < lhs`)
+ // (note that `lhs < 0`, `lhs >= 0` or `lhs <= 0` all imply that the signedness of
+ // `lhs` is understood, so should not be flagged).
+ (e instanceof GTExpr or e instanceof LTExpr) and
+ e.getGreaterOperand() = lhs and
e.getLesserOperand().getValue() = "0" and
+ // lhs is signed
+ lhs.getActualType().(IntegralType).isSigned() and
+ // if `lhs` has the form `x & c`, with constant `c`, `c` is negative
+ forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and
+ // exception for cases involving macros
not e.isAffectedByMacro()
select e, "Potential unsafe sign check of a bitwise operation."
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql
index 16d7925c53f..d4b23265eed 100644
--- a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql
@@ -14,5 +14,8 @@
import cpp
from ComparisonOperation co, ComparisonOperation chco
-where co.getAChild() = chco and not chco.isParenthesised()
-select co, "Check the comparison operator precedence."
+where
+ co.getAChild() = chco and
+ not chco.isParenthesised() and
+ not co.isFromUninstantiatedTemplate(_)
+select co, "Comparison as an operand to another comparison."
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.ql b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.ql
index afe49a1053b..04ef1af44ae 100644
--- a/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.ql
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/PointlessSelfComparison.ql
@@ -13,17 +13,13 @@
import cpp
import PointlessSelfComparison
+import semmle.code.cpp.commons.Exclusions
from ComparisonOperation cmp
where
pointlessSelfComparison(cmp) and
not nanTest(cmp) and
not overflowTest(cmp) and
- not exists(MacroInvocation mi |
- // cmp is in mi
- mi.getAnExpandedElement() = cmp and
- // and cmp was apparently not passed in as a macro parameter
- cmp.getLocation().getStartLine() = mi.getLocation().getStartLine() and
- cmp.getLocation().getStartColumn() = mi.getLocation().getStartColumn()
- )
+ not cmp.isFromTemplateInstantiation(_) and
+ not isFromMacroDefinition(cmp)
select cmp, "Self comparison."
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-bad1.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-bad1.cpp
new file mode 100644
index 00000000000..e50273aacde
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-bad1.cpp
@@ -0,0 +1,3 @@
+bool foo(int n1, unsigned short delta) {
+ return n1 + delta < n1; // BAD
+}
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-bad2.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-bad2.cpp
new file mode 100644
index 00000000000..7f69e374ed1
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-bad2.cpp
@@ -0,0 +1,4 @@
+bool bar(unsigned short n1, unsigned short delta) {
+ // NB: Comparison is always false
+ return n1 + delta < n1; // GOOD (but misleading)
+}
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-good1.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-good1.cpp
new file mode 100644
index 00000000000..424684ee2ec
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-good1.cpp
@@ -0,0 +1,4 @@
+#include
+bool foo(int n1, unsigned short delta) {
+ return n1 > INT_MAX - delta; // GOOD
+}
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-good2.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-good2.cpp
new file mode 100644
index 00000000000..de8de2b9847
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck-good2.cpp
@@ -0,0 +1,3 @@
+bool bar(unsigned short n1, unsigned short delta) {
+ return (unsigned short)(n1 + delta) < n1; // GOOD
+}
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.qhelp
new file mode 100644
index 00000000000..621ae3273fd
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.qhelp
@@ -0,0 +1,115 @@
+
+
+
+
+When checking for integer overflow, you may often write tests like
+a + b < a. This works fine if a or
+b are unsigned integers, since any overflow in the addition
+will cause the value to simply "wrap around." However, using
+signed integers is problematic because signed overflow has undefined
+behavior according to the C and C++ standards. If the addition overflows
+and has an undefined result, the comparison will likewise be undefined;
+it may produce an unintended result, or may be deleted entirely by an
+optimizing compiler.
+
+
+
+
+Solutions to this problem can be thought of as falling into one of two
+categories: (1) rewrite the signed expression so that overflow cannot occur
+but the signedness remains, or (2) rewrite (or cast) the signed expression
+into unsigned form.
+
+
+
+Below we list examples of expressions where signed overflow may
+occur, along with proposed solutions. The list should not be
+considered exhaustive.
+
+
+
+Given unsigned short i, delta and i + delta < i,
+it is possible to rewrite it as (unsigned short)(i + delta) < i.
+Note that i + deltadoes not actually overflow, due to int promotion
+
+
+
+Given unsigned short i, delta and i + delta < i,
+it is also possible to rewrite it as USHORT_MAX - delta. It must be true
+that delta > 0 and the limits.h or climits
+header has been included.
+
+
+
+Given int i, delta and i + delta < i,
+it is possible to rewrite it as INT_MAX - delta. It must be true
+that delta > 0 and the limits.h or climits
+header has been included.
+
+
+
+Given int i, delta and i + delta < i,
+it is also possible to rewrite it as (unsigned)i + delta < i.
+Note that program semantics are affected by this change.
+
+
+
+Given int i, delta and i + delta < i,
+it is also possible to rewrite it as unsigned int i, delta and
+i + delta < i. Note that program semantics are
+affected by this change.
+
+
+
+
+
+In the following example, even though delta has been declared
+unsigned short, C/C++ type promotion rules require that its
+type is promoted to the larger type used in the addition and comparison,
+namely a signed int. Addition is performed on
+signed integers, and may have undefined behavior if an overflow occurs.
+As a result, the entire (comparison) expression may also have an undefined
+result.
+
+
+
+The following example builds upon the previous one. Instead of
+performing an addition (which could overflow), we have re-framed the
+solution so that a subtraction is used instead. Since delta
+is promoted to a signed int and INT_MAX denotes
+the largest possible positive value for an signed int,
+the expression INT_MAX - delta can never be less than zero
+or more than INT_MAX. Hence, any overflow and underflow
+are avoided.
+
+
+
+In the following example, even though both n and delta
+have been declared unsigned short, both are promoted to
+signed int prior to addition. Because we started out with the
+narrower short type, the addition is guaranteed not to overflow
+and is therefore defined. But the fact that n1 + delta never
+overflows means that the condition n1 + delta < n1 will never
+hold true, which likely is not what the programmer intended. (see also the
+cpp/bad-addition-overflow-check query).
+
+
+
+The next example provides a solution to the previous one. Even though
+i + delta does not overflow, casting it to an
+unsigned short truncates the addition modulo 2^16,
+so that unsigned short "wrap around" may now be observed.
+Furthermore, since the left-hand side is now of type unsigned short,
+the right-hand side does not need to be promoted to a signed int.
+
+
+
+
+
+comp.lang.c FAQ list · Question 3.19 (Preserving rules)
+INT31-C. Ensure that integer conversions do not result in lost or misinterpreted data
+W. Dietz, P. Li, J. Regehr, V. Adve. Understanding Integer Overflow in C/C++
+
+
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql
new file mode 100644
index 00000000000..ce1a8149c40
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/SignedOverflowCheck.ql
@@ -0,0 +1,31 @@
+/**
+ * @name Signed overflow check
+ * @description Testing for overflow by adding a value to a variable
+ * to see if it "wraps around" works only for
+ * unsigned integer values.
+ * @kind problem
+ * @problem.severity warning
+ * @precision high
+ * @id cpp/signed-overflow-check
+ * @tags correctness
+ * security
+ */
+
+import cpp
+private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
+
+from RelationalOperation ro, AddExpr add, Expr expr1, Expr expr2
+where
+ ro.getAnOperand() = add and
+ add.getAnOperand() = expr1 and
+ ro.getAnOperand() = expr2 and
+ globalValueNumber(expr1) = globalValueNumber(expr2) and
+ add.getUnspecifiedType().(IntegralType).isSigned() and
+ not exists(MacroInvocation mi | mi.getAnAffectedElement() = add) and
+ exprMightOverflowPositively(add) and
+ exists(Compilation c | c.getAFileCompiled() = ro.getFile() |
+ not c.getAnArgument() = "-fwrapv" and
+ not c.getAnArgument() = "-fno-strict-overflow"
+ )
+select ro, "Testing for signed overflow may produce undefined results."
diff --git a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
index 75c5bc73029..6f44f3d4d08 100644
--- a/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
+++ b/cpp/ql/src/Likely Bugs/Format/WrongTypeFormatArguments.ql
@@ -157,7 +157,8 @@ where
formatOtherArgType(ffc, n, expected, arg, actual) and
not actual.getUnspecifiedType().(IntegralType).getSize() = sizeof_IntType()
) and
- not arg.isAffectedByMacro()
+ not arg.isAffectedByMacro() and
+ not arg.isFromUninstantiatedTemplate(_)
select arg,
"This argument should be of type '" + expected.getName() + "' but is of type '" +
actual.getUnspecifiedType().getName() + "'"
diff --git a/cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qll b/cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qll
index f75305dbf2a..23b66bd94a6 100644
--- a/cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qll
+++ b/cpp/ql/src/Likely Bugs/Leap Year/LeapYear.qll
@@ -30,7 +30,7 @@ private predicate additionalLogicalCheck(Expr e, string operation, int valueToCh
/**
* An `Operation` that seems to be checking for leap year.
*/
-class CheckForLeapYearOperation extends Operation {
+class CheckForLeapYearOperation extends Expr {
CheckForLeapYearOperation() {
exists(BinaryArithmeticOperation bo | bo = this |
bo.getAnOperand().getValue().toInt() = 4 and
@@ -39,8 +39,6 @@ class CheckForLeapYearOperation extends Operation {
additionalLogicalCheck(this.getEnclosingElement(), "%", 400)
)
}
-
- override string getOperator() { result = "LeapYearCheck" }
}
/**
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound-bad.cpp b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound-bad.cpp
new file mode 100644
index 00000000000..0185bbb9da6
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound-bad.cpp
@@ -0,0 +1,6 @@
+int get_number_from_network();
+
+int process_network(int[] buff, int buffSize) {
+ int i = ntohl(get_number_from_network());
+ return buff[i];
+}
\ No newline at end of file
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound-good.cpp b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound-good.cpp
new file mode 100644
index 00000000000..2d5ec265261
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound-good.cpp
@@ -0,0 +1,10 @@
+uint32_t get_number_from_network();
+
+int process_network(int[] buff, uint32_t buffSize) {
+ uint32_t i = ntohl(get_number_from_network());
+ if (i < buffSize) {
+ return buff[i];
+ } else {
+ return -1;
+ }
+}
\ No newline at end of file
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.qhelp
new file mode 100644
index 00000000000..3556df38c58
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.qhelp
@@ -0,0 +1,54 @@
+
+
+
+
+
+Data received over a network connection may be received in a different byte order than
+the byte order used by the local host, making the data difficult to process. To address this,
+data received over the wire is usually converted to host byte order by a call to a network-to-host
+byte order function, such as ntohl.
+
+
+The use of a network-to-host byte order function is therefore a good indicator that the returned
+value is unvalidated data retrieved from the network, and should not be used without further
+validation. In particular, the returned value should not be used as an array index or array length
+value without validation, as this could result in a buffer overflow vulnerability.
+
+
+
+
+
+Validate data returned by network-to-host byte order functions before use and especially before
+using the value as an array index or bound.
+
+
+
+
+In the example below, network data is retrieved and passed to ntohl to convert
+it to host byte order. The data is then used as an index in an array access expression. However,
+there is no validation that the data returned by ntohl is within the bounds of the array,
+which could lead to reading outside the bounds of the buffer.
+
+
+In the corrected example, the returned data is validated against the known size of the buffer,
+before being used as an array index.
+
+
+
+
+
+
+ ntohl - winsock reference
+
+
+
+
+ ntohl - Linux man page
+
+
+
+
+
+
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.ql b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.ql
new file mode 100644
index 00000000000..d6d0a55d148
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.ql
@@ -0,0 +1,17 @@
+/**
+ * @id cpp/network-to-host-function-as-array-bound
+ * @name Untrusted network-to-host usage
+ * @description Using the result of a network-to-host byte order function, such as ntohl, as an
+ * array bound or length value without checking it may result in buffer overflows or
+ * other vulnerabilties.
+ * @kind problem
+ * @problem.severity error
+ */
+
+import cpp
+import NtohlArrayNoBound
+import semmle.code.cpp.dataflow.DataFlow
+
+from NetworkToBufferSizeConfiguration bufConfig, DataFlow::Node source, DataFlow::Node sink
+where bufConfig.hasFlow(source, sink)
+select sink, "Unchecked use of data from network function $@", source, source.toString()
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.qll b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.qll
new file mode 100644
index 00000000000..0871da2f92e
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Memory Management/NtohlArrayNoBound.qll
@@ -0,0 +1,154 @@
+import cpp
+import semmle.code.cpp.dataflow.DataFlow
+import semmle.code.cpp.controlflow.Guards
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+
+/**
+ * An access (read or write) to a buffer, provided as a pair of
+ * a pointer to the buffer and the length of data to be read or written.
+ * Extend this class to support different kinds of buffer access.
+ */
+abstract class BufferAccess extends Locatable {
+ /** Gets the pointer to the buffer being accessed. */
+ abstract Expr getPointer();
+
+ /** Gets the length of the data being read or written by this buffer access. */
+ abstract Expr getAccessedLength();
+}
+
+/**
+ * A buffer access through an array expression.
+ */
+class ArrayBufferAccess extends BufferAccess, ArrayExpr {
+ override Expr getPointer() { result = this.getArrayBase() }
+
+ override Expr getAccessedLength() { result = this.getArrayOffset() }
+}
+
+/**
+ * A buffer access through an overloaded array expression.
+ */
+class OverloadedArrayBufferAccess extends BufferAccess, OverloadedArrayExpr {
+ override Expr getPointer() { result = this.getQualifier() }
+
+ override Expr getAccessedLength() { result = this.getAnArgument() }
+}
+
+/**
+ * A buffer access through pointer arithmetic.
+ */
+class PointerArithmeticAccess extends BufferAccess, Expr {
+ PointerArithmeticOperation p;
+
+ PointerArithmeticAccess() {
+ this = p and
+ p.getAnOperand().getType().getUnspecifiedType() instanceof IntegralType and
+ not p.getParent() instanceof ComparisonOperation
+ }
+
+ override Expr getPointer() {
+ result = p.getAnOperand() and
+ result.getType().getUnspecifiedType() instanceof PointerType
+ }
+
+ override Expr getAccessedLength() {
+ result = p.getAnOperand() and
+ result.getType().getUnspecifiedType() instanceof IntegralType
+ }
+}
+
+/**
+ * A pair of buffer accesses through a call to memcpy.
+ */
+class MemCpy extends BufferAccess, FunctionCall {
+ MemCpy() { getTarget().hasName("memcpy") }
+
+ override Expr getPointer() {
+ result = getArgument(0) or
+ result = getArgument(1)
+ }
+
+ override Expr getAccessedLength() { result = getArgument(2) }
+}
+
+class StrncpySizeExpr extends BufferAccess, FunctionCall {
+ StrncpySizeExpr() { getTarget().hasName("strncpy") }
+
+ override Expr getPointer() {
+ result = getArgument(0) or
+ result = getArgument(1)
+ }
+
+ override Expr getAccessedLength() { result = getArgument(2) }
+}
+
+class RecvSizeExpr extends BufferAccess, FunctionCall {
+ RecvSizeExpr() { getTarget().hasName("recv") }
+
+ override Expr getPointer() { result = getArgument(1) }
+
+ override Expr getAccessedLength() { result = getArgument(2) }
+}
+
+class SendSizeExpr extends BufferAccess, FunctionCall {
+ SendSizeExpr() { getTarget().hasName("send") }
+
+ override Expr getPointer() { result = getArgument(1) }
+
+ override Expr getAccessedLength() { result = getArgument(2) }
+}
+
+class SnprintfSizeExpr extends BufferAccess, FunctionCall {
+ SnprintfSizeExpr() { getTarget().hasName("snprintf") }
+
+ override Expr getPointer() { result = getArgument(0) }
+
+ override Expr getAccessedLength() { result = getArgument(1) }
+}
+
+class MemcmpSizeExpr extends BufferAccess, FunctionCall {
+ MemcmpSizeExpr() { getTarget().hasName("Memcmp") }
+
+ override Expr getPointer() {
+ result = getArgument(0) or
+ result = getArgument(1)
+ }
+
+ override Expr getAccessedLength() { result = getArgument(2) }
+}
+
+class MallocSizeExpr extends BufferAccess, FunctionCall {
+ MallocSizeExpr() { getTarget().hasName("malloc") }
+
+ override Expr getPointer() { none() }
+
+ override Expr getAccessedLength() { result = getArgument(1) }
+}
+
+class NetworkFunctionCall extends FunctionCall {
+ NetworkFunctionCall() {
+ getTarget().hasName("ntohd") or
+ getTarget().hasName("ntohf") or
+ getTarget().hasName("ntohl") or
+ getTarget().hasName("ntohll") or
+ getTarget().hasName("ntohs")
+ }
+}
+
+class NetworkToBufferSizeConfiguration extends DataFlow::Configuration {
+ NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" }
+
+ override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof NetworkFunctionCall }
+
+ override predicate isSink(DataFlow::Node node) {
+ node.asExpr() = any(BufferAccess ba).getAccessedLength()
+ }
+
+ override predicate isBarrier(DataFlow::Node node) {
+ exists(GuardCondition gc, GVN gvn |
+ gc.getAChild*() = gvn.getAnExpr() and
+ globalValueNumber(node.asExpr()) = gvn and
+ gc.controls(node.asExpr().getBasicBlock(), _)
+ )
+ }
+}
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow-bad.cpp b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow-bad.cpp
new file mode 100644
index 00000000000..4ab13e20912
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow-bad.cpp
@@ -0,0 +1,3 @@
+bool not_in_range(T *ptr, T *ptr_end, size_t i) {
+ return ptr + i >= ptr_end || ptr + i < ptr; // BAD
+}
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow-good.cpp b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow-good.cpp
new file mode 100644
index 00000000000..33c619cc806
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow-good.cpp
@@ -0,0 +1,3 @@
+bool not_in_range(T *ptr, T *ptr_end, size_t i) {
+ return i >= ptr_end - ptr; // GOOD
+}
\ No newline at end of file
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.qhelp
new file mode 100644
index 00000000000..5cc0ae21af9
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.qhelp
@@ -0,0 +1,67 @@
+
+
+
+
+When checking for integer overflow, you may often write tests like
+p + i < p. This works fine if p and
+i are unsigned integers, since any overflow in the addition
+will cause the value to simply "wrap around." However, using this pattern when
+p is a pointer is problematic because pointer overflow has
+undefined behavior according to the C and C++ standards. If the addition
+overflows and has an undefined result, the comparison will likewise be
+undefined; it may produce an unintended result, or may be deleted entirely by an
+optimizing compiler.
+
+
+
+
+
+To check whether an index i is less than the length of an array,
+simply compare these two numbers as unsigned integers: i < ARRAY_LENGTH.
+If the length of the array is defined as the difference between two pointers
+ptr and p_end, write i < p_end - ptr.
+If i is signed, cast it to unsigned
+in order to guard against negative i. For example, write
+(size_t)i < p_end - ptr.
+
+
+
+
+An invalid check for pointer overflow is most often seen as part of checking
+whether a number a is too large by checking first if adding the
+number to ptr goes past the end of an allocation and then
+checking if adding it to ptr creates a pointer so large that it
+overflows and wraps around.
+
+
+
+
+
+In both of these checks, the operations are performed in the wrong order.
+First, an expression that may cause undefined behavior is evaluated
+(ptr + i), and then the result is checked for being in range.
+But once undefined behavior has happened in the pointer addition, it cannot
+be recovered from: it's too late to perform the range check after a possible
+pointer overflow.
+
+
+
+While it's not the subject of this query, the expression ptr + i <
+ptr_end is also an invalid range check. It's undefined behavor in
+C/C++ to create a pointer that points more than one past the end of an
+allocation.
+
+
+
+The next example shows how to portably check whether an unsigned number is outside the
+range of an allocation between ptr and ptr_end.
+
+
+
+
+Embedded in Academia: Pointer Overflow Checking.
+LWN: GCC and pointer overflows.
+
+
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql
new file mode 100644
index 00000000000..6344addd547
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Memory Management/PointerOverflow.ql
@@ -0,0 +1,31 @@
+/**
+ * @name Pointer overflow check
+ * @description Adding a value to a pointer to check if it overflows relies
+ * on undefined behavior and may lead to memory corruption.
+ * @kind problem
+ * @problem.severity error
+ * @precision high
+ * @id cpp/pointer-overflow-check
+ * @tags reliability
+ * security
+ */
+
+import cpp
+private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+private import semmle.code.cpp.commons.Exclusions
+
+from RelationalOperation ro, PointerAddExpr add, Expr expr1, Expr expr2
+where
+ ro.getAnOperand() = add and
+ add.getAnOperand() = expr1 and
+ ro.getAnOperand() = expr2 and
+ globalValueNumber(expr1) = globalValueNumber(expr2) and
+ // Exclude macros but not their arguments
+ not isFromMacroDefinition(ro) and
+ // There must be a compilation of this file without a flag that makes pointer
+ // overflow well defined.
+ exists(Compilation c | c.getAFileCompiled() = ro.getFile() |
+ not c.getAnArgument() = "-fwrapv-pointer" and
+ not c.getAnArgument() = "-fno-strict-overflow"
+ )
+select ro, "Range check relying on pointer overflow."
diff --git a/cpp/ql/src/Likely Bugs/Protocols/TlsSettingsMisconfiguration.qhelp b/cpp/ql/src/Likely Bugs/Protocols/TlsSettingsMisconfiguration.qhelp
new file mode 100644
index 00000000000..03288c39a71
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Protocols/TlsSettingsMisconfiguration.qhelp
@@ -0,0 +1,15 @@
+
+
+
+ Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols may expose the software to known vulnerabilities or permit weak encryption algorithms to be used. Disabling the minimum-recommended protocols is also flagged.
+
+
+
+
+ Boost.Asio documentation.
+
+
+
+
diff --git a/cpp/ql/src/Likely Bugs/Protocols/TlsSettingsMisconfiguration.ql b/cpp/ql/src/Likely Bugs/Protocols/TlsSettingsMisconfiguration.ql
new file mode 100644
index 00000000000..fb7edce038c
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Protocols/TlsSettingsMisconfiguration.ql
@@ -0,0 +1,94 @@
+/**
+ * @name Boost_asio TLS Settings Misconfiguration
+ * @description Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols, or disabling minimum-recommended protocols.
+ * @kind problem
+ * @problem.severity error
+ * @id cpp/boost/tls_settings_misconfiguration
+ * @tags security
+ */
+
+import cpp
+import semmle.code.cpp.security.boostorg.asio.protocols
+
+class ExistsAnyFlowConfig extends DataFlow::Configuration {
+ ExistsAnyFlowConfig() { this = "ExistsAnyFlowConfig" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(BoostorgAsio::SslContextClass c | c.getAContructorCall() = source.asExpr())
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(BoostorgAsio::SslSetOptionsFunction f, FunctionCall fcSetOptions |
+ f.getACallToThisFunction() = fcSetOptions and
+ fcSetOptions.getQualifier() = sink.asExpr()
+ )
+ }
+}
+
+bindingset[flag]
+predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
+ exists(ExistsAnyFlowConfig anyFlowConfig, VariableAccess contextSetOptions |
+ anyFlowConfig.hasFlow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
+ exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
+ contextSetOptions = fcSetOptions.getQualifier() and
+ forall(
+ Expr optionArgument, BoostorgAsio::SslOptionConfig optionArgConfig,
+ Expr optionArgumentSource
+ |
+ optionArgument = fcSetOptions.getArgument(0) and
+ optionArgConfig
+ .hasFlow(DataFlow::exprNode(optionArgumentSource), DataFlow::exprNode(optionArgument))
+ |
+ optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
+ )
+ )
+ )
+}
+
+bindingset[flag]
+predicate isOptionNotSet(ConstructorCall cc, int flag) {
+ not exists(FunctionCall fcSetOptions | isOptionSet(cc, flag, fcSetOptions))
+}
+
+from
+ BoostorgAsio::SslContextCallTlsProtocolConfig configConstructor, Expr protocolSource,
+ Expr protocolSink, ConstructorCall cc, Expr e, string msg
+where
+ configConstructor.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
+ cc.getArgument(0) = protocolSink and
+ (
+ BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
+ not (
+ isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3(), _) and
+ isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
+ isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
+ isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
+ )
+ or
+ BoostorgAsio::isExprTlsBoostProtocol(protocolSource) and
+ not BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
+ not (
+ isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1(), _) and
+ isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1(), _) and
+ isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2())
+ )
+ ) and
+ (
+ BoostorgAsio::isExprSslV23BoostProtocol(protocolSource) and
+ isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoSsl3()) and
+ e = cc and
+ msg = "no_sslv3 has not been set"
+ or
+ isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1()) and
+ e = cc and
+ msg = "no_tlsv1 has not been set"
+ or
+ isOptionNotSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_1()) and
+ e = cc and
+ msg = "no_tlsv1_1 has not been set"
+ or
+ isOptionSet(cc, BoostorgAsio::getShiftedSslOptionsNoTls1_2(), e) and
+ msg = "no_tlsv1_2 was set"
+ )
+select cc, "Usage of $@ with protocol $@ is not configured correctly: The option $@.", cc,
+ "boost::asio::ssl::context::context", protocolSource, protocolSource.toString(), e, msg
diff --git a/cpp/ql/src/Likely Bugs/Protocols/UseOfDeprecatedHardcodedProtocol.qhelp b/cpp/ql/src/Likely Bugs/Protocols/UseOfDeprecatedHardcodedProtocol.qhelp
new file mode 100644
index 00000000000..6f4d2acedd6
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Protocols/UseOfDeprecatedHardcodedProtocol.qhelp
@@ -0,0 +1,16 @@
+
+
+
+ Using boost::asio library but specifying a deprecated hardcoded protocol.
+ Using a deprecated hardcoded protocol instead of negotiting would lock your application to a protocol that has known vulnerabilities or weaknesses.
+
+
+
+
+ Boost.Asio documentation.
+
+
+
+
diff --git a/cpp/ql/src/Likely Bugs/Protocols/UseOfDeprecatedHardcodedProtocol.ql b/cpp/ql/src/Likely Bugs/Protocols/UseOfDeprecatedHardcodedProtocol.ql
new file mode 100644
index 00000000000..b3693ead656
--- /dev/null
+++ b/cpp/ql/src/Likely Bugs/Protocols/UseOfDeprecatedHardcodedProtocol.ql
@@ -0,0 +1,27 @@
+/**
+ * @name boost::asio Use of deprecated hardcoded Protocol
+ * @description Using a deprecated hard-coded protocol using the boost::asio library.
+ * @kind problem
+ * @problem.severity error
+ * @id cpp/boost/use-of-deprecated-hardcoded-security-protocol
+ * @tags security
+ */
+
+import cpp
+import semmle.code.cpp.security.boostorg.asio.protocols
+
+from
+ BoostorgAsio::SslContextCallConfig config, Expr protocolSource, Expr protocolSink,
+ ConstructorCall cc
+where
+ config.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink)) and
+ not exists(BoostorgAsio::SslContextCallTlsProtocolConfig tlsConfig |
+ tlsConfig.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink))
+ ) and
+ cc.getArgument(0) = protocolSink and
+ exists(BoostorgAsio::SslContextCallBannedProtocolConfig bannedConfig |
+ bannedConfig.hasFlow(DataFlow::exprNode(protocolSource), DataFlow::exprNode(protocolSink))
+ )
+select protocolSink, "Usage of $@ specifying a deprecated hardcoded protocol $@ in function $@.",
+ cc, "boost::asio::ssl::context::context", protocolSource, protocolSource.toString(),
+ cc.getEnclosingFunction(), cc.getEnclosingFunction().toString()
diff --git a/cpp/ql/src/Microsoft/IgnoreReturnValueSAL.ql b/cpp/ql/src/Microsoft/IgnoreReturnValueSAL.ql
index 64f398fa855..fb25a93963d 100644
--- a/cpp/ql/src/Microsoft/IgnoreReturnValueSAL.ql
+++ b/cpp/ql/src/Microsoft/IgnoreReturnValueSAL.ql
@@ -7,6 +7,10 @@
* @id cpp/ignore-return-value-sal
* @problem.severity warning
* @tags reliability
+ * external/cwe/cwe-573
+ * external/cwe/cwe-252
+ * @opaque-id SM02344
+ * @microsoft.severity Important
*/
import SAL
diff --git a/cpp/ql/src/Microsoft/SAL.qll b/cpp/ql/src/Microsoft/SAL.qll
index b9f6c2d037d..963a726ae54 100644
--- a/cpp/ql/src/Microsoft/SAL.qll
+++ b/cpp/ql/src/Microsoft/SAL.qll
@@ -2,29 +2,45 @@ import cpp
class SALMacro extends Macro {
SALMacro() {
- this.getFile().getBaseName() = "sal.h" or
- this.getFile().getBaseName() = "specstrings_strict.h" or
- this.getFile().getBaseName() = "specstrings.h"
+ 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
+ (
+ // Dialect for Windows 8 and above
+ this.getName().matches("\\_%\\_")
+ or
+ // Dialect for Windows 7
+ this.getName().matches("\\_\\_%")
+ )
}
}
+pragma[noinline]
+predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
+
class SALAnnotation extends MacroInvocation {
SALAnnotation() {
this.getMacro() instanceof SALMacro and
- not exists(this.getParentInvocation())
+ isTopLevelMacroAccess(this)
}
/** Returns the `Declaration` annotated by `this`. */
- Declaration getDeclaration() { annotatesAt(this, result.getADeclarationEntry(), _, _) }
+ Declaration getDeclaration() {
+ annotatesAt(this, result.getADeclarationEntry(), _, _) and
+ not result instanceof Type // exclude typedefs
+ }
/** Returns the `DeclarationEntry` annotated by `this`. */
- DeclarationEntry getDeclarationEntry() { annotatesAt(this, result, _, _) }
+ DeclarationEntry getDeclarationEntry() {
+ annotatesAt(this, result, _, _) and
+ not result instanceof TypeDeclarationEntry // exclude typedefs
+ }
}
-/*
- * Particular SAL annotations of interest
- */
-
class SALCheckReturn extends SALAnnotation {
SALCheckReturn() {
exists(SALMacro m | m = this.getMacro() |
@@ -39,8 +55,8 @@ class SALNotNull extends SALAnnotation {
exists(SALMacro m | m = this.getMacro() |
not m.getName().matches("%\\_opt\\_%") and
(
- m.getName().matches("\\_In%") or
- m.getName().matches("\\_Out%") or
+ m.getName().matches("_In%") or
+ m.getName().matches("_Out%") or
m.getName() = "_Ret_notnull_"
)
) and
@@ -63,42 +79,124 @@ class SALMaybeNull extends SALAnnotation {
}
}
-/*
- * Implementation details
+///////////////////////////////////////////////////////////////////////////////
+// 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.
*/
-
-private predicate annotatesAt(SALAnnotation a, DeclarationEntry e, File file, int idx) {
- a = salElementAt(file, idx) and
- (
- // Base case: `a` right before `e`
- e = salElementAt(file, idx + 1)
- or
- // Recursive case: `a` right before some annotation on `e`
- annotatesAt(_, e, file, idx + 1)
- )
-}
-
-library class SALElement extends Element {
- SALElement() {
- this instanceof DeclarationEntry or
- this instanceof SALAnnotation
- }
-}
-
-/** Gets the `idx`th `SALElement` in `file`. */
-private SALElement salElementAt(File file, int idx) {
- interestingLoc(file, result, interestingStartPos(file, idx))
+predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
+ annotatesAtPosition(a.(SALElement).getStartPosition(), d, file, idx)
}
/**
- * Holds if an SALElement element at character `result` comes at
- * position `idx` in `file`.
+ * Holds if `pos` is the `idx`th position in `file` that holds a SAL element,
+ * which annotates the declaration entry `d` (by occurring before it without
+ * any other declaration entries in between).
*/
-private int interestingStartPos(File file, int idx) {
- result = rank[idx](int otherStart | interestingLoc(file, _, otherStart))
+// For performance reasons, do not mention the annotation itself here,
+// but compute with positions instead. This performs better on databases
+// with many annotations at the same position.
+private predicate annotatesAtPosition(SALPosition pos, DeclarationEntry d, File file, int idx) {
+ pos = salRelevantPositionAt(file, idx) and
+ salAnnotationPos(pos) and
+ (
+ // Base case: `pos` right before `d`
+ d.(SALElement).getStartPosition() = salRelevantPositionAt(file, idx + 1)
+ or
+ // Recursive case: `pos` right before some annotation on `d`
+ annotatesAtPosition(_, d, file, idx + 1)
+ )
}
-/** Holds if `element` in `file` is at character `startPos`. */
-private predicate interestingLoc(File file, SALElement element, int startPos) {
- element.getLocation().charLoc(file, startPos, _)
+/**
+ * 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.
+ */
+library class SALElement extends Element {
+ SALElement() {
+ containsSALAnnotation(this.(DeclarationEntry).getFile()) or
+ this instanceof SALAnnotation
+ }
+
+ predicate hasStartPosition(File file, int line, int col) {
+ exists(Location loc | loc = this.getLocation() |
+ file = loc.getFile() and
+ line = loc.getStartLine() and
+ col = loc.getStartColumn()
+ )
+ }
+
+ predicate hasEndPosition(File file, int line, int col) {
+ exists(Location loc |
+ loc = this.(FunctionDeclarationEntry).getBlock().getLocation()
+ or
+ this = any(VariableDeclarationEntry vde |
+ vde.isDefinition() and
+ loc = vde.getVariable().getInitializer().getLocation()
+ )
+ |
+ file = loc.getFile() and
+ line = loc.getEndLine() and
+ col = loc.getEndColumn()
+ )
+ }
+
+ SALPosition getStartPosition() {
+ exists(File file, int line, int col |
+ this.hasStartPosition(file, line, col) and
+ result = MkSALPosition(file, line, col)
+ )
+ }
+}
+
+/** Holds if `file` contains a SAL annotation. */
+pragma[noinline]
+private predicate containsSALAnnotation(File file) { any(SALAnnotation a).getFile() = file }
+
+/**
+ * A source-file position of a `SALElement`. Unlike location, this denotes a
+ * point in the file rather than a range.
+ */
+private newtype SALPosition =
+ MkSALPosition(File file, int line, int col) {
+ exists(SALElement e |
+ e.hasStartPosition(file, line, col)
+ or
+ e.hasEndPosition(file, line, col)
+ )
+ }
+
+/** Holds if `pos` is the start position of a SAL annotation. */
+pragma[noinline]
+private predicate salAnnotationPos(SALPosition pos) {
+ any(SALAnnotation a).(SALElement).getStartPosition() = pos
+}
+
+/**
+ * Gets the `idx`th position in `file` that holds a SAL element,
+ * ordering positions lexicographically by their start line and start column.
+ */
+private SALPosition salRelevantPositionAt(File file, int idx) {
+ result = rank[idx](SALPosition pos, int line, int col |
+ pos = MkSALPosition(file, line, col)
+ |
+ pos order by line, col
+ )
}
diff --git a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql
index 096b1468bb9..42a29b96268 100644
--- a/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql
+++ b/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql
@@ -34,8 +34,10 @@ class FileFunction extends FunctionWithWrappers {
nme.matches("CreateFile%")
)
or
+ this.hasQualifiedName("std", "fopen")
+ or
// on any of the fstream classes, or filebuf
- exists(string nme | this.getDeclaringType().getSimpleName() = nme |
+ exists(string nme | this.getDeclaringType().hasQualifiedName("std", nme) |
nme = "basic_fstream" or
nme = "basic_ifstream" or
nme = "basic_ofstream" or
diff --git a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.qhelp b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.qhelp
index ccd297c3b36..4ad7a40fed6 100644
--- a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.qhelp
+++ b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.qhelp
@@ -34,7 +34,7 @@ characters before writing to the HTML page.
OWASP:
-XSS
+XSS
(Cross Site Scripting) Prevention Cheat Sheet.
diff --git a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql
index 4469517c369..8b7fb83df81 100644
--- a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql
+++ b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.ql
@@ -17,8 +17,8 @@ import semmle.code.cpp.security.TaintTracking
/** A call that prints its arguments to `stdout`. */
class PrintStdoutCall extends FunctionCall {
PrintStdoutCall() {
- getTarget().hasGlobalName("puts") or
- getTarget().hasGlobalName("printf")
+ getTarget().hasGlobalOrStdName("puts") or
+ getTarget().hasGlobalOrStdName("printf")
}
}
diff --git a/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql
index 575752f0b74..ff17daca05c 100644
--- a/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql
+++ b/cpp/ql/src/Security/CWE/CWE-131/NoSpaceForZeroTerminator.ql
@@ -19,10 +19,7 @@ import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.models.implementations.Memcpy
class MallocCall extends FunctionCall {
- MallocCall() {
- this.getTarget().hasGlobalName("malloc") or
- this.getTarget().hasQualifiedName("std", "malloc")
- }
+ MallocCall() { this.getTarget().hasGlobalOrStdName("malloc") }
Expr getAllocatedSize() {
if this.getArgument(0) instanceof VariableAccess
diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql
index c704506a4fa..92f679a0f1f 100644
--- a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql
+++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql
@@ -5,7 +5,7 @@
* @id cpp/comparison-with-wider-type
* @kind problem
* @problem.severity warning
- * @precision medium
+ * @precision high
* @tags reliability
* security
* external/cwe/cwe-190
diff --git a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.qhelp b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.qhelp
new file mode 100644
index 00000000000..8e6a8903483
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.qhelp
@@ -0,0 +1,59 @@
+
+
+
+
+A common pattern is to initialize a local variable by calling another function (an
+"initialization" function) with the address of the local variable as a pointer argument. That
+function is then responsible for writing to the memory location referenced by the pointer.
+
+In some cases, the called function may not always write to the memory pointed to by the
+pointer argument. In such cases, the function will typically return a "status" code, informing the
+caller as to whether the initialization succeeded or not. If the caller does not check the status
+code before reading the local variable, it may read unitialized memory, which can result in
+unexpected behavior.
+
+
+
+
+When using a initialization function that does not guarantee to initialize the memory pointed to
+by the passed pointer, and returns a status code to indicate whether such initialization occurred,
+the status code should be checked before reading from the local variable.
+
+
+
+
+In this hypothetical example we have code for managing a series of devices. The code
+includes a DeviceConfig struct that can represent properties about each device.
+The initDeviceConfig function can be called to initialize one of these structures, by
+providing a "device number", which can be used to look up the appropriate properties in some data
+store. If an invalid device number is provided, the function returns a status code of
+-1, and does not initialize the provided pointer.
+
+In the first code sample below, the notify function calls the
+initDeviceConfig function with a pointer to the local variable config,
+which is then subsequently accessed to fetch properties of the device. However, the code does not
+check the return value from the function call to initDeviceConfig. If the
+device number passed to the notify function was invalid, the
+initDeviceConfig function will leave the config variable uninitialized,
+which will result in the notify function accessing uninitialized memory.
+
+
+
+To fix this, the code needs to check that the return value of the call to
+initDeviceConfig is zero. If that is true, then the calling code can safely assume
+that the local variable has been initialized.
+
+
+
+
+
+
+
+ Wikipedia:
+ Uninitialized variable.
+
+
+
+
\ No newline at end of file
diff --git a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql
new file mode 100644
index 00000000000..f9eb2fe5400
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql
@@ -0,0 +1,33 @@
+/**
+ * @name Conditionally uninitialized variable
+ * @description When an initialization function is used to initialize a local variable, but the
+ * returned status code is not checked, the variable may be left in an uninitialized
+ * state, and reading the variable may result in undefined behavior.
+ * @kind problem
+ * @problem.severity warning
+ * @opaque-id SM02313
+ * @id cpp/conditionally-uninitialized-variable
+ * @tags security
+ * external/cwe/cwe-457
+ */
+
+import cpp
+import semmle.code.cpp.controlflow.SSA
+private import UninitializedVariables
+
+from
+ ConditionallyInitializedVariable v, ConditionalInitializationFunction f,
+ ConditionalInitializationCall call, string defined, Evidence e
+where
+ exists(v.getARiskyAccess(f, call, e)) and
+ (
+ if e = DefinitionInSnapshot()
+ then defined = ""
+ else
+ if e = SuggestiveSALAnnotation()
+ then defined = "externally defined (SAL) "
+ else defined = "externally defined (CSV) "
+ )
+select call,
+ "The status of this call to " + defined +
+ "$@ is not checked, potentially leaving $@ uninitialized.", f, f.getName(), v, v.getName()
diff --git a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableBad.c b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableBad.c
new file mode 100644
index 00000000000..73a01c2d900
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableBad.c
@@ -0,0 +1,25 @@
+struct DeviceConfig {
+ bool isEnabled;
+ int channel;
+};
+
+int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
+ if (deviceNumber >= getMaxDevices()) {
+ // No device with that number, return -1 to indicate failure
+ return -1;
+ }
+ // Device with that number, fetch parameters and initialize struct
+ ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
+ ref->channel = fetchDeviceChannel(deviceNumber);
+ // Return 0 to indicate success
+ return 0;
+}
+
+int notify(int deviceNumber) {
+ DeviceConfig config;
+ initDeviceConfig(&config, deviceNumber);
+ // BAD: Using config without checking the status code that is returned
+ if (config.isEnabled) {
+ notifyChannel(config.channel);
+ }
+}
diff --git a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableGood.c b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableGood.c
new file mode 100644
index 00000000000..ced43a66cfc
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableGood.c
@@ -0,0 +1,27 @@
+struct DeviceConfig {
+ bool isEnabled;
+ int channel;
+};
+
+int initDeviceConfig(DeviceConfig *ref, int deviceNumber) {
+ if (deviceNumber >= getMaxDevices()) {
+ // No device with that number, return -1 to indicate failure
+ return -1;
+ }
+ // Device with that number, fetch parameters and initialize struct
+ ref->isEnabled = fetchIsDeviceEnabled(deviceNumber);
+ ref->channel = fetchDeviceChannel(deviceNumber);
+ // Return 0 to indicate success
+ return 0;
+}
+
+void notify(int deviceNumber) {
+ DeviceConfig config;
+ int statusCode = initDeviceConfig(&config, deviceNumber);
+ if (statusCode == 0) {
+ // GOOD: Status code returned by initialization function is checked, so this is safe
+ if (config.isEnabled) {
+ notifyChannel(config.channel);
+ }
+ }
+}
diff --git a/cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll b/cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll
new file mode 100644
index 00000000000..8f2950c9123
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll
@@ -0,0 +1,705 @@
+/**
+ * Provides classes and predicates for identifying functions that initialize their arguments.
+ */
+
+import cpp
+import external.ExternalArtifact
+private import semmle.code.cpp.dispatch.VirtualDispatchPrototype
+import semmle.code.cpp.NestedFields
+import Microsoft.SAL
+import semmle.code.cpp.controlflow.Guards
+
+/** A context under which a function may be called. */
+private newtype TContext =
+ /** No specific call context. */
+ NoContext() or
+ /**
+ * The call context is that the given other parameter is null.
+ *
+ * This context is created for all parameters that are null checked in the body of the function.
+ */
+ ParamNull(Parameter p) { p = any(ParameterNullCheck pnc).getParameter() } or
+ /**
+ * The call context is that the given other parameter is not null.
+ *
+ * This context is created for all parameters that are null checked in the body of the function.
+ */
+ ParamNotNull(Parameter p) { p = any(ParameterNullCheck pnc).getParameter() }
+
+/**
+ * A context under which a function may be called.
+ *
+ * Some functions may conditionally initialize a parameter depending on the value of another
+ * parameter. Consider:
+ * ```
+ * int MyInitFunction(int* paramToBeInitialized, int* paramToCheck) {
+ * if (!paramToCheck) {
+ * // fail!
+ * return -1;
+ * }
+ * paramToBeInitialized = 0;
+ * }
+ * ```
+ * In this case, whether `paramToBeInitialized` is initialized when this function call completes
+ * depends on whether `paramToCheck` is or is not null. A call-context insensitive analysis will
+ * determine that any call to this function may leave the parameter uninitialized, even if the
+ * argument to paramToCheck is guaranteed to be non-null (`&foo`, for example).
+ *
+ * This class models call contexts that can be considered when calculating whether a given parameter
+ * initializes or not. The supported contexts are:
+ * - `ParamNull(otherParam)` - the given `otherParam` is considered to be null. Applies when
+ * exactly one parameter other than this one is null checked.
+ * - `ParamNotNull(otherParam)` - the given `otherParam` is considered to be not null. Applies when
+ * exactly one parameter other than this one is null checked.
+ * - `NoContext()` - applies in all other circumstances.
+ */
+class Context extends TContext {
+ string toString() {
+ this = NoContext() and result = "NoContext"
+ or
+ this = ParamNull(any(Parameter p | result = "ParamNull(" + p.getName() + ")"))
+ or
+ this = ParamNotNull(any(Parameter p | result = "ParamNotNull(" + p.getName() + ")"))
+ }
+}
+
+/**
+ * A check against a parameter.
+ */
+abstract class ParameterCheck extends Expr {
+ /**
+ * Gets a successor of this check that should be ignored for the given context.
+ */
+ abstract ControlFlowNode getIgnoredSuccessorForContext(Context c);
+}
+
+/** A null-check expression on a parameter. */
+class ParameterNullCheck extends ParameterCheck {
+ Parameter p;
+ ControlFlowNode nullSuccessor;
+ ControlFlowNode notNullSuccessor;
+
+ ParameterNullCheck() {
+ this.isCondition() and
+ p.getFunction() instanceof InitializationFunction and
+ p.getType().getUnspecifiedType() instanceof PointerType and
+ exists(VariableAccess va | va = p.getAnAccess() |
+ nullSuccessor = getATrueSuccessor() and
+ notNullSuccessor = getAFalseSuccessor() and
+ (
+ va = this.(NotExpr).getOperand() or
+ va = any(EQExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
+ va = getCheckedFalseCondition(this) or
+ va = any(NEExpr eq |
+ eq = getCheckedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
+ ).getAnOperand()
+ )
+ or
+ nullSuccessor = getAFalseSuccessor() and
+ notNullSuccessor = getATrueSuccessor() and
+ (
+ va = this or
+ va = any(NEExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or
+ va = any(EQExpr eq |
+ eq = getCheckedFalseCondition(this) and eq.getAnOperand().getValue() = "0"
+ ).getAnOperand()
+ )
+ )
+ }
+
+ /** The parameter being null-checked. */
+ Parameter getParameter() { result = p }
+
+ override ControlFlowNode getIgnoredSuccessorForContext(Context c) {
+ c = ParamNull(p) and result = notNullSuccessor
+ or
+ c = ParamNotNull(p) and result = nullSuccessor
+ }
+
+ /** The successor at which the parameter is confirmed to be null. */
+ ControlFlowNode getNullSuccessor() { result = nullSuccessor }
+
+ /** The successor at which the parameter is confirmed to be not-null. */
+ ControlFlowNode getNotNullSuccessor() { result = notNullSuccessor }
+}
+
+/**
+ * An entry in a CSV file in cond-init that contains externally defined functions that are
+ * conditional initializers. These files are typically produced by running the
+ * ConditionallyInitializedFunction companion query.
+ */
+class ValidatedExternalCondInitFunction extends ExternalData {
+ ValidatedExternalCondInitFunction() { this.getDataPath().matches("%cond-init%.csv") }
+
+ predicate isExternallyVerified(Function f, int param) {
+ functionSignature(f, getField(1), getField(2)) and param = getFieldAsInt(3)
+ }
+}
+
+/**
+ * The type of evidence used to determine whether a function initializes a parameter.
+ */
+newtype Evidence =
+ /**
+ * The function is defined in the snapshot, and the CFG has been analyzed to determine that the
+ * parameter is not initialized on at least one path to the exit.
+ */
+ DefinitionInSnapshot() or
+ /**
+ * The function is externally defined, but the parameter has an `_out` SAL annotation which
+ * suggests that it is initialized in the function.
+ */
+ SuggestiveSALAnnotation() or
+ /**
+ * We have been given a CSV file which indicates this parameter is conditionally initialized.
+ */
+ ExternalEvidence()
+
+/**
+ * A call to an function which initializes one or more of its parameters.
+ */
+class InitializationFunctionCall extends FunctionCall {
+ Expr initializedArgument;
+
+ InitializationFunctionCall() { initializedArgument = getAnInitializedArgument(this) }
+
+ /** Gets a parameter that is initialized by this call. */
+ Parameter getAnInitParameter() { result.getAnAccess() = initializedArgument }
+}
+
+/**
+ * A variable access which is dereferenced then assigned to.
+ */
+private predicate isPointerDereferenceAssignmentTarget(VariableAccess target) {
+ target.getParent().(PointerDereferenceExpr) = any(Assignment e).getLValue()
+}
+
+/**
+ * A function which initializes one or more of its parameters.
+ */
+class InitializationFunction extends Function {
+ int i;
+ Evidence evidence;
+
+ InitializationFunction() {
+ evidence = DefinitionInSnapshot() and
+ (
+ // Assignment by pointer dereferencing the parameter
+ isPointerDereferenceAssignmentTarget(this.getParameter(i).getAnAccess()) or
+ // Field wise assignment to the parameter
+ any(Assignment e).getLValue() = getAFieldAccess(this.getParameter(i)) or
+ i = this
+ .(MemberFunction)
+ .getAnOverridingFunction+()
+ .(InitializationFunction)
+ .initializedParameter() or
+ getParameter(i) = any(InitializationFunctionCall c).getAnInitParameter()
+ )
+ or
+ // If we have no definition, we look at SAL annotations
+ not this.isDefined() and
+ this.getParameter(i).(SALParameter).isOut() and
+ evidence = SuggestiveSALAnnotation()
+ or
+ // We have some external information that this function conditionally initializes
+ not this.isDefined() and
+ any(ValidatedExternalCondInitFunction vc).isExternallyVerified(this, i) and
+ evidence = ExternalEvidence()
+ }
+
+ /** Gets a parameter index which is initialized by this function. */
+ int initializedParameter() { result = i }
+
+ /** Gets a `ControlFlowNode` which assigns a new value to the parameter with the given index. */
+ ControlFlowNode paramReassignment(int index) {
+ index = i and
+ (
+ result = this.getParameter(i).getAnAccess() and
+ (
+ result = any(Assignment a).getLValue().(PointerDereferenceExpr).getOperand()
+ or
+ // Field wise assignment to the parameter
+ result = any(Assignment a).getLValue().(FieldAccess).getQualifier()
+ or
+ // Assignment to a nested field of the parameter
+ result = any(Assignment a).getLValue().(NestedFieldAccess).getUltimateQualifier()
+ or
+ result = getAnInitializedArgument(any(Call c))
+ or
+ exists(IfStmt check | result = check.getCondition().getAChild*() |
+ paramReassignmentCondition(check)
+ )
+ )
+ or
+ result = any(AssumeExpr e |
+ e.getEnclosingFunction() = this and e.getAChild().(Literal).getValue() = "0"
+ )
+ )
+ }
+
+ /**
+ * Helper predicate: holds if the `if` statement `check` contains a
+ * reassignment to the `i`th parameter within its `then` statement.
+ */
+ pragma[noinline]
+ private predicate paramReassignmentCondition(IfStmt check) {
+ this.paramReassignment(i).getEnclosingStmt().getParentStmt*() = check.getThen()
+ }
+
+ /** Holds if `n` can be reached without the parameter at `index` being reassigned. */
+ predicate paramNotReassignedAt(ControlFlowNode n, int index, Context c) {
+ c = getAContext(index) and
+ (
+ not exists(this.getEntryPoint()) and index = i and n = this
+ or
+ n = this.getEntryPoint() and index = i
+ or
+ exists(ControlFlowNode mid | paramNotReassignedAt(mid, index, c) |
+ n = mid.getASuccessor() and
+ not n = paramReassignment(index) and
+ /*
+ * Ignore successor edges where the parameter is null, because it is then confirmed to be
+ * initialized.
+ */
+
+ not exists(ParameterNullCheck nullCheck |
+ nullCheck = mid and
+ nullCheck = getANullCheck(index) and
+ n = nullCheck.getNullSuccessor()
+ ) and
+ /*
+ * Ignore successor edges which are excluded by the given context
+ */
+
+ not exists(ParameterCheck paramCheck | paramCheck = mid |
+ n = paramCheck.getIgnoredSuccessorForContext(c)
+ )
+ )
+ )
+ }
+
+ /** Gets a null-check on the parameter at `index`. */
+ private ParameterNullCheck getANullCheck(int index) {
+ getParameter(index) = result.getParameter()
+ }
+
+ /** Gets a parameter which is not at the given index. */
+ private Parameter getOtherParameter(int index) {
+ index = i and
+ result = getAParameter() and
+ not result.getIndex() = index
+ }
+
+ /**
+ * Gets a call `Context` that is applicable when considering whether parameter at the `index` can
+ * be conditionally initialized.
+ */
+ Context getAContext(int index) {
+ index = i and
+ /*
+ * If there is one and only one other parameter which is null checked in the body of the method,
+ * then we have two contexts to consider - that the other param is null, or that the other param
+ * is not null.
+ */
+
+ if
+ strictcount(Parameter p |
+ exists(Context c | c = ParamNull(p) or c = ParamNotNull(p)) and
+ p = getOtherParameter(index)
+ ) = 1
+ then
+ exists(Parameter p | p = getOtherParameter(index) |
+ result = ParamNull(p) or result = ParamNotNull(p)
+ )
+ else
+ // Otherwise, only consider NoContext.
+ result = NoContext()
+ }
+
+ /**
+ * Holds if this function should be whitelisted - that is, not considered as conditionally
+ * initializing its parameters.
+ */
+ predicate whitelisted() {
+ exists(string name | this.hasName(name) |
+ // Return value is not a success code but the output functions never fail.
+ name.matches("_Interlocked%")
+ or
+ // Functions that never fail, according to MSDN.
+ name = "QueryPerformanceCounter"
+ or
+ name = "QueryPerformanceFrequency"
+ or
+ // Functions that never fail post-Vista, according to MSDN.
+ name = "InitializeCriticalSectionAndSpinCount"
+ or
+ // `rand_s` writes 0 to a non-null argument if it fails, according to MSDN.
+ name = "rand_s"
+ or
+ // IntersectRect initializes the argument regardless of whether the input intersects
+ name = "IntersectRect"
+ or
+ name = "SetRect"
+ or
+ name = "UnionRect"
+ or
+ // These functions appears to have an incorrect CFG, which leads to false positives
+ name = "PhysicalToLogicalDPIPoint"
+ or
+ name = "LogicalToPhysicalDPIPoint"
+ or
+ // Sets NtProductType to default on error
+ name = "RtlGetNtProductType"
+ or
+ // Our CFG is not sophisticated enough to detect that the argument is always initialized
+ name = "StringCchLengthA"
+ or
+ // All paths init the argument, and always returns SUCCESS.
+ name = "RtlUnicodeToMultiByteSize"
+ or
+ // All paths init the argument, and always returns SUCCESS.
+ name = "RtlMultiByteToUnicodeSize"
+ or
+ // All paths init the argument, and always returns SUCCESS.
+ name = "RtlUnicodeToMultiByteN"
+ or
+ // Always initializes argument
+ name = "RtlGetFirstRange"
+ or
+ // Destination range is zeroed out on failure, assuming first two parameters are valid
+ name = "memcpy_s"
+ or
+ // This zeroes the memory unconditionally
+ name = "SeCreateAccessState"
+ )
+ }
+}
+
+/**
+ * A function which initializes one or more of its parameters, but not on all paths.
+ */
+class ConditionalInitializationFunction extends InitializationFunction {
+ Context c;
+
+ ConditionalInitializationFunction() {
+ c = this.getAContext(i) and
+ not this.whitelisted() and
+ exists(Type status | status = this.getType().getUnspecifiedType() |
+ status instanceof IntegralType or
+ status instanceof Enum
+ ) and
+ not this.getType().getName().toLowerCase() = "size_t" and
+ (
+ /*
+ * If there is no definition, consider this to be conditionally initializing (based on either
+ * SAL or external data).
+ */
+
+ not evidence = DefinitionInSnapshot()
+ or
+ /*
+ * If this function is defined in this snapshot, then it conditionally initializes if there
+ * is at least one path through the function which doesn't initialize the parameter.
+ *
+ * Explicitly ignore pure virtual functions.
+ */
+
+ this.isDefined() and
+ this.paramNotReassignedAt(this, i, c) and
+ not this instanceof PureVirtualFunction
+ )
+ }
+
+ /** Gets the evidence associated with the given parameter. */
+ Evidence getEvidence(int param) {
+ /*
+ * Note: due to the way the predicate dispatch interacts with fields, this needs to be
+ * implemented on this class, not `InitializationFunction`. If implemented on the latter it
+ * can return evidence that does not result in conditional initialization.
+ */
+
+ param = i and evidence = result
+ }
+
+ /** Gets the index of a parameter which is conditionally initialized. */
+ int conditionallyInitializedParameter(Context context) { result = i and context = c }
+}
+
+/**
+ * More elaborate tracking, flagging cases where the status is checked after
+ * the potentially uninitialized variable has been used, and ignoring cases
+ * where the status is not checked but there is no use of the potentially
+ * uninitialized variable, may be obtained via `getARiskyAccess`.
+ */
+class ConditionalInitializationCall extends FunctionCall {
+ ConditionalInitializationFunction target;
+
+ ConditionalInitializationCall() { target = getTarget(this) }
+
+ /** Gets the argument passed for the given parameter to this call. */
+ Expr getArgumentForParameter(Parameter p) {
+ p = getTarget().getAParameter() and
+ result = getArgument(p.getIndex())
+ }
+
+ /**
+ * Gets an argument conditionally initialized by this call.
+ */
+ Expr getAConditionallyInitializedArgument(ConditionalInitializationFunction condTarget, Evidence e) {
+ condTarget = target and
+ exists(Context context |
+ result = getAConditionallyInitializedArgument(this, condTarget, context, e)
+ |
+ context = NoContext()
+ or
+ exists(Parameter otherP, Expr otherArg |
+ context = ParamNotNull(otherP) or
+ context = ParamNull(otherP)
+ |
+ otherArg = getArgumentForParameter(otherP) and
+ (otherArg instanceof AddressOfExpr implies context = ParamNotNull(otherP)) and
+ (otherArg.getType() instanceof ArrayType implies context = ParamNotNull(otherP)) and
+ (otherArg.getValue() = "0" implies context = ParamNull(otherP))
+ )
+ )
+ }
+
+ VariableAccess getAConditionallyInitializedVariable() {
+ not result.getTarget().getAnAssignedValue().getASuccessor+() = result and
+ // Should not be assigned field-wise prior to the call.
+ not exists(Assignment a, FieldAccess fa |
+ fa.getQualifier() = result.getTarget().getAnAccess() and
+ a.getLValue() = fa and
+ fa.getASuccessor+() = result
+ ) and
+ result = this
+ .getArgument(getTarget(this)
+ .(ConditionalInitializationFunction)
+ .conditionallyInitializedParameter(_))
+ .(AddressOfExpr)
+ .getOperand()
+ }
+
+ Variable getStatusVariable() {
+ exists(AssignExpr a | a.getLValue() = result.getAnAccess() | a.getRValue() = this)
+ or
+ result.getInitializer().getExpr() = this
+ }
+
+ Expr getSuccessCheck() {
+ exists(this.getAFalseSuccessor()) and result = this
+ or
+ result = this.getParent() and
+ (
+ result instanceof NotExpr or
+ result.(EQExpr).getAnOperand().getValue() = "0" or
+ result.(GEExpr).getLesserOperand().getValue() = "0"
+ )
+ }
+
+ Expr getFailureCheck() {
+ result = this.getParent() and
+ (
+ result instanceof NotExpr or
+ result.(NEExpr).getAnOperand().getValue() = "0" or
+ result.(LTExpr).getLesserOperand().getValue() = "0"
+ )
+ }
+
+ private predicate inCheckedContext() {
+ exists(Call parent | this = parent.getAnArgument() |
+ parent.getTarget() instanceof Operator or
+ parent.getTarget().hasName("VerifyOkCatastrophic")
+ )
+ }
+
+ ControlFlowNode uncheckedReaches(LocalVariable var) {
+ (
+ not exists(var.getInitializer()) and
+ var = this.getAConditionallyInitializedVariable().getTarget() and
+ if exists(this.getFailureCheck())
+ then result = this.getFailureCheck().getATrueSuccessor()
+ else
+ if exists(this.getSuccessCheck())
+ then result = this.getSuccessCheck().getAFalseSuccessor()
+ else (
+ result = this.getASuccessor() and not this.inCheckedContext()
+ )
+ )
+ or
+ exists(ControlFlowNode mid | mid = uncheckedReaches(var) |
+ not mid = getStatusVariable().getAnAccess() and
+ not mid = var.getAnAccess() and
+ not exists(VariableAccess write | result = write and write = var.getAnAccess() |
+ write = any(AssignExpr a).getLValue() or
+ write = any(AddressOfExpr a).getOperand()
+ ) and
+ result = mid.getASuccessor()
+ )
+ }
+
+ VariableAccess getARiskyRead(Function f) {
+ f = this.getTarget() and
+ exists(this.getFile().getRelativePath()) and
+ result = this.uncheckedReaches(result.getTarget()) and
+ not this.(GuardCondition).controls(result.getBasicBlock(), _)
+ }
+}
+
+/**
+ * Gets the position of an argument to the call which is initialized by the call.
+ */
+pragma[nomagic]
+int initializedArgument(Call call) {
+ exists(InitializationFunction target |
+ target = getTarget(call) and
+ result = target.initializedParameter()
+ )
+}
+
+/**
+ * Gets an argument which is initialized by the call.
+ */
+Expr getAnInitializedArgument(Call call) { result = call.getArgument(initializedArgument(call)) }
+
+/**
+ * Gets the position of an argument to the call to the target which is conditionally initialized by
+ * the call, under the given context and evidence.
+ */
+pragma[nomagic]
+private int conditionallyInitializedArgument(
+ Call call, ConditionalInitializationFunction target, Context c, Evidence e
+) {
+ target = getTarget(call) and
+ c = target.getAContext(result) and
+ e = target.getEvidence(result) and
+ result = target.conditionallyInitializedParameter(c)
+}
+
+/**
+ * Gets an argument which is conditionally initialized by the call to the given target under the given context and evidence.
+ */
+Expr getAConditionallyInitializedArgument(
+ Call call, ConditionalInitializationFunction target, Context c, Evidence e
+) {
+ result = call.getArgument(conditionallyInitializedArgument(call, target, c, e))
+}
+
+/**
+ * Gets the type signature for the functions parameters.
+ */
+private string typeSig(Function f) {
+ result = concat(int i, Type pt |
+ pt = f.getParameter(i).getType()
+ |
+ pt.getUnspecifiedType().toString(), "," order by i
+ )
+}
+
+/**
+ * Holds where qualifiedName and typeSig make up the signature for the function.
+ */
+private predicate functionSignature(Function f, string qualifiedName, string typeSig) {
+ qualifiedName = f.getQualifiedName() and
+ typeSig = typeSig(f)
+}
+
+/**
+ * Gets a possible definition for the undefined function by matching the undefined function name
+ * and parameter arity with a defined function.
+ *
+ * This is useful for identifying call to target dependencies across libraries, where the libraries
+ * are never statically linked together.
+ */
+private Function getAPossibleDefinition(Function undefinedFunction) {
+ not undefinedFunction.isDefined() and
+ exists(string qn, string typeSig |
+ functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig)
+ ) and
+ result.isDefined()
+}
+
+/**
+ * Helper predicate for `getTarget`, that computes possible targets of a `Call`.
+ *
+ * If there is at least one defined target after performing some simple virtual dispatch
+ * resolution, then the result is all the defined targets.
+ */
+private Function getTarget1(Call c) {
+ result = VirtualDispatch::getAViableTarget(c) and
+ result.isDefined()
+}
+
+/**
+ * Helper predicate for `getTarget`, that computes possible targets of a `Call`.
+ *
+ * If we can use the heuristic matching of functions to find definitions for some of the viable
+ * targets, return those.
+ */
+private Function getTarget2(Call c) {
+ not exists(getTarget1(c)) and
+ result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))
+}
+
+/**
+ * Helper predicate for `getTarget`, that computes possible targets of a `Call`.
+ *
+ * Otherwise, the result is the undefined `Function` instances.
+ */
+private Function getTarget3(Call c) {
+ not exists(getTarget1(c)) and
+ not exists(getTarget2(c)) and
+ result = VirtualDispatch::getAViableTarget(c)
+}
+
+/**
+ * Gets a possible target for the `Call`, using the name and parameter matching if we did not associate
+ * this call with a specific definition at link or compile time, and performing simple virtual
+ * dispatch resolution.
+ */
+Function getTarget(Call c) {
+ result = getTarget1(c) or
+ result = getTarget2(c) or
+ result = getTarget3(c)
+}
+
+/**
+ * Get an access of a field on `Variable` v.
+ */
+FieldAccess getAFieldAccess(Variable v) {
+ exists(VariableAccess va, Expr qualifierExpr |
+ // Find an access of the variable, or an AddressOfExpr that has the access
+ va = v.getAnAccess() and
+ (
+ qualifierExpr = va or
+ qualifierExpr.(AddressOfExpr).getOperand() = va
+ )
+ |
+ // Direct field access
+ qualifierExpr = result.getQualifier()
+ or
+ // Nested field access
+ qualifierExpr = result.(NestedFieldAccess).getUltimateQualifier()
+ )
+}
+
+/**
+ * Gets a condition which is checked to be false by the given `ne` expression, according to this pattern:
+ * ```
+ * int a = !!result;
+ * if (!a) { // <- ne
+ * ....
+ * }
+ * ```
+ */
+private Expr getCheckedFalseCondition(NotExpr ne) {
+ exists(LocalVariable v |
+ result = v.getInitializer().getExpr().(NotExpr).getOperand().(NotExpr).getOperand() and
+ ne.getOperand() = v.getAnAccess() and
+ nonAssignedVariable(v)
+ // and not passed by val?
+ )
+}
+
+pragma[noinline]
+private predicate nonAssignedVariable(Variable v) { not exists(v.getAnAssignment()) }
diff --git a/cpp/ql/src/Security/CWE/CWE-457/UninitializedVariables.qll b/cpp/ql/src/Security/CWE/CWE-457/UninitializedVariables.qll
new file mode 100644
index 00000000000..4289f66e21d
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-457/UninitializedVariables.qll
@@ -0,0 +1,190 @@
+/**
+ * A module for identifying conditionally initialized variables.
+ */
+
+import cpp
+import InitializationFunctions
+
+// Optimised reachability predicates
+private predicate reaches(ControlFlowNode a, ControlFlowNode b) = fastTC(successor/2)(a, b)
+
+private predicate successor(ControlFlowNode a, ControlFlowNode b) { b = a.getASuccessor() }
+
+class WhitelistedCallsConfig extends string {
+ WhitelistedCallsConfig() { this = "config" }
+
+ abstract predicate isWhitelisted(Call c);
+}
+
+abstract class WhitelistedCall extends Call {
+ override Function getTarget() { none() }
+}
+
+private predicate hasConditionalInitialization(
+ ConditionalInitializationFunction f, ConditionalInitializationCall call, LocalVariable v,
+ VariableAccess initAccess, Evidence e
+) {
+ // Ignore whitelisted calls
+ not call instanceof WhitelistedCall and
+ f = getTarget(call) and
+ initAccess = v.getAnAccess() and
+ initAccess = call.getAConditionallyInitializedArgument(f, e).(AddressOfExpr).getOperand()
+}
+
+/**
+ * A variable that can be conditionally initialized by a call.
+ */
+class ConditionallyInitializedVariable extends LocalVariable {
+ ConditionalInitializationCall call;
+ ConditionalInitializationFunction f;
+ VariableAccess initAccess;
+ Evidence e;
+
+ ConditionallyInitializedVariable() {
+ // Find a call that conditionally initializes this variable
+ hasConditionalInitialization(f, call, this, initAccess, e) and
+ // Ignore cases where the variable is assigned prior to the call
+ not reaches(getAnAssignedValue(), initAccess) and
+ // Ignore cases where the variable is assigned field-wise prior to the call.
+ not exists(FieldAccess fa |
+ exists(Assignment a |
+ fa = getAFieldAccess(this) and
+ a.getLValue() = fa
+ )
+ |
+ reaches(fa, initAccess)
+ ) and
+ // Ignore cases where the variable is assigned by a prior call to an initialization function
+ not exists(Call c |
+ getAnAccess() = getAnInitializedArgument(c).(AddressOfExpr).getOperand() and
+ reaches(c, initAccess)
+ ) and
+ /*
+ * Static local variables with constant initializers do not have the initializer expr as part of
+ * the CFG, but should always be considered as initialized, so exclude them.
+ */
+
+ not exists(getInitializer().getExpr())
+ }
+
+ /**
+ * Gets an access of the variable `v` which is not used as an lvalue, and not used as an argument
+ * to an initialization function.
+ */
+ private VariableAccess getAReadAccess() {
+ result = this.getAnAccess() and
+ // Not used as an lvalue
+ not result = any(AssignExpr a).getLValue() and
+ // Not passed to another initialization function
+ not exists(Call c, int j | j = c.getTarget().(InitializationFunction).initializedParameter() |
+ result = c.getArgument(j).(AddressOfExpr).getOperand()
+ ) and
+ // Not a pointless read
+ not result = any(ExprStmt es).getExpr()
+ }
+
+ /**
+ * Gets a read access of variable `v` that occurs after the `initializingCall`.
+ */
+ private VariableAccess getAReadAccessAfterCall(ConditionalInitializationCall initializingCall) {
+ // Variable associated with this particular call
+ call = initializingCall and
+ // Access is a meaningful read access
+ result = getAReadAccess() and
+ // Which occurs after the call
+ reaches(call, result) and
+ /*
+ * Ignore risky accesses which are arguments to calls which also include another parameter to
+ * the original call. This is an attempt to eliminate results where the "status" can be checked
+ * through another parameter that assigned as part of the original call.
+ */
+
+ not exists(Call c |
+ c.getAnArgument() = result or
+ c.getAnArgument().(AddressOfExpr).getOperand() = result
+ |
+ exists(LocalVariable lv |
+ call.getAnArgument().(AddressOfExpr).getOperand() = lv.getAnAccess() and
+ not lv = this
+ |
+ c.getAnArgument() = lv.getAnAccess()
+ )
+ )
+ }
+
+ /**
+ * Gets an access to the variable that is risky because the variable may not be initialized after
+ * the `call`, and the status of the call is never checked.
+ */
+ VariableAccess getARiskyAccessWithNoStatusCheck(
+ ConditionalInitializationFunction initializingFunction,
+ ConditionalInitializationCall initializingCall, Evidence evidence
+ ) {
+ // Variable associated with this particular call
+ call = initializingCall and
+ initializingFunction = f and
+ e = evidence and
+ result = getAReadAccessAfterCall(initializingCall) and
+ (
+ // Access is risky because status return code ignored completely
+ call instanceof ExprInVoidContext
+ or
+ // Access is risky because status return code ignored completely
+ exists(LocalVariable status | call = status.getAnAssignedValue() |
+ not exists(status.getAnAccess())
+ )
+ )
+ }
+
+ /**
+ * Gets an access to the variable that is risky because the variable may not be initialized after
+ * the `call`, and the status of the call is only checked after the risky access.
+ */
+ VariableAccess getARiskyAccessBeforeStatusCheck(
+ ConditionalInitializationFunction initializingFunction,
+ ConditionalInitializationCall initializingCall, Evidence evidence
+ ) {
+ // Variable associated with this particular call
+ call = initializingCall and
+ initializingFunction = f and
+ e = evidence and
+ result = getAReadAccessAfterCall(initializingCall) and
+ exists(LocalVariable status, Assignment a |
+ a.getRValue() = call and
+ call = status.getAnAssignedValue() and
+ // There exists a check of the status code
+ definitionUsePair(status, a, _) and
+ // And the check of the status code does not occur before the risky access
+ not exists(VariableAccess statusAccess |
+ definitionUsePair(status, a, statusAccess) and
+ reaches(statusAccess, result)
+ ) and
+ // Ignore cases where the assignment to the status code is used directly
+ a instanceof ExprInVoidContext and
+ /*
+ * Ignore risky accesses which are arguments to calls which also include the status code.
+ * If both the risky value and status code are passed to a different function, that
+ * function is responsible for checking the status code.
+ */
+
+ not exists(Call c |
+ c.getAnArgument() = result or
+ c.getAnArgument().(AddressOfExpr).getOperand() = result
+ |
+ definitionUsePair(status, a, c.getAnArgument())
+ )
+ )
+ }
+
+ /**
+ * Gets an access to the variable that is risky because the variable may not be initialized after
+ * the `call`.
+ */
+ VariableAccess getARiskyAccess(
+ ConditionalInitializationFunction initializingFunction,
+ ConditionalInitializationCall initializingCall, Evidence evidence
+ ) {
+ result = getARiskyAccessBeforeStatusCheck(initializingFunction, initializingCall, evidence) or
+ result = getARiskyAccessWithNoStatusCheck(initializingFunction, initializingCall, evidence)
+ }
+}
diff --git a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
index fa74c85555d..63eca292297 100644
--- a/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
+++ b/cpp/ql/src/Security/CWE/CWE-497/ExposedSystemData.ql
@@ -190,11 +190,11 @@ private predicate windowsSystemInfo(FunctionCall source, Element use) {
// void WINAPI GetSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
// void WINAPI GetNativeSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo);
(
- source.getTarget().hasName("GetVersionEx") or
- source.getTarget().hasName("GetVersionExA") or
- source.getTarget().hasName("GetVersionExW") or
- source.getTarget().hasName("GetSystemInfo") or
- source.getTarget().hasName("GetNativeSystemInfo")
+ source.getTarget().hasGlobalName("GetVersionEx") or
+ source.getTarget().hasGlobalName("GetVersionExA") or
+ source.getTarget().hasGlobalName("GetVersionExW") or
+ source.getTarget().hasGlobalName("GetSystemInfo") or
+ source.getTarget().hasGlobalName("GetNativeSystemInfo")
) and
use = source.getArgument(0)
}
@@ -216,9 +216,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
// _In_ BOOL fCreate
// );
(
- source.getTarget().hasName("SHGetSpecialFolderPath") or
- source.getTarget().hasName("SHGetSpecialFolderPathA") or
- source.getTarget().hasName("SHGetSpecialFolderPathW")
+ source.getTarget().hasGlobalName("SHGetSpecialFolderPath") or
+ source.getTarget().hasGlobalName("SHGetSpecialFolderPathA") or
+ source.getTarget().hasGlobalName("SHGetSpecialFolderPathW")
) and
use = source.getArgument(1)
or
@@ -228,7 +228,7 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
// _In_opt_ HANDLE hToken,
// _Out_ PWSTR *ppszPath
// );
- source.getTarget().hasName("SHGetKnownFolderPath") and
+ source.getTarget().hasGlobalName("SHGetKnownFolderPath") and
use = source.getArgument(3)
or
// HRESULT SHGetFolderPath(
@@ -239,9 +239,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
// _Out_ LPTSTR pszPath
// );
(
- source.getTarget().hasName("SHGetFolderPath") or
- source.getTarget().hasName("SHGetFolderPathA") or
- source.getTarget().hasName("SHGetFolderPathW")
+ source.getTarget().hasGlobalName("SHGetFolderPath") or
+ source.getTarget().hasGlobalName("SHGetFolderPathA") or
+ source.getTarget().hasGlobalName("SHGetFolderPathW")
) and
use = source.getArgument(4)
or
@@ -254,9 +254,9 @@ private predicate windowsFolderPath(FunctionCall source, Element use) {
// _Out_ LPTSTR pszPath
// );
(
- source.getTarget().hasName("SHGetFolderPathAndSubDir") or
- source.getTarget().hasName("SHGetFolderPathAndSubDirA") or
- source.getTarget().hasName("SHGetFolderPathAndSubDirW")
+ source.getTarget().hasGlobalName("SHGetFolderPathAndSubDir") or
+ source.getTarget().hasGlobalName("SHGetFolderPathAndSubDirA") or
+ source.getTarget().hasGlobalName("SHGetFolderPathAndSubDirW")
) and
use = source.getArgument(5)
}
@@ -273,9 +273,9 @@ class WindowsFolderPath extends SystemData {
private predicate logonUser(FunctionCall source, VariableAccess use) {
(
- source.getTarget().hasName("LogonUser") or
- source.getTarget().hasName("LogonUserW") or
- source.getTarget().hasName("LogonUserA")
+ source.getTarget().hasGlobalName("LogonUser") or
+ source.getTarget().hasGlobalName("LogonUserW") or
+ source.getTarget().hasGlobalName("LogonUserA")
) and
use = source.getAnArgument()
}
@@ -297,9 +297,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
// _Inout_opt_ PLONG lpcbValue
// );
(
- source.getTarget().hasName("RegQueryValue") or
- source.getTarget().hasName("RegQueryValueA") or
- source.getTarget().hasName("RegQueryValueW")
+ source.getTarget().hasGlobalName("RegQueryValue") or
+ source.getTarget().hasGlobalName("RegQueryValueA") or
+ source.getTarget().hasGlobalName("RegQueryValueW")
) and
use = source.getArgument(2)
or
@@ -311,9 +311,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
// _Inout_opt_ LPDWORD ldwTotsize
// );
(
- source.getTarget().hasName("RegQueryMultipleValues") or
- source.getTarget().hasName("RegQueryMultipleValuesA") or
- source.getTarget().hasName("RegQueryMultipleValuesW")
+ source.getTarget().hasGlobalName("RegQueryMultipleValues") or
+ source.getTarget().hasGlobalName("RegQueryMultipleValuesA") or
+ source.getTarget().hasGlobalName("RegQueryMultipleValuesW")
) and
use = source.getArgument(3)
or
@@ -326,9 +326,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
// _Inout_opt_ LPDWORD lpcbData
// );
(
- source.getTarget().hasName("RegQueryValueEx") or
- source.getTarget().hasName("RegQueryValueExA") or
- source.getTarget().hasName("RegQueryValueExW")
+ source.getTarget().hasGlobalName("RegQueryValueEx") or
+ source.getTarget().hasGlobalName("RegQueryValueExA") or
+ source.getTarget().hasGlobalName("RegQueryValueExW")
) and
use = source.getArgument(4)
or
@@ -342,9 +342,9 @@ private predicate regQuery(FunctionCall source, VariableAccess use) {
// _Inout_opt_ LPDWORD pcbData
// );
(
- source.getTarget().hasName("RegGetValue") or
- source.getTarget().hasName("RegGetValueA") or
- source.getTarget().hasName("RegGetValueW")
+ source.getTarget().hasGlobalName("RegGetValue") or
+ source.getTarget().hasGlobalName("RegGetValueA") or
+ source.getTarget().hasGlobalName("RegGetValueW")
) and
use = source.getArgument(5)
}
diff --git a/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql b/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql
index 84d7b48e265..fe9b56bd521 100644
--- a/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql
+++ b/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.ql
@@ -15,5 +15,5 @@ import cpp
from FunctionCall call, Function target
where
call.getTarget() = target and
- target.hasGlobalName("gets")
+ target.hasGlobalOrStdName("gets")
select call, "gets does not guard against buffer overflow"
diff --git a/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls b/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
new file mode 100644
index 00000000000..2036584e44c
--- /dev/null
+++ b/cpp/ql/src/codeql-suites/cpp-lgtm-full.qls
@@ -0,0 +1,14 @@
+- description: Standard LGTM queries for C/C++, including ones not displayed by default
+- qlpack: codeql-cpp
+- apply: lgtm-selectors.yml
+ from: codeql-suite-helpers
+# These queries are infeasible to compute on large projects:
+- exclude:
+ query path:
+ - Security/CWE/CWE-497/ExposedSystemData.ql
+ - Critical/DescriptorMayNotBeClosed.ql
+ - Critical/DescriptorNeverClosed.ql
+ - Critical/FileMayNotBeClosed.ql
+ - Critical/FileNeverClosed.ql
+ - Critical/MemoryMayNotBeFreed.ql
+ - Critical/MemoryNeverFreed.ql
diff --git a/cpp/ql/src/codeql-suites/cpp-lgtm.qls b/cpp/ql/src/codeql-suites/cpp-lgtm.qls
new file mode 100644
index 00000000000..fe06e19b7fa
--- /dev/null
+++ b/cpp/ql/src/codeql-suites/cpp-lgtm.qls
@@ -0,0 +1,4 @@
+- description: Standard LGTM queries for C/C++
+- apply: codeql-suites/cpp-lgtm-full.qls
+- apply: lgtm-displayed-only.yml
+ from: codeql-suite-helpers
diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql
index 4b1f45185d1..f2512b93003 100644
--- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql
+++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.ql
@@ -22,7 +22,7 @@ predicate acquireExpr(Expr acquire, string kind) {
exists(FunctionCall fc, Function f, string name |
fc = acquire and
f = fc.getTarget() and
- f.hasGlobalName(name) and
+ f.hasGlobalOrStdName(name) and
(
name = "fopen" and
kind = "file"
@@ -46,7 +46,7 @@ predicate releaseExpr(Expr release, Expr resource, string kind) {
exists(FunctionCall fc, Function f, string name |
fc = release and
f = fc.getTarget() and
- f.hasGlobalName(name) and
+ f.hasGlobalOrStdName(name) and
(
name = "fclose" and
resource = fc.getArgument(0) and
diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 81.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 81.ql
index 5a8df7fc625..2a3a70bd016 100644
--- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 81.ql
+++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 81.ql
@@ -51,7 +51,7 @@ class ReferenceCopyAssignmentOperator extends MemberFunction {
/**
* A call to a function called swap. Note: could be a member,
- * std::swap or a function overloading std::swap (not in std::)
+ * `std::swap` or a function overloading `std::swap` (not in `std::`)
* so keep it simple
*/
FunctionCall getASwapCall() {
diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.ql b/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.ql
index 501101f706c..872a7443e6e 100644
--- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.ql
+++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 97.ql
@@ -22,8 +22,8 @@ predicate containsArray(Type t) {
or
containsArray(t.getUnderlyingType()) and
not exists(TypedefType allowed | allowed = t |
- allowed.hasGlobalName("jmp_buf") or
- allowed.hasGlobalName("va_list")
+ allowed.hasGlobalOrStdName("jmp_buf") or
+ allowed.hasGlobalOrStdName("va_list")
)
}
diff --git a/cpp/ql/src/jsf/4.21 Operators/AV Rule 159.ql b/cpp/ql/src/jsf/4.21 Operators/AV Rule 159.ql
index 99d60f7290d..5542dad405b 100644
--- a/cpp/ql/src/jsf/4.21 Operators/AV Rule 159.ql
+++ b/cpp/ql/src/jsf/4.21 Operators/AV Rule 159.ql
@@ -10,7 +10,7 @@
/*
* See More Effective C++ item 7.
- * Note: Meyers allows unary & to be overloaded but not comma
+ * Note: Meyers allows unary `&` to be overloaded but not comma.
*/
import cpp
diff --git a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 213.ql b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 213.ql
index fbfe211cb19..992351e4c82 100644
--- a/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 213.ql
+++ b/cpp/ql/src/jsf/4.28 Portable Code/AV Rule 213.ql
@@ -15,11 +15,11 @@ import cpp
/*
* Interpretation and deviations:
* - if the higher operator has precedence > arithmetic then it is fine
- * RATIONALE: exprs like f(), *x, &x are easily understood to bind tightly
+ * RATIONALE: exprs like `f()`, `*x`, `&x` are easily understood to bind tightly
* - if the higher operator is the RHS of an assign then it is fine
* RATIONALE: cf. MISRA, too many cases excluded otherwise
* - comparison operators can be mixed with arithmetic
- * RATIONALE: x==y+z is common and unambiguous
+ * RATIONALE: `x==y+z` is common and unambiguous
*/
predicate arithmeticPrecedence(int p) { p = 12 or p = 13 }
diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml
new file mode 100644
index 00000000000..a1c7df902de
--- /dev/null
+++ b/cpp/ql/src/qlpack.yml
@@ -0,0 +1,4 @@
+name: codeql-cpp
+version: 0.0.0
+dbscheme: semmlecode.cpp.dbscheme
+suites: codeql-suites
diff --git a/cpp/ql/src/semmle/code/cpp/Class.qll b/cpp/ql/src/semmle/code/cpp/Class.qll
index 5ac4f52392e..297654a1afa 100644
--- a/cpp/ql/src/semmle/code/cpp/Class.qll
+++ b/cpp/ql/src/semmle/code/cpp/Class.qll
@@ -605,15 +605,6 @@ class Class extends UserType {
class_instantiation(underlyingElement(this), unresolveElement(c))
}
- /**
- * Gets the `i`th template argument used to instantiate this class from a
- * class template. When called on a class template, this will return the
- * `i`th template parameter.
- */
- override Type getTemplateArgument(int i) {
- class_template_argument(underlyingElement(this), i, unresolveElement(result))
- }
-
/**
* Holds if this class/struct is polymorphic (has a virtual function, or
* inherits one).
@@ -623,7 +614,7 @@ class Class extends UserType {
}
override predicate involvesTemplateParameter() {
- getATemplateArgument().involvesTemplateParameter()
+ getATemplateArgument().(Type).involvesTemplateParameter()
}
/** Holds if this class, struct or union was declared 'final'. */
diff --git a/cpp/ql/src/semmle/code/cpp/Declaration.qll b/cpp/ql/src/semmle/code/cpp/Declaration.qll
index fa061b239cc..1d5603fe4f4 100644
--- a/cpp/ql/src/semmle/code/cpp/Declaration.qll
+++ b/cpp/ql/src/semmle/code/cpp/Declaration.qll
@@ -14,8 +14,12 @@ private import semmle.code.cpp.internal.QualifiedName as Q
* ```
* extern int myglobal;
* ```
- * Each of these declarations is given its own distinct `DeclarationEntry`,
- * but they all share the same `Declaration`.
+ * and defined in one:
+ * ```
+ * int myglobal;
+ * ```
+ * Each of these declarations (including the definition) is given its own
+ * distinct `DeclarationEntry`, but they all share the same `Declaration`.
*
* Some derived class of `Declaration` do not have a corresponding
* `DeclarationEntry`, because they always have a unique source location.
@@ -119,6 +123,13 @@ abstract class Declaration extends Locatable, @declaration {
/** Holds if this declaration has the given name in the global namespace. */
predicate hasGlobalName(string name) { this.hasQualifiedName("", "", name) }
+ /** Holds if this declaration has the given name in the global namespace or the `std` namespace. */
+ predicate hasGlobalOrStdName(string name) {
+ this.hasGlobalName(name)
+ or
+ this.hasQualifiedName("std", "", name)
+ }
+
/** Gets a specifier of this declaration. */
abstract Specifier getASpecifier();
@@ -189,26 +200,99 @@ abstract class Declaration extends Locatable, @declaration {
/**
* Gets a template argument used to instantiate this declaration from a template.
- * When called on a template, this will return a template parameter.
+ * When called on a template, this will return a template parameter type for
+ * both typed and non-typed parameters.
*/
- final Type getATemplateArgument() { result = getTemplateArgument(_) }
+ final Locatable getATemplateArgument() { result = getTemplateArgument(_) }
+
+ /**
+ * Gets a template argument used to instantiate this declaration from a template.
+ * When called on a template, this will return a non-typed template
+ * parameter value.
+ */
+ final Locatable getATemplateArgumentKind() { result = getTemplateArgumentKind(_) }
/**
* Gets the `i`th template argument used to instantiate this declaration from a
- * template. When called on a template, this will return the `i`th template parameter.
+ * template.
+ *
+ * For example:
+ *
+ * `template class Foo;`
+ *
+ * Will have `getTemplateArgument(0)` return `T`, and
+ * `getTemplateArgument(1)` return `X`.
+ *
+ * `Foo bar;`
+ *
+ * Will have `getTemplateArgument())` return `int`, and
+ * `getTemplateArgument(1)` return `1`.
*/
- Type getTemplateArgument(int index) { none() }
+ final Locatable getTemplateArgument(int index) {
+ if exists(getTemplateArgumentValue(index))
+ then result = getTemplateArgumentValue(index)
+ else result = getTemplateArgumentType(index)
+ }
+
+ /**
+ * Gets the `i`th template argument value used to instantiate this declaration
+ * from a template. When called on a template, this will return the `i`th template
+ * parameter value if it exists.
+ *
+ * For example:
+ *
+ * `template class Foo;`
+ *
+ * Will have `getTemplateArgumentKind(1)` return `T`, and no result for
+ * `getTemplateArgumentKind(0)`.
+ *
+ * `Foo bar;
+ *
+ * Will have `getTemplateArgumentKind(1)` return `int`, and no result for
+ * `getTemplateArgumentKind(0)`.
+ */
+ final Locatable getTemplateArgumentKind(int index) {
+ if exists(getTemplateArgumentValue(index))
+ then result = getTemplateArgumentType(index)
+ else none()
+ }
/** Gets the number of template arguments for this declaration. */
final int getNumberOfTemplateArguments() {
result = count(int i | exists(getTemplateArgument(i)))
}
+
+ private Type getTemplateArgumentType(int index) {
+ class_template_argument(underlyingElement(this), index, unresolveElement(result))
+ or
+ function_template_argument(underlyingElement(this), index, unresolveElement(result))
+ or
+ variable_template_argument(underlyingElement(this), index, unresolveElement(result))
+ }
+
+ private Expr getTemplateArgumentValue(int index) {
+ class_template_argument_value(underlyingElement(this), index, unresolveElement(result))
+ or
+ function_template_argument_value(underlyingElement(this), index, unresolveElement(result))
+ or
+ variable_template_argument_value(underlyingElement(this), index, unresolveElement(result))
+ }
}
/**
- * A C/C++ declaration entry. See the comment above `Declaration` for an
- * explanation of the relationship between `Declaration` and
- * `DeclarationEntry`.
+ * A C/C++ declaration entry. For example the following code contains five
+ * declaration entries:
+ * ```
+ * extern int myGlobal;
+ * int myVariable;
+ * typedef char MyChar;
+ * void myFunction();
+ * void myFunction() {
+ * // ...
+ * }
+ * ```
+ * See the comment above `Declaration` for an explanation of the relationship
+ * between `Declaration` and `DeclarationEntry`.
*/
abstract class DeclarationEntry extends Locatable {
/** Gets a specifier associated with this declaration entry. */
@@ -281,8 +365,19 @@ abstract class DeclarationEntry extends Locatable {
* A declaration that can potentially have more C++ access rights than its
* enclosing element. This comprises `Class` (they have access to their own
* private members) along with other `UserType`s and `Function` (they can be
- * the target of `friend` declarations).
+ * the target of `friend` declarations). For example `MyClass` and
+ * `myFunction` in the following code:
+ * ```
+ * class MyClass
+ * {
+ * public:
+ * ...
+ * };
*
+ * void myFunction() {
+ * // ...
+ * }
+ * ```
* In the C++ standard (N4140 11.2), rules for access control revolve around
* the informal phrase "_R_ occurs in a member or friend of class C", where
* `AccessHolder` corresponds to this _R_.
@@ -416,8 +511,19 @@ abstract class AccessHolder extends Declaration {
/**
* A declaration that very likely has more C++ access rights than its
* enclosing element. This comprises `Class` (they have access to their own
- * private members) along with any target of a `friend` declaration.
+ * private members) along with any target of a `friend` declaration. For
+ * example `MyClass` and `friendFunction` in the following code:
+ * ```
+ * class MyClass
+ * {
+ * public:
+ * friend void friendFunction();
+ * };
*
+ * void friendFunction() {
+ * // ...
+ * }
+ * ```
* Most access rights are computed for `DirectAccessHolder` instead of
* `AccessHolder` -- that's more efficient because there are fewer
* `DirectAccessHolder`s. If a `DirectAccessHolder` contains an `AccessHolder`,
diff --git a/cpp/ql/src/semmle/code/cpp/Function.qll b/cpp/ql/src/semmle/code/cpp/Function.qll
index 94d11d63575..85c277f945b 100644
--- a/cpp/ql/src/semmle/code/cpp/Function.qll
+++ b/cpp/ql/src/semmle/code/cpp/Function.qll
@@ -343,15 +343,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
function_instantiation(underlyingElement(this), unresolveElement(f))
}
- /**
- * Gets the `i`th template argument used to instantiate this function from a
- * function template. When called on a function template, this will return the
- * `i`th template parameter.
- */
- override Type getTemplateArgument(int index) {
- function_template_argument(underlyingElement(this), index, unresolveElement(result))
- }
-
/**
* Holds if this function is defined in several files. This is illegal in
* C (though possible in some C++ compilers), and likely indicates that
@@ -434,7 +425,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
// ... and likewise for destructors.
this.(Destructor).getADestruction().mayBeGloballyImpure()
else
- not exists(string name | this.hasGlobalName(name) |
+ not exists(string name | this.hasGlobalOrStdName(name) |
// Unless it's a function that we know is side-effect-free, it may
// have side-effects.
name = "strcmp" or
diff --git a/cpp/ql/src/semmle/code/cpp/NestedFields.qll b/cpp/ql/src/semmle/code/cpp/NestedFields.qll
new file mode 100644
index 00000000000..c4be8b8b9ff
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/NestedFields.qll
@@ -0,0 +1,40 @@
+import cpp
+
+/**
+ * Gets a `Field` that is within the given `Struct`, either directly or nested
+ * inside one or more levels of member structs.
+ */
+private Field getANestedField(Struct s) {
+ result = s.getAField()
+ or
+ exists(NestedStruct ns |
+ s = ns.getDeclaringType() and
+ result = getANestedField(ns)
+ )
+}
+
+/**
+ * Unwraps a series of field accesses to determine the outer-most qualifier.
+ */
+private Expr getUltimateQualifier(FieldAccess fa) {
+ exists(Expr qualifier | qualifier = fa.getQualifier() |
+ result = getUltimateQualifier(qualifier)
+ or
+ not qualifier instanceof FieldAccess and result = qualifier
+ )
+}
+
+/**
+ * Accesses to nested fields.
+ */
+class NestedFieldAccess extends FieldAccess {
+ Expr ultimateQualifier;
+
+ NestedFieldAccess() {
+ ultimateQualifier = getUltimateQualifier(this) and
+ getTarget() = getANestedField(ultimateQualifier.getType().stripType())
+ }
+
+ /** Gets the ultimate qualifier of this nested field access. */
+ Expr getUltimateQualifier() { result = ultimateQualifier }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/Parameter.qll b/cpp/ql/src/semmle/code/cpp/Parameter.qll
index 74326ada99c..8b391101c6c 100644
--- a/cpp/ql/src/semmle/code/cpp/Parameter.qll
+++ b/cpp/ql/src/semmle/code/cpp/Parameter.qll
@@ -158,3 +158,10 @@ class Parameter extends LocalScopeVariable, @parameter {
)
}
}
+
+/**
+ * An `int` that is a parameter index for some function. This is needed for binding in certain cases.
+ */
+class ParameterIndex extends int {
+ ParameterIndex() { exists(Parameter p | this = p.getIndex()) }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/Print.qll b/cpp/ql/src/semmle/code/cpp/Print.qll
index ab979d89b03..e3ad19f6028 100644
--- a/cpp/ql/src/semmle/code/cpp/Print.qll
+++ b/cpp/ql/src/semmle/code/cpp/Print.qll
@@ -35,6 +35,14 @@ private string getParameterTypeString(Type parameterType) {
else result = parameterType.(DumpType).getTypeIdentityString()
}
+private string getTemplateArgumentString(Declaration d, int i) {
+ if exists(d.getTemplateArgumentKind(i))
+ then
+ result = d.getTemplateArgumentKind(i).(DumpType).getTypeIdentityString() + " " +
+ d.getTemplateArgument(i)
+ else result = d.getTemplateArgument(i).(DumpType).getTypeIdentityString()
+}
+
/**
* A `Declaration` extended to add methods for generating strings useful only for dumps and debugging.
*/
@@ -56,7 +64,7 @@ abstract private class DumpDeclaration extends Declaration {
strictconcat(int i |
exists(this.getTemplateArgument(i))
|
- this.getTemplateArgument(i).(DumpType).getTypeIdentityString(), ", " order by i
+ getTemplateArgumentString(this, i), ", " order by i
) + ">"
else result = ""
}
diff --git a/cpp/ql/src/semmle/code/cpp/PrintAST.ql b/cpp/ql/src/semmle/code/cpp/PrintAST.ql
index 6fc40dd0525..e4c53030da5 100644
--- a/cpp/ql/src/semmle/code/cpp/PrintAST.ql
+++ b/cpp/ql/src/semmle/code/cpp/PrintAST.ql
@@ -7,3 +7,15 @@
import cpp
import PrintAST
+
+/**
+ * Temporarily tweak this class or make a copy to control which functions are
+ * printed.
+ */
+class Cfg extends PrintASTConfiguration {
+ /**
+ * TWEAK THIS PREDICATE AS NEEDED.
+ * Holds if the AST for `func` should be printed.
+ */
+ override predicate shouldPrintFunction(Function func) { any() }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll
index c65c4941355..ae42165bab8 100644
--- a/cpp/ql/src/semmle/code/cpp/Type.qll
+++ b/cpp/ql/src/semmle/code/cpp/Type.qll
@@ -5,6 +5,8 @@ private import semmle.code.cpp.internal.ResolveClass
/**
* A C/C++ type.
+ *
+ * This QL class represents the root of the C/C++ type hierarchy.
*/
class Type extends Locatable, @type {
Type() { isType(underlyingElement(this)) }
@@ -210,7 +212,7 @@ class Type extends Locatable, @type {
// A function call that provides an explicit template argument that refers to T uses T.
// We exclude calls within instantiations, since they do not appear directly in the source.
exists(FunctionCall c |
- c.getAnExplicitTemplateArgument().refersTo(this) and
+ c.getAnExplicitTemplateArgument().(Type).refersTo(this) and
result = c and
not c.getEnclosingFunction().isConstructedFrom(_)
)
@@ -289,6 +291,13 @@ class Type extends Locatable, @type {
/**
* A C/C++ built-in primitive type (int, float, void, and so on). See 4.1.1.
+ * In the following example, `unsigned int` and `double` denote primitive
+ * built-in types:
+ * ```
+ * double a;
+ * unsigned int ua[40];
+ * typedef double LargeFloat;
+ * ```
*/
class BuiltInType extends Type, @builtintype {
override string toString() { result = this.getName() }
@@ -301,7 +310,14 @@ class BuiltInType extends Type, @builtintype {
}
/**
- * An erroneous type.
+ * An erroneous type. This type has no corresponding C/C++ syntax.
+ *
+ * `ErroneousType` is the type of `ErrorExpr`, which in turn refers to an illegal
+ * language construct. In the example below, a temporary (`0`) cannot be bound
+ * to an lvalue reference (`int &`):
+ * ```
+ * int &intref = 0;
+ * ```
*/
class ErroneousType extends BuiltInType {
ErroneousType() { builtintypes(underlyingElement(this), _, 1, _, _, _) }
@@ -310,7 +326,18 @@ class ErroneousType extends BuiltInType {
}
/**
- * The unknown type.
+ * The unknown type. This type has no corresponding C/C++ syntax.
+ *
+ * Unknown types usually occur inside _uninstantiated_ template functions.
+ * In the example below, the expressions `x.a` and `x.b` have unknown type
+ * in the _uninstantiated_ template.
+ * ```
+ * template
+ * bool check(T x) {
+ * if (x.a == x.b)
+ * abort();
+ * }
+ * ```
*/
class UnknownType extends BuiltInType {
UnknownType() { builtintypes(underlyingElement(this), _, 2, _, _, _) }
@@ -326,6 +353,10 @@ private predicate isArithmeticType(@builtintype type, int kind) {
/**
* The C/C++ arithmetic types. See 4.1.1.
+ *
+ * This includes primitive types on which arithmetic, bitwise or logical
+ * operations may be performed. Examples of arithmetic types include
+ * `char`, `int`, `float`, and `bool`.
*/
class ArithmeticType extends BuiltInType {
ArithmeticType() { isArithmeticType(underlyingElement(this), _) }
@@ -349,11 +380,20 @@ private predicate isIntegralType(@builtintype type, int kind) {
}
/**
- * A C/C++ integral or enum type.
- * The definition of "integral type" in the C++ Standard excludes enum types,
- * but because an enum type holds a value of its underlying integral type,
+ * A C/C++ integral or `enum` type.
+ *
+ * The definition of "integral type" in the C++ standard excludes `enum` types,
+ * but because an `enum` type holds a value of its underlying integral type,
* it is often useful to have a common category that includes both integral
- * and enum types.
+ * and `enum` types.
+ *
+ * In the following example, `a`, `b` and `c` are all declared with an
+ * integral or `enum` type:
+ * ```
+ * unsigned long a;
+ * enum e1 { val1, val2 } b;
+ * enum class e2: short { val3, val4 } c;
+ * ```
*/
class IntegralOrEnumType extends Type {
IntegralOrEnumType() {
@@ -426,7 +466,17 @@ private predicate integralTypeMapping(int original, int canonical, int unsigned,
}
/**
- * The C/C++ integral types. See 4.1.1.
+ * The C/C++ integral types. See 4.1.1. These are types that are represented
+ * as integers of varying sizes. Both `enum` types and floating-point types
+ * are excluded.
+ *
+ * In the following examples, `a`, `b` and `c` are declared using integral
+ * types:
+ * ```
+ * unsigned int a;
+ * long long b;
+ * char c;
+ * ```
*/
class IntegralType extends ArithmeticType, IntegralOrEnumType {
int kind;
@@ -497,7 +547,12 @@ class IntegralType extends ArithmeticType, IntegralOrEnumType {
}
/**
- * The C/C++ boolean type. See 4.2.
+ * The C/C++ boolean type. See 4.2. This is the C `_Bool` type
+ * or the C++ `bool` type. For example:
+ * ```
+ * extern bool a, b; // C++
+ * _Bool c, d; // C
+ * ```
*/
class BoolType extends IntegralType {
BoolType() { builtintypes(underlyingElement(this), _, 4, _, _, _) }
@@ -506,12 +561,23 @@ class BoolType extends IntegralType {
}
/**
- * The C/C++ character types. See 4.3.
+ * The C/C++ character types. See 4.3. This includes the `char`,
+ * `signed char` and `unsigned char` types, all of which are
+ * distinct from one another. For example:
+ * ```
+ * char a, b;
+ * signed char c, d;
+ * unsigned char e, f;
+ * ```
*/
abstract class CharType extends IntegralType { }
/**
- * The C/C++ char type (which is different to signed char and unsigned char).
+ * The C/C++ `char` type (which is distinct from `signed char` and
+ * `unsigned char`). For example:
+ * ```
+ * char a, b;
+ * ```
*/
class PlainCharType extends CharType {
PlainCharType() { builtintypes(underlyingElement(this), _, 5, _, _, _) }
@@ -520,7 +586,11 @@ class PlainCharType extends CharType {
}
/**
- * The C/C++ unsigned char type (which is different to plain char, even when chars are unsigned by default).
+ * The C/C++ `unsigned char` type (which is distinct from plain `char`
+ * even when `char` is `unsigned` by default).
+ * ```
+ * unsigned char e, f;
+ * ```
*/
class UnsignedCharType extends CharType {
UnsignedCharType() { builtintypes(underlyingElement(this), _, 6, _, _, _) }
@@ -529,7 +599,11 @@ class UnsignedCharType extends CharType {
}
/**
- * The C/C++ signed char type (which is different to plain char, even when chars are signed by default).
+ * The C/C++ `signed char` type (which is distinct from plain `char`
+ * even when `char` is `signed` by default).
+ * ```
+ * signed char c, d;
+ * ```
*/
class SignedCharType extends CharType {
SignedCharType() { builtintypes(underlyingElement(this), _, 7, _, _, _) }
@@ -538,7 +612,11 @@ class SignedCharType extends CharType {
}
/**
- * The C/C++ short types. See 4.3.
+ * The C/C++ short types. See 4.3. This includes `short`, `signed short`
+ * and `unsigned short`.
+ * ```
+ * signed short ss;
+ * ```
*/
class ShortType extends IntegralType {
ShortType() {
@@ -551,7 +629,11 @@ class ShortType extends IntegralType {
}
/**
- * The C/C++ integer types. See 4.4.
+ * The C/C++ integer types. See 4.4. This includes `int`, `signed int`
+ * and `unsigned int`.
+ * ```
+ * unsigned int ui;
+ * ```
*/
class IntType extends IntegralType {
IntType() {
@@ -564,7 +646,11 @@ class IntType extends IntegralType {
}
/**
- * The C/C++ long types. See 4.4.
+ * The C/C++ long types. See 4.4. This includes `long`, `signed long`
+ * and `unsigned long`.
+ * ```
+ * long l;
+ * ```
*/
class LongType extends IntegralType {
LongType() {
@@ -577,7 +663,11 @@ class LongType extends IntegralType {
}
/**
- * The C/C++ long long types. See 4.4.
+ * The C/C++ long long types. See 4.4. This includes `long long`, `signed long long`
+ * and `unsigned long long`.
+ * ```
+ * signed long long sll;
+ * ```
*/
class LongLongType extends IntegralType {
LongLongType() {
@@ -590,7 +680,12 @@ class LongLongType extends IntegralType {
}
/**
- * The GNU C __int128 types.
+ * The GNU C __int128 primitive types. They are not part of standard C/C++.
+ *
+ * This includes `__int128`, `signed __int128` and `unsigned __int128`.
+ * ```
+ * unsigned __int128 ui128;
+ * ```
*/
class Int128Type extends IntegralType {
Int128Type() {
@@ -598,10 +693,18 @@ class Int128Type extends IntegralType {
builtintypes(underlyingElement(this), _, 36, _, _, _) or
builtintypes(underlyingElement(this), _, 37, _, _, _)
}
+
+ override string getCanonicalQLClass() { result = "Int128Type" }
}
/**
- * The C/C++ floating point types. See 4.5.
+ * The C/C++ floating point types. See 4.5. This includes `float`,
+ * `double` and `long double` types.
+ * ```
+ * float f;
+ * double d;
+ * long double ld;
+ * ```
*/
class FloatingPointType extends ArithmeticType {
FloatingPointType() {
@@ -610,14 +713,19 @@ class FloatingPointType extends ArithmeticType {
(
kind >= 24 and kind <= 32
or
- kind = 38
+ kind >= 38 and kind <= 42
+ or
+ kind >= 45 and kind <= 50
)
)
}
}
/**
- * The C/C++ float type.
+ * The C/C++ `float` type.
+ * ```
+ * float f;
+ * ```
*/
class FloatType extends FloatingPointType {
FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) }
@@ -626,7 +734,10 @@ class FloatType extends FloatingPointType {
}
/**
- * The C/C++ double type.
+ * The C/C++ `double` type.
+ * ```
+ * double d;
+ * ```
*/
class DoubleType extends FloatingPointType {
DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) }
@@ -635,7 +746,10 @@ class DoubleType extends FloatingPointType {
}
/**
- * The C/C++ long double type.
+ * The C/C++ `long double` type.
+ * ```
+ * long double ld;
+ * ```
*/
class LongDoubleType extends FloatingPointType {
LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) }
@@ -644,35 +758,58 @@ class LongDoubleType extends FloatingPointType {
}
/**
- * The GNU C __float128 type.
+ * The GNU C `__float128` primitive type. This is not standard C/C++.
+ * ```
+ * __float128 f128;
+ * ```
*/
class Float128Type extends FloatingPointType {
Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) }
+
+ override string getCanonicalQLClass() { result = "Float128Type" }
}
/**
- * The GNU C _Decimal32 type.
+ * The GNU C `_Decimal32` primitive type. This is not standard C/C++.
+ * ```
+ * _Decimal32 d32;
+ * ```
*/
class Decimal32Type extends FloatingPointType {
Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) }
+
+ override string getCanonicalQLClass() { result = "Decimal32Type" }
}
/**
- * The GNU C _Decimal64 type.
+ * The GNU C `_Decimal64` primitive type. This is not standard C/C++.
+ * ```
+ * _Decimal64 d64;
+ * ```
*/
class Decimal64Type extends FloatingPointType {
Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) }
+
+ override string getCanonicalQLClass() { result = "Decimal64Type" }
}
/**
- * The GNU C _Decimal128 type.
+ * The GNU C `_Decimal128` primitive type. This is not standard C/C++.
+ * ```
+ * _Decimal128 d128;
+ * ```
*/
class Decimal128Type extends FloatingPointType {
Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) }
+
+ override string getCanonicalQLClass() { result = "Decimal128Type" }
}
/**
- * The C/C++ void type. See 4.7.
+ * The C/C++ `void` type. See 4.7.
+ * ```
+ * void foo();
+ * ```
*/
class VoidType extends BuiltInType {
VoidType() { builtintypes(underlyingElement(this), _, 3, _, _, _) }
@@ -686,6 +823,9 @@ class VoidType extends BuiltInType {
* Note that on some platforms `wchar_t` doesn't exist as a built-in
* type but a typedef is provided. Consider using the `Wchar_t` QL
* class to include these types.
+ * ```
+ * wchar_t wc;
+ * ```
*/
class WideCharType extends IntegralType {
WideCharType() { builtintypes(underlyingElement(this), _, 33, _, _, _) }
@@ -694,7 +834,10 @@ class WideCharType extends IntegralType {
}
/**
- * The C/C++ `char16_t` type.
+ * The C/C++ `char16_t` type. This is available starting with C11 and C++11.
+ * ```
+ * char16_t c16;
+ * ```
*/
class Char16Type extends IntegralType {
Char16Type() { builtintypes(underlyingElement(this), _, 43, _, _, _) }
@@ -703,7 +846,10 @@ class Char16Type extends IntegralType {
}
/**
- * The C/C++ `char32_t` type.
+ * The C/C++ `char32_t` type. This is available starting with C11 and C++11.
+ * ```
+ * char32_t c32;
+ * ```
*/
class Char32Type extends IntegralType {
Char32Type() { builtintypes(underlyingElement(this), _, 44, _, _, _) }
@@ -712,13 +858,13 @@ class Char32Type extends IntegralType {
}
/**
- * The type of the C++11 nullptr constant.
- *
- * Note that this is not `nullptr_t`, as `nullptr_t` is defined as:
+ * The (primitive) type of the C++11 `nullptr` constant. It is a
+ * distinct type, denoted by `decltype(nullptr)`, that is not itself a pointer
+ * type or a pointer to member type. The `` header usually defines
+ * the `std::nullptr_t` type as follows:
* ```
- * typedef decltype(nullptr) nullptr_t;
+ * typedef decltype(nullptr) nullptr_t;
* ```
- * Instead, this is the unspeakable type given by `decltype(nullptr)`.
*/
class NullPointerType extends BuiltInType {
NullPointerType() { builtintypes(underlyingElement(this), _, 34, _, _, _) }
@@ -729,8 +875,13 @@ class NullPointerType extends BuiltInType {
/**
* A C/C++ derived type.
*
- * These are pointer and reference types, array and vector types, and const and volatile types.
- * In all cases, the type is formed from a single base type.
+ * These are pointer and reference types, array and GNU vector types, and `const` and `volatile` types.
+ * In all cases, the type is formed from a single base type. For example:
+ * ```
+ * int *pi;
+ * int &ri = *pi;
+ * const float fa[40];
+ * ```
*/
class DerivedType extends Type, @derivedtype {
override string toString() { result = this.getName() }
@@ -775,9 +926,15 @@ class DerivedType extends Type, @derivedtype {
}
/**
- * An instance of the C++11 decltype operator.
+ * An instance of the C++11 `decltype` operator. For example:
+ * ```
+ * int a;
+ * decltype(a) b;
+ * ```
*/
class Decltype extends Type, @decltype {
+ override string getCanonicalQLClass() { result = "Decltype" }
+
/**
* The expression whose type is being obtained by this decltype.
*/
@@ -788,17 +945,17 @@ class Decltype extends Type, @decltype {
*/
Type getBaseType() { decltypes(underlyingElement(this), _, unresolveElement(result), _) }
- override string getCanonicalQLClass() { result = "Decltype" }
-
/**
* Whether an extra pair of parentheses around the expression would change the semantics of this decltype.
*
* The following example shows the effect of an extra pair of parentheses:
- * struct A { double x; };
- * const A* a = new A();
- * decltype( a->x ); // type is double
- * decltype((a->x)); // type is const double&
- * Consult the C++11 standard for more details.
+ * ```
+ * struct A { double x; };
+ * const A* a = new A();
+ * decltype( a->x ); // type is double
+ * decltype((a->x)); // type is const double&
+ * ```
+ * Please consult the C++11 standard for more details.
*/
predicate parenthesesWouldChangeMeaning() { decltypes(underlyingElement(this), _, _, true) }
@@ -841,6 +998,10 @@ class Decltype extends Type, @decltype {
/**
* A C/C++ pointer type. See 4.9.1.
+ * ```
+ * void *ptr;
+ * void **ptr2 = &ptr;
+ * ```
*/
class PointerType extends DerivedType {
PointerType() { derivedtypes(underlyingElement(this), _, 1, _) }
@@ -863,8 +1024,8 @@ class PointerType extends DerivedType {
/**
* A C++ reference type. See 4.9.1.
*
- * For C++11 code bases, this includes both lvalue references (&) and rvalue references (&&).
- * To distinguish between them, use the LValueReferenceType and RValueReferenceType classes.
+ * For C++11 code bases, this includes both _lvalue_ references (`&`) and _rvalue_ references (`&&`).
+ * To distinguish between them, use the LValueReferenceType and RValueReferenceType QL classes.
*/
class ReferenceType extends DerivedType {
ReferenceType() {
@@ -889,7 +1050,11 @@ class ReferenceType extends DerivedType {
}
/**
- * A C++11 lvalue reference type (e.g. int&).
+ * A C++11 lvalue reference type (e.g. `int &`).
+ * ```
+ * int a;
+ * int& b = a;
+ * ```
*/
class LValueReferenceType extends ReferenceType {
LValueReferenceType() { derivedtypes(underlyingElement(this), _, 2, _) }
@@ -898,7 +1063,14 @@ class LValueReferenceType extends ReferenceType {
}
/**
- * A C++11 rvalue reference type (e.g. int&&).
+ * A C++11 rvalue reference type (e.g., `int &&`). It is used to
+ * implement "move" semantics for object construction and assignment.
+ * ```
+ * class C {
+ * E e;
+ * C(C&& from): e(std::move(from.e)) { }
+ * };
+ * ```
*/
class RValueReferenceType extends ReferenceType {
RValueReferenceType() { derivedtypes(underlyingElement(this), _, 8, _) }
@@ -910,6 +1082,10 @@ class RValueReferenceType extends ReferenceType {
/**
* A type with specifiers.
+ * ```
+ * const int a;
+ * volatile char v;
+ * ```
*/
class SpecifiedType extends DerivedType {
SpecifiedType() { derivedtypes(underlyingElement(this), _, 3, _) }
@@ -955,6 +1131,9 @@ class SpecifiedType extends DerivedType {
/**
* A C/C++ array type. See 4.9.1.
+ * ```
+ * char table[32];
+ * ```
*/
class ArrayType extends DerivedType {
ArrayType() { derivedtypes(underlyingElement(this), _, 4, _) }
@@ -1001,10 +1180,16 @@ class ArrayType extends DerivedType {
* A GNU/Clang vector type.
*
* In both Clang and GNU compilers, vector types can be introduced using the
- * __attribute__((vector_size(byte_size))) syntax. The Clang compiler also
- * allows vector types to be introduced using the ext_vector_type,
- * neon_vector_type, and neon_polyvector_type attributes (all of which take
- * an element type rather than a byte size).
+ * `__attribute__((vector_size(byte_size)))` syntax. The Clang compiler also
+ * allows vector types to be introduced using the `ext_vector_type`,
+ * `neon_vector_type`, and `neon_polyvector_type` attributes (all of which take
+ * an element count rather than a byte size).
+ *
+ * In the example below, both `v4si` and `float4` are GNU vector types:
+ * ```
+ * typedef int v4si __attribute__ (( vector_size(4*sizeof(int)) ));
+ * typedef float float4 __attribute__((ext_vector_type(4)));
+ * ```
*/
class GNUVectorType extends DerivedType {
GNUVectorType() { derivedtypes(underlyingElement(this), _, 5, _) }
@@ -1043,7 +1228,10 @@ class GNUVectorType extends DerivedType {
}
/**
- * A C/C++ pointer to function. See 7.7.
+ * A C/C++ pointer to a function. See 7.7.
+ * ```
+ * int(* pointer)(const void *element1, const void *element2);
+ * ```
*/
class FunctionPointerType extends FunctionPointerIshType {
FunctionPointerType() { derivedtypes(underlyingElement(this), _, 6, _) }
@@ -1058,7 +1246,10 @@ class FunctionPointerType extends FunctionPointerIshType {
}
/**
- * A C/C++ reference to function.
+ * A C++ reference to a function.
+ * ```
+ * int(& reference)(const void *element1, const void *element2);
+ * ```
*/
class FunctionReferenceType extends FunctionPointerIshType {
FunctionReferenceType() { derivedtypes(underlyingElement(this), _, 7, _) }
@@ -1073,10 +1264,14 @@ class FunctionReferenceType extends FunctionPointerIshType {
}
/**
- * A block type, for example int(^)(char, float).
+ * A block type, for example, `int(^)(char, float)`.
*
* Block types (along with blocks themselves) are a language extension
* supported by Clang, and by Apple's branch of GCC.
+ * ```
+ * int(^ block)(const char *element1, const char *element2)
+ * = ^int (const char *element1, const char *element2) { return element1 - element 2; }
+ * ```
*/
class BlockType extends FunctionPointerIshType {
BlockType() { derivedtypes(underlyingElement(this), _, 10, _) }
@@ -1089,7 +1284,9 @@ class BlockType extends FunctionPointerIshType {
}
/**
- * A C/C++ pointer to function, or a block.
+ * A C/C++ pointer to a function, a C++ function reference, or a clang/Apple block.
+ *
+ * See `FunctionPointerType`, `FunctionReferenceType` and `BlockType` for more information.
*/
class FunctionPointerIshType extends DerivedType {
FunctionPointerIshType() {
@@ -1134,7 +1331,13 @@ class FunctionPointerIshType extends DerivedType {
}
/**
- * A C++ pointer to member. See 15.5.
+ * A C++ pointer to data member. See 15.5.
+ * ```
+ * class C { int m; };
+ * int C::* p = &C::m; // pointer to data member m of class C
+ * class C *;
+ * int val = c.*p; // access data member
+ * ```
*/
class PointerToMemberType extends Type, @ptrtomember {
/** a printable representation of this named element */
@@ -1171,7 +1374,14 @@ class PointerToMemberType extends Type, @ptrtomember {
}
/**
- * A C/C++ routine type. This is what results from stripping away the pointer from a function pointer type.
+ * A C/C++ routine type. Conceptually, this is what results from stripping
+ * away the pointer from a function pointer type. It can also occur in C++
+ * code, for example the base type of `myRoutineType` in the following code:
+ * ```
+ * using myRoutineType = int(int);
+ *
+ * myRoutineType *fp = 0;
+ * ```
*/
class RoutineType extends Type, @routinetype {
/** a printable representation of this named element */
@@ -1231,7 +1441,13 @@ class RoutineType extends Type, @routinetype {
}
/**
- * A C++ typename template parameter.
+ * A C++ `typename` (or `class`) template parameter.
+ *
+ * In the example below, `T` is a template parameter:
+ * ```
+ * template
+ * class C { };
+ * ```
*/
class TemplateParameter extends UserType {
TemplateParameter() {
@@ -1243,7 +1459,16 @@ class TemplateParameter extends UserType {
override predicate involvesTemplateParameter() { any() }
}
-/** A C++ template template parameter, e.g. template <template <typename,typename> class T>. */
+/**
+ * A C++ template template parameter.
+ *
+ * In the example below, `T` is a template template parameter (although its name
+ * may be omitted):
+ * ```
+ * template class Container, class Elem>
+ * void foo(const Container &value) { }
+ * ```
+ */
class TemplateTemplateParameter extends TemplateParameter {
TemplateTemplateParameter() { usertypes(underlyingElement(this), _, 8) }
@@ -1251,7 +1476,10 @@ class TemplateTemplateParameter extends TemplateParameter {
}
/**
- * A type representing the use of the C++11 auto keyword.
+ * A type representing the use of the C++11 `auto` keyword.
+ * ```
+ * auto val = some_typed_expr();
+ * ```
*/
class AutoType extends TemplateParameter {
AutoType() { usertypes(underlyingElement(this), "auto", 7) }
diff --git a/cpp/ql/src/semmle/code/cpp/TypedefType.qll b/cpp/ql/src/semmle/code/cpp/TypedefType.qll
index b7fd5f294fa..504333aeedc 100644
--- a/cpp/ql/src/semmle/code/cpp/TypedefType.qll
+++ b/cpp/ql/src/semmle/code/cpp/TypedefType.qll
@@ -2,12 +2,17 @@ import semmle.code.cpp.Type
private import semmle.code.cpp.internal.ResolveClass
/**
- * A C/C++ typedef type. See 4.9.1.
+ * A C/C++ typedef type. See 4.9.1. For example the types declared on each line of the following code:
+ * ```
+ * typedef int my_int;
+ * using my_int2 = int;
+ * ```
*/
class TypedefType extends UserType {
- TypedefType() { usertypes(underlyingElement(this), _, 5) }
-
- override string getCanonicalQLClass() { result = "TypedefType" }
+ TypedefType() {
+ usertypes(underlyingElement(this), _, 5) or
+ usertypes(underlyingElement(this), _, 14)
+ }
/**
* Gets the base type of this typedef type.
@@ -26,10 +31,6 @@ class TypedefType extends UserType {
result = this.getBaseType().getPointerIndirectionLevel()
}
- override string explain() {
- result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
- }
-
override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // Just an alias
override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } // Just an alias
@@ -46,7 +47,43 @@ class TypedefType extends UserType {
}
/**
- * A C++ typedef type that is directly enclosed by a function.
+ * A traditional C/C++ typedef type. See 4.9.1. For example the type declared in the following code:
+ * ```
+ * typedef int my_int;
+ * ```
+ */
+class CTypedefType extends TypedefType {
+ CTypedefType() { usertypes(underlyingElement(this), _, 5) }
+
+ override string getCanonicalQLClass() { result = "CTypedefType" }
+
+ override string explain() {
+ result = "typedef {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
+ }
+}
+
+/**
+ * A using alias C++ typedef type. For example the type declared in the following code:
+ * ```
+ * using my_int2 = int;
+ * ```
+ */
+class UsingAliasTypedefType extends TypedefType {
+ UsingAliasTypedefType() { usertypes(underlyingElement(this), _, 14) }
+
+ override string getCanonicalQLClass() { result = "UsingAliasTypedefType" }
+
+ override string explain() {
+ result = "using {" + this.getBaseType().explain() + "} as \"" + this.getName() + "\""
+ }
+}
+
+/**
+ * A C++ `typedef` type that is directly enclosed by a function. For example the type declared inside the function `foo` in
+ * the following code:
+ * ```
+ * int foo(void) { typedef int local; }
+ * ```
*/
class LocalTypedefType extends TypedefType {
LocalTypedefType() { isLocal() }
@@ -55,7 +92,11 @@ class LocalTypedefType extends TypedefType {
}
/**
- * A C++ typedef type that is directly enclosed by a class, struct or union.
+ * A C++ `typedef` type that is directly enclosed by a `class`, `struct` or `union`. For example the type declared inside
+ * the class `C` in the following code:
+ * ```
+ * class C { typedef int nested; };
+ * ```
*/
class NestedTypedefType extends TypedefType {
NestedTypedefType() { this.isMember() }
diff --git a/cpp/ql/src/semmle/code/cpp/UserType.qll b/cpp/ql/src/semmle/code/cpp/UserType.qll
index e41d3ecd664..cbb7f39adbd 100644
--- a/cpp/ql/src/semmle/code/cpp/UserType.qll
+++ b/cpp/ql/src/semmle/code/cpp/UserType.qll
@@ -5,8 +5,14 @@ import semmle.code.cpp.Function
private import semmle.code.cpp.internal.ResolveClass
/**
- * A C/C++ user-defined type. Examples include `Class`, `Struct`, `Union`,
- * `Enum`, and `TypedefType`.
+ * A C/C++ user-defined type. Examples include `class`, `struct`, `union`,
+ * `enum` and `typedef` types.
+ * ```
+ * enum e1 { val1, val2 } b;
+ * enum class e2: short { val3, val4 } c;
+ * typedef int my_int;
+ * class C { int a, b; };
+ * ```
*/
class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @usertype {
/**
@@ -88,6 +94,10 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
/**
* A particular definition or forward declaration of a C/C++ user-defined type.
+ * ```
+ * class C;
+ * typedef int ti;
+ * ```
*/
class TypeDeclarationEntry extends DeclarationEntry, @type_decl {
override UserType getDeclaration() { result = getType() }
diff --git a/cpp/ql/src/semmle/code/cpp/Variable.qll b/cpp/ql/src/semmle/code/cpp/Variable.qll
index ee1b41109f4..194cc5333c7 100644
--- a/cpp/ql/src/semmle/code/cpp/Variable.qll
+++ b/cpp/ql/src/semmle/code/cpp/Variable.qll
@@ -155,15 +155,6 @@ class Variable extends Declaration, @variable {
variable_instantiation(underlyingElement(this), unresolveElement(v))
}
- /**
- * Gets the `i`th template argument used to instantiate this variable from a
- * variable template. When called on a variable template, this will return the
- * `i`th template parameter.
- */
- override Type getTemplateArgument(int index) {
- variable_template_argument(underlyingElement(this), index, unresolveElement(result))
- }
-
/**
* Holds if this is a compiler-generated variable. For example, a
* [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for)
@@ -315,7 +306,7 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
* static int c;
* }
* ```
- *
+ *
* Local variables can be static; use the `isStatic` member predicate to
* detect those.
*/
@@ -343,7 +334,7 @@ deprecated class StackVariable extends Variable {
* static int c;
* }
* ```
- *
+ *
* Local variables can be static; use the `isStatic` member predicate to detect
* those.
*
@@ -512,9 +503,9 @@ class TemplateVariable extends Variable {
* void myTemplateFunction() {
* T b;
* }
- *
+ *
* ...
- *
+ *
* myTemplateFunction();
* ```
*/
diff --git a/cpp/ql/src/semmle/code/cpp/commons/Alloc.qll b/cpp/ql/src/semmle/code/cpp/commons/Alloc.qll
index e90aea37781..8462cb13b1d 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Alloc.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Alloc.qll
@@ -5,13 +5,17 @@ import cpp
*/
predicate allocationFunction(Function f) {
exists(string name |
- f.hasGlobalName(name) and
+ f.hasGlobalOrStdName(name) and
(
name = "malloc" or
name = "calloc" or
name = "realloc" or
name = "strdup" or
- name = "wcsdup" or
+ name = "wcsdup"
+ )
+ or
+ f.hasGlobalName(name) and
+ (
name = "_strdup" or
name = "_wcsdup" or
name = "_mbsdup" or
@@ -59,7 +63,7 @@ predicate allocationCall(FunctionCall fc) {
allocationFunction(fc.getTarget()) and
(
// realloc(ptr, 0) only frees the pointer
- fc.getTarget().hasGlobalName("realloc") implies not fc.getArgument(1).getValue() = "0"
+ fc.getTarget().hasGlobalOrStdName("realloc") implies not fc.getArgument(1).getValue() = "0"
)
}
@@ -73,7 +77,10 @@ predicate freeFunction(Function f, int argNum) {
name = "free" and argNum = 0
or
name = "realloc" and argNum = 0
- or
+ )
+ or
+ f.hasGlobalOrStdName(name) and
+ (
name = "ExFreePoolWithTag" and argNum = 0
or
name = "ExFreeToLookasideListEx" and argNum = 1
diff --git a/cpp/ql/src/semmle/code/cpp/commons/Buffer.qll b/cpp/ql/src/semmle/code/cpp/commons/Buffer.qll
index 5db9648124c..83147e5a705 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Buffer.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Buffer.qll
@@ -44,7 +44,7 @@ predicate memberMayBeVarSize(Class c, MemberVariable v) {
aoe.getAddressable() = v
)
or
- exists(BuiltInOperationOffsetOf oo |
+ exists(BuiltInOperationBuiltInOffsetOf oo |
// `offsetof(c, v)` using a builtin
oo.getAChild().(VariableAccess).getTarget() = v
)
diff --git a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll
index c4eb0ff1c2a..f584a8e4802 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/CommonType.qll
@@ -174,9 +174,7 @@ class MicrosoftInt64Type extends IntegralType {
* `__builtin_va_copy` and `__builtin_va_arg` expressions.
*/
class BuiltInVarArgsList extends Type {
- BuiltInVarArgsList() {
- this.hasName("__builtin_va_list")
- }
+ BuiltInVarArgsList() { this.hasName("__builtin_va_list") }
override string getCanonicalQLClass() { 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 e36244f4d5c..f3f1759dd5c 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Environment.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Environment.qll
@@ -28,7 +28,7 @@ class EnvironmentRead extends Expr {
private predicate readsEnvironment(Expr read, string sourceDescription) {
exists(FunctionCall call, string name |
read = call and
- call.getTarget().hasGlobalName(name) and
+ call.getTarget().hasGlobalOrStdName(name) and
(name = "getenv" or name = "secure_getenv" or name = "_wgetenv") and
sourceDescription = name
)
diff --git a/cpp/ql/src/semmle/code/cpp/commons/Exclusions.qll b/cpp/ql/src/semmle/code/cpp/commons/Exclusions.qll
index b27f11a9ab3..cae647982d2 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Exclusions.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Exclusions.qll
@@ -79,3 +79,26 @@ predicate functionContainsPreprocCode(Function f) {
pbdStartLine >= fBlockStartLine
)
}
+
+/**
+ * Holds if `e` is completely or partially from a macro definition, as opposed
+ * to being passed in as an argument.
+ *
+ * In the following example, the call to `f` is from a macro definition,
+ * while `y`, `+`, `1`, and `;` are not. This assumes that no identifier apart
+ * from `M` refers to a macro.
+ * ```
+ * #define M(x) f(x)
+ * ...
+ * M(y + 1);
+ * ```
+ */
+predicate isFromMacroDefinition(Element e) {
+ exists(MacroInvocation mi |
+ // e is in mi
+ mi.getAnExpandedElement() = e and
+ // and e was apparently not passed in as a macro parameter
+ e.getLocation().getStartLine() = mi.getLocation().getStartLine() and
+ e.getLocation().getStartColumn() = mi.getLocation().getStartColumn()
+ )
+}
diff --git a/cpp/ql/src/semmle/code/cpp/commons/File.qll b/cpp/ql/src/semmle/code/cpp/commons/File.qll
index 192918d25b3..5808d704e38 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/File.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/File.qll
@@ -5,7 +5,7 @@ import cpp
*/
predicate fopenCall(FunctionCall fc) {
exists(Function f | f = fc.getTarget() |
- f.hasGlobalName("fopen") or
+ f.hasGlobalOrStdName("fopen") or
f.hasGlobalName("open") or
f.hasGlobalName("_open") or
f.hasGlobalName("_wopen") or
@@ -23,7 +23,7 @@ predicate fopenCall(FunctionCall fc) {
*/
predicate fcloseCall(FunctionCall fc, Expr closed) {
exists(Function f | f = fc.getTarget() |
- f.hasGlobalName("fclose") and
+ f.hasGlobalOrStdName("fclose") and
closed = fc.getArgument(0)
or
f.hasGlobalName("close") and
@@ -32,7 +32,7 @@ predicate fcloseCall(FunctionCall fc, Expr closed) {
f.hasGlobalName("_close") and
closed = fc.getArgument(0)
or
- f.hasGlobalName("CloseHandle") and
+ f.hasGlobalOrStdName("CloseHandle") and
closed = fc.getArgument(0)
)
}
diff --git a/cpp/ql/src/semmle/code/cpp/commons/Printf.qll b/cpp/ql/src/semmle/code/cpp/commons/Printf.qll
index a7ab7c25b86..5bb60564c1b 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/Printf.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/Printf.qll
@@ -8,25 +8,32 @@ import semmle.code.cpp.commons.StringAnalysis
import semmle.code.cpp.models.interfaces.FormattingFunction
import semmle.code.cpp.models.implementations.Printf
+class PrintfFormatAttribute extends FormatAttribute {
+ PrintfFormatAttribute() {
+ getArchetype() = "printf" or
+ getArchetype() = "__printf__"
+ }
+}
+
/**
* A function that can be identified as a `printf` style formatting
* function by its use of the GNU `format` attribute.
*/
class AttributeFormattingFunction extends FormattingFunction {
- FormatAttribute printf_attrib;
-
override string getCanonicalQLClass() { result = "AttributeFormattingFunction" }
AttributeFormattingFunction() {
- printf_attrib = getAnAttribute() and
- (
- printf_attrib.getArchetype() = "printf" or
- printf_attrib.getArchetype() = "__printf__"
- ) and
- exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions
+ exists(PrintfFormatAttribute printf_attrib |
+ printf_attrib = getAnAttribute() and
+ exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions
+ )
}
- override int getFormatParameterIndex() { result = printf_attrib.getFormatIndex() }
+ override int getFormatParameterIndex() {
+ forex(PrintfFormatAttribute printf_attrib | printf_attrib = getAnAttribute() |
+ result = printf_attrib.getFormatIndex()
+ )
+ }
}
/**
@@ -124,15 +131,17 @@ class FormattingFunctionCall extends Expr {
}
/**
- * Gets the argument corresponding to the nth conversion specifier
+ * Gets the argument corresponding to the nth conversion specifier.
*/
Expr getConversionArgument(int n) {
- exists(FormatLiteral fl, int b, int o |
+ exists(FormatLiteral fl |
fl = this.getFormat() and
- b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
- o = fl.getNumArgNeeded(n) and
- o > 0 and
- result = this.getFormatArgument(b + o - 1)
+ (
+ result = this.getFormatArgument(fl.getParameterFieldValue(n))
+ or
+ result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 2)) and
+ not exists(fl.getParameterFieldValue(n))
+ )
)
}
@@ -142,11 +151,14 @@ class FormattingFunctionCall extends Expr {
* an explicit minimum field width).
*/
Expr getMinFieldWidthArgument(int n) {
- exists(FormatLiteral fl, int b |
+ exists(FormatLiteral fl |
fl = this.getFormat() and
- b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
- fl.hasImplicitMinFieldWidth(n) and
- result = this.getFormatArgument(b)
+ (
+ result = this.getFormatArgument(fl.getMinFieldWidthParameterFieldValue(n))
+ or
+ result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 0)) and
+ not exists(fl.getMinFieldWidthParameterFieldValue(n))
+ )
)
}
@@ -156,12 +168,14 @@ class FormattingFunctionCall extends Expr {
* precision).
*/
Expr getPrecisionArgument(int n) {
- exists(FormatLiteral fl, int b, int o |
+ exists(FormatLiteral fl |
fl = this.getFormat() and
- b = sum(int i, int toSum | i < n and toSum = fl.getNumArgNeeded(i) | toSum) and
- (if fl.hasImplicitMinFieldWidth(n) then o = 1 else o = 0) and
- fl.hasImplicitPrecision(n) and
- result = this.getFormatArgument(b + o)
+ (
+ result = this.getFormatArgument(fl.getPrecisionParameterFieldValue(n))
+ or
+ result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 1)) and
+ not exists(fl.getPrecisionParameterFieldValue(n))
+ )
)
}
@@ -169,7 +183,11 @@ class FormattingFunctionCall extends Expr {
* Gets the number of arguments to this call that are parameters to the
* format string.
*/
- int getNumFormatArgument() { result = count(this.getFormatArgument(_)) }
+ int getNumFormatArgument() {
+ result = count(this.getFormatArgument(_)) and
+ // format arguments must be known
+ exists(getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
+ }
}
/**
@@ -357,6 +375,14 @@ class FormatLiteral extends Literal {
*/
string getParameterField(int n) { this.parseConvSpec(n, _, result, _, _, _, _, _) }
+ /**
+ * Gets the parameter field of the nth conversion specifier (if it has one) as a
+ * zero-based number.
+ */
+ int getParameterFieldValue(int n) {
+ result = this.getParameterField(n).regexpCapture("([0-9]*)\\$", 1).toInt() - 1
+ }
+
/**
* Gets the flags of the nth conversion specifier.
*/
@@ -426,6 +452,14 @@ class FormatLiteral extends Literal {
*/
int getMinFieldWidth(int n) { result = this.getMinFieldWidthOpt(n).toInt() }
+ /**
+ * Gets the zero-based parameter number of the minimum field width of the nth
+ * conversion specifier, if it is implicit and uses a parameter field (such as `*1$`).
+ */
+ int getMinFieldWidthParameterFieldValue(int n) {
+ result = this.getMinFieldWidthOpt(n).regexpCapture("\\*([0-9]*)\\$", 1).toInt() - 1
+ }
+
/**
* Gets the precision of the nth conversion specifier (empty string if none is given).
*/
@@ -456,6 +490,14 @@ class FormatLiteral extends Literal {
else result = this.getPrecisionOpt(n).regexpCapture("\\.([0-9]*)", 1).toInt()
}
+ /**
+ * Gets the zero-based parameter number of the precision of the nth conversion
+ * specifier, if it is implicit and uses a parameter field (such as `*1$`).
+ */
+ int getPrecisionParameterFieldValue(int n) {
+ result = this.getPrecisionOpt(n).regexpCapture("\\.\\*([0-9]*)\\$", 1).toInt() - 1
+ }
+
/**
* Gets the length flag of the nth conversion specifier.
*/
@@ -773,19 +815,49 @@ class FormatLiteral extends Literal {
)
}
+ /**
+ * Holds if the nth conversion specifier of this format string (if `mode = 2`), it's
+ * minimum field width (if `mode = 0`) or it's precision (if `mode = 1`) requires a
+ * format argument.
+ *
+ * Most conversion specifiers require a format argument, whereas minimum field width
+ * and precision only require a format argument if they are present and a `*` was
+ * used for it's value in the format string.
+ */
+ private predicate hasFormatArgumentIndexFor(int n, int mode) {
+ mode = 0 and
+ this.hasImplicitMinFieldWidth(n)
+ or
+ mode = 1 and
+ this.hasImplicitPrecision(n)
+ or
+ mode = 2 and
+ exists(this.getConvSpecOffset(n)) and
+ not this.getConversionChar(n) = "m"
+ }
+
+ /**
+ * Gets the computed format argument index for the nth conversion specifier of this
+ * format string (if `mode = 2`), it's minimum field width (if `mode = 0`) or it's
+ * precision (if `mode = 1`). Has no result if that element is not present. Does
+ * not account for positional arguments (`$`).
+ */
+ int getFormatArgumentIndexFor(int n, int mode) {
+ hasFormatArgumentIndexFor(n, mode) and
+ (3 * n) + mode = rank[result + 1](int n2, int mode2 |
+ hasFormatArgumentIndexFor(n2, mode2)
+ |
+ (3 * n2) + mode2
+ )
+ }
+
/**
* Gets the number of arguments required by the nth conversion specifier
* of this format string.
*/
int getNumArgNeeded(int n) {
exists(this.getConvSpecOffset(n)) and
- not this.getConversionChar(n) = "%" and
- exists(int n1, int n2, int n3 |
- (if this.hasImplicitMinFieldWidth(n) then n1 = 1 else n1 = 0) and
- (if this.hasImplicitPrecision(n) then n2 = 1 else n2 = 0) and
- (if this.getConversionChar(n) = "m" then n3 = 0 else n3 = 1) and
- result = n1 + n2 + n3
- )
+ result = count(int mode | hasFormatArgumentIndexFor(n, mode))
}
/**
@@ -797,7 +869,7 @@ class FormatLiteral extends Literal {
// At least one conversion specifier has a parameter field, in which case,
// they all should have.
result = max(string s | this.getParameterField(_) = s + "$" | s.toInt())
- else result = sum(int n, int toSum | toSum = this.getNumArgNeeded(n) | toSum)
+ else result = count(int n, int mode | hasFormatArgumentIndexFor(n, mode))
}
/**
diff --git a/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll b/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll
index e10e52df07d..772bb42e319 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/StringAnalysis.qll
@@ -53,8 +53,8 @@ class AnalysedString extends Expr {
*/
class StrlenCall extends FunctionCall {
StrlenCall() {
- this.getTarget().hasGlobalName("strlen") or
- this.getTarget().hasGlobalName("wcslen") or
+ this.getTarget().hasGlobalOrStdName("strlen") or
+ this.getTarget().hasGlobalOrStdName("wcslen") or
this.getTarget().hasGlobalName("_mbslen") or
this.getTarget().hasGlobalName("_mbslen_l") or
this.getTarget().hasGlobalName("_mbstrlen") or
diff --git a/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll b/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll
index 08def499e13..4b0b1299fd8 100644
--- a/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll
+++ b/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll
@@ -35,7 +35,8 @@ private predicate exprInVoidContext(Expr e) {
exists(CommaExpr c | c.getLeftOperand() = e)
or
exists(CommaExpr c | c.getRightOperand() = e and c instanceof ExprInVoidContext)
+ or
+ exists(ForStmt for | for.getUpdate() = e)
) and
- not e instanceof Qualifier and
not e.getActualType() instanceof VoidType
}
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll
index e4965cb8637..9174f474a8f 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll
@@ -1,6 +1,7 @@
import cpp
import BasicBlocks
private import semmle.code.cpp.controlflow.internal.ConstantExprs
+private import semmle.code.cpp.controlflow.internal.CFG
/**
* A control-flow node is either a statement or an expression; in addition,
@@ -86,11 +87,11 @@ import ControlFlowGraphPublic
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
- truecond(unresolveElement(n1), unresolveElement(n2))
+ qlCFGTrueSuccessor(n1, n2)
}
predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
- falsecond(unresolveElement(n1), unresolveElement(n2))
+ qlCFGFalseSuccessor(n1, n2)
}
/**
@@ -120,7 +121,7 @@ abstract class AdditionalControlFlowEdge extends ControlFlowNodeBase {
* `AdditionalControlFlowEdge`. Use this relation instead of `successors`.
*/
predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) {
- successors(unresolveElement(source), unresolveElement(target))
+ qlCFGSuccessor(source, target)
or
source.(AdditionalControlFlowEdge).getAnEdgeTarget() = target
}
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Dereferenced.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Dereferenced.qll
index cfd3efa91f3..69c5963af30 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/Dereferenced.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/Dereferenced.qll
@@ -6,7 +6,7 @@ import Nullness
*/
predicate callDereferences(FunctionCall fc, int i) {
exists(string name |
- fc.getTarget().hasGlobalName(name) and
+ fc.getTarget().hasGlobalOrStdName(name) and
(
name = "bcopy" and i in [0 .. 1]
or
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll
index 3164f44e451..c64f724975d 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/Guards.qll
@@ -156,7 +156,7 @@ private predicate compares_eq(
/**
* If `test => part` and `part => left == right + k` then `test => left == right + k`.
- * Similarly for the case where `test` is false.
+ * Similarly for the case where `test` is false.
*/
private predicate logical_comparison_eq(
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue
@@ -275,7 +275,7 @@ private predicate compares_ge(
/**
* If `test => part` and `part => left < right + k` then `test => left < right + k`.
- * Similarly for the case where `test` evaluates false.
+ * Similarly for the case where `test` evaluates false.
*/
private predicate logical_comparison_lt(
BinaryLogicalOperation test, Expr left, Expr right, int k, boolean isLt, boolean testIsTrue
@@ -362,7 +362,7 @@ private predicate add_lt(
)
}
-/** The int value of integer constant expression. */
+/** The `int` value of integer constant expression. */
private int int_value(Expr e) {
e.getUnderlyingType() instanceof IntegralType and
result = e.getValue().toInt()
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Nullness.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Nullness.qll
index 54b14aadd9d..caaa5b54e8c 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/Nullness.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/Nullness.qll
@@ -264,9 +264,9 @@ predicate callMayReturnNull(Call call) {
* Holds if `f` may, directly or indirectly, return a null literal.
*/
predicate mayReturnNull(Function f) {
- f.hasGlobalName("malloc")
+ f.hasGlobalOrStdName("malloc")
or
- f.hasGlobalName("calloc")
+ f.hasGlobalOrStdName("calloc")
or
// f.hasGlobalName("strchr")
// or
diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/SSA.qll b/cpp/ql/src/semmle/code/cpp/controlflow/SSA.qll
index 9f7c1c88b24..8384a5b3c66 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/SSA.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/SSA.qll
@@ -29,11 +29,11 @@ class SsaDefinition extends ControlFlowNodeBase {
/**
* Gets a string representation of the SSA variable represented by the pair
- * (this, v).
+ * `(this, v)`.
*/
string toString(LocalScopeVariable v) { exists(StandardSSA x | result = x.toString(this, v)) }
- /** Gets a use of the SSA variable represented by the pair (this, v). */
+ /** Gets a use of the SSA variable represented by the pair `(this, v)`. */
VariableAccess getAUse(LocalScopeVariable v) {
exists(StandardSSA x | result = x.getAUse(this, v))
}
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 3200987c347..faf8f913750 100644
--- a/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll
+++ b/cpp/ql/src/semmle/code/cpp/controlflow/internal/CFG.qll
@@ -132,7 +132,7 @@ private predicate excludeNodeAndNodesBelow(Expr e) {
* control flow in them.
*/
private predicate excludeNodesStrictlyBelow(Node n) {
- n instanceof BuiltInOperationOffsetOf
+ n instanceof BuiltInOperationBuiltInOffsetOf
or
n instanceof BuiltInIntAddr
or
@@ -173,49 +173,36 @@ predicate excludeNode(Node n) {
excludeNode(n.getParentNode())
}
-private newtype TPos =
- PosBefore() or
- PosAt() or
- PosAfter() or
- PosBeforeDestructors() or
- PosAfterDestructors()
-
-/** A `Pos` without a `bindingset` requirement on the constructor. */
-private class AnyPos extends TPos {
- string toString() { result = "Pos" }
-}
-
/**
* A constant that indicates the type of sub-node in a pair of `(Node, Pos)`.
* See the comment block at the top of this file.
*/
-private class Pos extends AnyPos {
- // This is to make sure we get compile errors in code that forgets to restrict a `Pos`.
+private class Pos extends int {
bindingset[this]
Pos() { any() }
/** Holds if this is the position just _before_ the associated `Node`. */
- predicate isBefore() { this = PosBefore() }
+ predicate isBefore() { this = 0 }
/** Holds if `(n, this)` is the sub-node that represents `n` itself. */
- predicate isAt() { this = PosAt() }
+ predicate isAt() { this = 1 }
/** Holds if this is the position just _after_ the associated `Node`. */
- predicate isAfter() { this = PosAfter() }
+ predicate isAfter() { this = 2 }
/**
* Holds if `(n, this)` is the virtual sub-node that comes just _before_ any
* implicit destructor calls following `n`. The node `n` will be some node
* that may be followed by local variables going out of scope.
*/
- predicate isBeforeDestructors() { this = PosBeforeDestructors() }
+ predicate isBeforeDestructors() { this = 3 }
/**
* Holds if `(n, this)` is the virtual sub-node that comes just _after_ any
* implicit destructor calls following `n`. The node `n` will be some node
* that may be followed by local variables going out of scope.
*/
- predicate isAfterDestructors() { this = PosAfterDestructors() }
+ predicate isAfterDestructors() { this = 4 }
pragma[inline]
predicate nodeBefore(Node n, Node nEq) { this.isBefore() and n = nEq }
@@ -489,17 +476,6 @@ private Node getLastControlOrderChild(Node n) {
result = getControlOrderChildDense(n, max(int i | exists(getControlOrderChildDense(n, i))))
}
-private newtype TSpec =
- SpecPos(AnyPos p) or
- SpecAround() or
- SpecAroundDestructors() or
- SpecBarrier()
-
-/** A `Spec` without a `bindingset` requirement on the constructor. */
-private class AnySpec extends TSpec {
- string toString() { result = "Spec" }
-}
-
/**
* A constant that represents two positions: one position for when it's used as
* the _source_ of a sub-edge, and another position for when it's used as the
@@ -507,25 +483,10 @@ private class AnySpec extends TSpec {
* themselves as both source and target, as well as two _around_ values and a
* _barrier_ value.
*/
-private class Spec extends AnySpec {
+private class Spec extends Pos {
bindingset[this]
Spec() { any() }
- /** See Pos.isBefore. */
- predicate isBefore() { this = SpecPos(PosBefore()) }
-
- /** See Pos.isAt. */
- predicate isAt() { this = SpecPos(PosAt()) }
-
- /** See Pos.isAfter. */
- predicate isAfter() { this = SpecPos(PosAfter()) }
-
- /** See Pos.isBeforeDestructors. */
- predicate isBeforeDestructors() { this = SpecPos(PosBeforeDestructors()) }
-
- /** See Pos.isAfterDestructors. */
- predicate isAfterDestructors() { this = SpecPos(PosAfterDestructors()) }
-
/**
* Holds if this spec, when used on a node `n` between `(n1, p1)` and
* `(n2, p2)`, should add the following sub-edges.
@@ -533,7 +494,7 @@ private class Spec extends AnySpec {
* (n1, p1) ----> before(n)
* after(n) ----> (n2, p2)
*/
- predicate isAround() { this = SpecAround() }
+ predicate isAround() { this = 5 }
/**
* Holds if this spec, when used on a node `n` between `(n1, p1)` and
@@ -542,16 +503,17 @@ private class Spec extends AnySpec {
* (n1, p1) ----> beforeDestructors(n)
* afterDestructors(n) ----> (n2, p2)
*/
- predicate isAroundDestructors() { this = SpecAroundDestructors() }
+ predicate isAroundDestructors() { this = 6 }
/**
* Holds if this node is a _barrier_. A barrier resolves to no positions and
* can be inserted between nodes that should have no sub-edges between them.
*/
- predicate isBarrier() { this = SpecBarrier() }
+ predicate isBarrier() { this = 7 }
Pos getSourcePos() {
- this = SpecPos(result)
+ this = [0 .. 4] and
+ result = this
or
this.isAround() and
result.isAfter()
@@ -561,7 +523,8 @@ private class Spec extends AnySpec {
}
Pos getTargetPos() {
- this = SpecPos(result)
+ this = [0 .. 4] and
+ result = this
or
this.isAround() and
result.isBefore()
@@ -888,6 +851,21 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
p2.nodeAfter(n2, s)
)
or
+ // ConstexprIfStmt -> condition ; { then, else } -> // same as IfStmt
+ exists(ConstexprIfStmt s |
+ p1.nodeAt(n1, s) and
+ p2.nodeBefore(n2, s.getCondition())
+ or
+ p1.nodeAfter(n1, s.getThen()) and
+ p2.nodeBeforeDestructors(n2, s)
+ or
+ p1.nodeAfter(n1, s.getElse()) and
+ p2.nodeBeforeDestructors(n2, s)
+ or
+ p1.nodeAfterDestructors(n1, s) and
+ p2.nodeAfter(n2, s)
+ )
+ or
// WhileStmt -> condition ; body -> condition ; after dtors -> after
exists(WhileStmt s |
p1.nodeAt(n1, s) and
@@ -1175,9 +1153,8 @@ private class ExceptionSource extends Node {
}
/**
- * Holds if `test` is the test of a control-flow construct that will always
- * have true/false sub-edges out of it, where the `truth`-sub-edge goes to
- * `(n2, p2)`.
+ * Holds if `test` is the test of a control-flow construct where the `truth`
+ * sub-edge goes to `(n2, p2)`.
*/
private predicate conditionJumpsTop(Expr test, boolean truth, Node n2, Pos p2) {
exists(IfStmt s | test = s.getCondition() |
@@ -1192,6 +1169,24 @@ private predicate conditionJumpsTop(Expr test, boolean truth, Node n2, Pos p2) {
p2.nodeBeforeDestructors(n2, s)
)
or
+ exists(ConstexprIfStmt s, string cond |
+ test = s.getCondition() and
+ cond = test.getFullyConverted().getValue()
+ |
+ truth = true and
+ cond != "0" and
+ p2.nodeBefore(n2, s.getThen())
+ or
+ truth = false and
+ cond = "0" and
+ p2.nodeBefore(n2, s.getElse())
+ or
+ not exists(s.getElse()) and
+ truth = false and
+ cond = "0" and
+ p2.nodeBeforeDestructors(n2, s)
+ )
+ or
exists(Loop l |
(
l instanceof WhileStmt
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 2da1d89e499..7ecb474f632 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -7,7 +7,7 @@
* on each other without introducing mutual recursion among those configurations.
*/
-private import DataFlowImplCommon
+private import DataFlowImplCommon::Public
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
- viableImpl(call) = result.getCallable() and
+private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
+ viableCallable(call) = result.getCallable() and
kind = result.getKind()
}
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
viableParamArg(_, node, arg)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- nodeCandFwd1(mid, config) and
- parameterValueFlowsToUpdate(p, mid) and
- viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
- nodeCandFwd1(ret, config) and
- getReturnPosition(ret) = viableReturnPos(call, kind) and
- node = getAnOutNode(call, kind)
+ exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
+ nodeCandFwd1ReturnPosition(pos, config) and
+ pos = viableReturnPos(call, kind) and
+ node = kind.getAnOutNode(call)
)
)
}
+pragma[noinline]
+private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(ReturnNodeExt ret |
+ nodeCandFwd1(ret, config) and
+ getReturnPosition(ret) = pos
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
exists(Node mid |
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
nodeCand1(param, config)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- parameterValueFlowsToUpdate(p, node) and
- viableParamArg(_, p, mid.getPreUpdateNode()) and
- nodeCand1(mid, config)
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind, OutNode out |
- nodeCand1(out, config) and
- getReturnPosition(node) = viableReturnPos(call, kind) and
- out = getAnOutNode(call, kind)
+ exists(ReturnPosition pos |
+ nodeCand1ReturnPosition(pos, config) and
+ getReturnPosition(node) = pos
)
)
}
+pragma[noinline]
+private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, ReturnKindExt kind, Node out |
+ nodeCand1(out, config) and
+ pos = viableReturnPos(call, kind) and
+ out = kind.getAnOutNode(call)
+ )
+}
+
/**
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
*/
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
simpleArgumentFlowsThrough(node1, node2, _, config)
}
+pragma[noinline]
+private ReturnPosition getReturnPosition1(Node node, Configuration config) {
+ result = getReturnPosition(node) and
+ nodeCand1(node, config)
+}
+
/**
* Holds if data can flow out of a callable from `node1` to `node2`, either
* through a `ReturnNode` or through an argument that has been mutated, and
* that this step is part of a path from a source to a sink.
*/
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
- nodeCand1(node1, unbind(config)) and
nodeCand1(node2, config) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
- (
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
- // flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
- )
+ exists(DataFlowCall call, ReturnKindExt kind |
+ getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
+ node2 = kind.getAnOutNode(call)
)
}
@@ -905,30 +903,35 @@ private predicate localFlowExit(Node node, Configuration config) {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
) {
- localFlowEntry(node1, config) and
+ not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowStep(node1, node2, config) and preservesValue = true
+ localFlowEntry(node1, config) and
+ (
+ localFlowStep(node1, node2, config) and preservesValue = true
+ or
+ additionalLocalFlowStep(node1, node2, config) and preservesValue = false
+ ) and
+ node1 != node2 and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ nodeCand(node2, unbind(config))
or
- additionalLocalFlowStep(node1, node2, config) and preservesValue = false
- ) and
- node1 != node2 and
- nodeCand(node2, unbind(config))
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, config) and
- localFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- nodeCand(node2, unbind(config))
- )
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, _, config) and
- additionalLocalFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- preservesValue = false and
- nodeCand(node2, unbind(config))
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, preservesValue, config, cc) and
+ localFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ nodeCand(node2, unbind(config))
+ )
+ or
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, _, config, cc) and
+ additionalLocalFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ preservesValue = false and
+ nodeCand(node2, unbind(config))
+ )
)
}
@@ -936,11 +939,11 @@ private predicate localFlowStepPlus(
* Holds if `node1` can step to `node2` in one or more local steps and this
* path can occur as a maximal subsequence of local steps in a dataflow path.
*/
-pragma[noinline]
+pragma[nomagic]
private predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, config) and
+ localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
localFlowExit(node2, config)
}
@@ -1000,7 +1003,7 @@ private class AccessPathFrontNilNode extends Node {
(
any(Configuration c).isSource(this)
or
- localFlowBigStep(_, this, false, _)
+ localFlowBigStep(_, this, false, _, _)
or
additionalJumpStep(_, this, _)
)
@@ -1023,12 +1026,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
(
exists(Node mid |
flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
apf = node.(AccessPathFrontNilNode).getApf()
)
or
@@ -1075,6 +1078,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
flowCandFwd(mid, fromArg, _, config) and
store(mid, f, node) and
nodeCand(node, unbind(config)) and
+ readStoreCand(f, unbind(config)) and
apf.headUsesContent(f)
)
or
@@ -1121,13 +1125,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
apf instanceof AccessPathFrontNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flowCand(mid, toReturn, apf, config)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(node, _, apf, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flowCand(mid, toReturn, nil, config) and
apf instanceof AccessPathFrontNil
)
@@ -1175,12 +1179,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
exists(Content f, AccessPathFront apf0 |
flowCandStore(node, f, toReturn, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, unbind(config))
+ consCand(f, apf, config)
)
or
exists(Content f, AccessPathFront apf0 |
flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, unbind(config)) and
+ consCandFwd(f, apf0, config) and
apf.headUsesContent(f)
)
}
@@ -1221,8 +1225,8 @@ private newtype TAccessPath =
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
/**
- * Conceptually a list of `Content`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
+ * 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.
@@ -1260,7 +1264,7 @@ abstract private class AccessPath extends TAccessPath {
private class AccessPathNil extends AccessPath, TNil {
override string toString() {
- exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -1276,7 +1280,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
override string toString() {
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
// The `concat` becomes "" if `ppReprType` has no result.
- result = f.toString() + concat(" : " + ppReprType(t))
+ result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
)
}
@@ -1293,8 +1297,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
override string toString() {
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
if len = 2
- then result = f1.toString() + ", " + f2.toString()
- else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f1.toString() + ", " + f2.toString() + "]"
+ else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -1362,12 +1366,12 @@ private predicate flowFwd0(
(
exists(Node mid |
flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
ap = node.(AccessPathNilNode).getAp() and
apf = ap.(AccessPathNil).getFront()
)
@@ -1471,13 +1475,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
ap instanceof AccessPathNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flow(mid, toReturn, ap, config)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(node, _, _, ap, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flow(mid, toReturn, nil, config) and
ap instanceof AccessPathNil
)
@@ -1618,11 +1622,14 @@ abstract class PathNode extends TPathNode {
/** Gets a successor of this node, if any. */
abstract PathNode getASuccessor();
+ /** Holds if this node is a source. */
+ abstract predicate isSource();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
exists(string s | s = this.(PathNodeMid).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -1683,12 +1690,6 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
// an intermediate step to another intermediate node
result = getSuccMid()
or
- // a final step to a sink via one or more local steps
- localFlowStepPlus(node, result.getNode(), _, config) and
- ap instanceof AccessPathNil and
- result instanceof PathNodeSink and
- result.getConfiguration() = unbind(this.getConfiguration())
- or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
mid = getSuccMid() and
@@ -1697,23 +1698,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
)
- or
- // a direct step from a source to a sink if a node is both
- this instanceof PathNodeSource and
- result instanceof PathNodeSink and
- this.getNode() = result.getNode() and
- result.getConfiguration() = unbind(this.getConfiguration())
}
-}
-/**
- * A flow graph node corresponding to a source.
- */
-private class PathNodeSource extends PathNodeMid {
- PathNodeSource() {
- getConfiguration().isSource(getNode()) and
- getCallContext() instanceof CallContextAny and
- getAp() instanceof AccessPathNil
+ override predicate isSource() {
+ config.isSource(node) and
+ cc instanceof CallContextAny and
+ ap instanceof AccessPathNil
}
}
@@ -1733,6 +1723,8 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
override Configuration getConfiguration() { result = config }
override PathNode getASuccessor() { none() }
+
+ override predicate isSource() { config.isSource(node) }
}
/**
@@ -1740,14 +1732,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
- localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- ap = mid.getAp()
- or
- localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof AccessPathNil and
- ap = node.(AccessPathNilNode).getAp()
+ exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
+ |
+ localFlowBigStep(midnode, node, true, conf, localCC) and
+ ap = ap0
+ or
+ localFlowBigStep(midnode, node, false, conf, localCC) and
+ ap0 instanceof AccessPathNil and
+ ap = node.(AccessPathNilNode).getAp()
+ )
or
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
or
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
or
- pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
- or
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
or
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
not innercc instanceof CallContextCall
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathOutOfCallable1(
- PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
+ PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
pathOutOfCallable0(mid, pos, innercc) and
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
- exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
- out = getAnOutNode(call, kind)
- )
-}
-
-private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- flow(node, unbind(mid.getConfiguration()))
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+ exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
+ out = kind.getAnOutNode(call)
)
}
@@ -1891,7 +1867,7 @@ private predicate pathIntoCallable(
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
/** Holds if data may flow from `p` to a return of kind `kind`. */
pragma[nomagic]
private predicate paramFlowsThrough(
- ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
) {
- exists(PathNodeMid mid, ReturnNode ret |
+ exists(PathNodeMid mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
)
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathThroughCallable0(
- DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
+ DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
) {
exists(ParameterNode p, CallContext innercc |
pathIntoCallable(mid, p, cc, innercc, call) and
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
- not parameterValueFlowsThrough(p, kind, innercc) and
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
mid.getAp() instanceof AccessPathNil
)
}
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(
- PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
-) {
- exists(DataFlowCall call, ReturnKind kind |
+private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
+ exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, apnil) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
@@ -1967,12 +1941,12 @@ private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallCon
* sinks.
*/
private predicate flowsTo(
- PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink,
- Configuration configuration
+ PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
) {
+ flowsource.isSource() and
flowsource.getConfiguration() = configuration and
flowsource.getNode() = source and
- pathSuccPlus(flowsource, flowsink) and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNode() = sink
}
@@ -1996,16 +1970,10 @@ private module FlowExploration {
// flow into callable
viableParamArg(_, node2, node1)
or
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
+ node2 = kind.getAnOutNode(call)
)
|
c1 = node1.getEnclosingCallable() and
@@ -2082,7 +2050,7 @@ private module FlowExploration {
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
- exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -2094,8 +2062,8 @@ private module FlowExploration {
override string toString() {
exists(Content f, int len | this = TPartialCons(f, len) |
if len = 1
- then result = f.toString()
- else result = f.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f.toString() + "]"
+ else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -2172,7 +2140,7 @@ private module FlowExploration {
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -2216,16 +2184,19 @@ private module FlowExploration {
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
) {
- localFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- or
- additionalLocalFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedRepr(node.getType())) and
- config = mid.getConfiguration()
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ (
+ localFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ ap = mid.getAp() and
+ config = mid.getConfiguration()
+ or
+ additionalLocalFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ mid.getAp() instanceof PartialAccessPathNil and
+ ap = TPartialNil(getErasedRepr(node.getType())) and
+ config = mid.getConfiguration()
+ )
or
jumpStep(mid.getNode(), node, config) and
cc instanceof CallContextAny and
@@ -2247,8 +2218,6 @@ private module FlowExploration {
apConsFwd(ap, f, ap0, config)
)
or
- partialPathOutOfArgument(mid, node, cc, ap, config)
- or
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
or
partialPathOutOfCallable(mid, node, cc, ap, config)
@@ -2307,7 +2276,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathOutOfCallable1(
- PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
+ PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -2321,36 +2290,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
- exists(ReturnKind kind, DataFlowCall call |
+ exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNode(call, kind)
- )
- }
-
- private predicate partialPathOutOfArgument(
- PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
- Configuration config
- ) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+ out = kind.getAnOutNode(call)
)
}
@@ -2389,7 +2334,7 @@ private module FlowExploration {
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -2397,10 +2342,10 @@ private module FlowExploration {
pragma[nomagic]
private predicate paramFlowsThroughInPartialPath(
- ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(PartialPathNodePriv mid, ReturnNode ret |
+ exists(PartialPathNodePriv mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -2417,23 +2362,23 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
- DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
+ DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
PartialAccessPathNil apnil, Configuration config
) {
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
- not parameterValueFlowsThrough(p, kind, innercc)
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
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 2da1d89e499..7ecb474f632 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -7,7 +7,7 @@
* on each other without introducing mutual recursion among those configurations.
*/
-private import DataFlowImplCommon
+private import DataFlowImplCommon::Public
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
- viableImpl(call) = result.getCallable() and
+private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
+ viableCallable(call) = result.getCallable() and
kind = result.getKind()
}
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
viableParamArg(_, node, arg)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- nodeCandFwd1(mid, config) and
- parameterValueFlowsToUpdate(p, mid) and
- viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
- nodeCandFwd1(ret, config) and
- getReturnPosition(ret) = viableReturnPos(call, kind) and
- node = getAnOutNode(call, kind)
+ exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
+ nodeCandFwd1ReturnPosition(pos, config) and
+ pos = viableReturnPos(call, kind) and
+ node = kind.getAnOutNode(call)
)
)
}
+pragma[noinline]
+private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(ReturnNodeExt ret |
+ nodeCandFwd1(ret, config) and
+ getReturnPosition(ret) = pos
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
exists(Node mid |
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
nodeCand1(param, config)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- parameterValueFlowsToUpdate(p, node) and
- viableParamArg(_, p, mid.getPreUpdateNode()) and
- nodeCand1(mid, config)
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind, OutNode out |
- nodeCand1(out, config) and
- getReturnPosition(node) = viableReturnPos(call, kind) and
- out = getAnOutNode(call, kind)
+ exists(ReturnPosition pos |
+ nodeCand1ReturnPosition(pos, config) and
+ getReturnPosition(node) = pos
)
)
}
+pragma[noinline]
+private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, ReturnKindExt kind, Node out |
+ nodeCand1(out, config) and
+ pos = viableReturnPos(call, kind) and
+ out = kind.getAnOutNode(call)
+ )
+}
+
/**
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
*/
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
simpleArgumentFlowsThrough(node1, node2, _, config)
}
+pragma[noinline]
+private ReturnPosition getReturnPosition1(Node node, Configuration config) {
+ result = getReturnPosition(node) and
+ nodeCand1(node, config)
+}
+
/**
* Holds if data can flow out of a callable from `node1` to `node2`, either
* through a `ReturnNode` or through an argument that has been mutated, and
* that this step is part of a path from a source to a sink.
*/
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
- nodeCand1(node1, unbind(config)) and
nodeCand1(node2, config) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
- (
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
- // flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
- )
+ exists(DataFlowCall call, ReturnKindExt kind |
+ getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
+ node2 = kind.getAnOutNode(call)
)
}
@@ -905,30 +903,35 @@ private predicate localFlowExit(Node node, Configuration config) {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
) {
- localFlowEntry(node1, config) and
+ not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowStep(node1, node2, config) and preservesValue = true
+ localFlowEntry(node1, config) and
+ (
+ localFlowStep(node1, node2, config) and preservesValue = true
+ or
+ additionalLocalFlowStep(node1, node2, config) and preservesValue = false
+ ) and
+ node1 != node2 and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ nodeCand(node2, unbind(config))
or
- additionalLocalFlowStep(node1, node2, config) and preservesValue = false
- ) and
- node1 != node2 and
- nodeCand(node2, unbind(config))
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, config) and
- localFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- nodeCand(node2, unbind(config))
- )
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, _, config) and
- additionalLocalFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- preservesValue = false and
- nodeCand(node2, unbind(config))
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, preservesValue, config, cc) and
+ localFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ nodeCand(node2, unbind(config))
+ )
+ or
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, _, config, cc) and
+ additionalLocalFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ preservesValue = false and
+ nodeCand(node2, unbind(config))
+ )
)
}
@@ -936,11 +939,11 @@ private predicate localFlowStepPlus(
* Holds if `node1` can step to `node2` in one or more local steps and this
* path can occur as a maximal subsequence of local steps in a dataflow path.
*/
-pragma[noinline]
+pragma[nomagic]
private predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, config) and
+ localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
localFlowExit(node2, config)
}
@@ -1000,7 +1003,7 @@ private class AccessPathFrontNilNode extends Node {
(
any(Configuration c).isSource(this)
or
- localFlowBigStep(_, this, false, _)
+ localFlowBigStep(_, this, false, _, _)
or
additionalJumpStep(_, this, _)
)
@@ -1023,12 +1026,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
(
exists(Node mid |
flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
apf = node.(AccessPathFrontNilNode).getApf()
)
or
@@ -1075,6 +1078,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
flowCandFwd(mid, fromArg, _, config) and
store(mid, f, node) and
nodeCand(node, unbind(config)) and
+ readStoreCand(f, unbind(config)) and
apf.headUsesContent(f)
)
or
@@ -1121,13 +1125,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
apf instanceof AccessPathFrontNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flowCand(mid, toReturn, apf, config)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(node, _, apf, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flowCand(mid, toReturn, nil, config) and
apf instanceof AccessPathFrontNil
)
@@ -1175,12 +1179,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
exists(Content f, AccessPathFront apf0 |
flowCandStore(node, f, toReturn, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, unbind(config))
+ consCand(f, apf, config)
)
or
exists(Content f, AccessPathFront apf0 |
flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, unbind(config)) and
+ consCandFwd(f, apf0, config) and
apf.headUsesContent(f)
)
}
@@ -1221,8 +1225,8 @@ private newtype TAccessPath =
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
/**
- * Conceptually a list of `Content`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
+ * 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.
@@ -1260,7 +1264,7 @@ abstract private class AccessPath extends TAccessPath {
private class AccessPathNil extends AccessPath, TNil {
override string toString() {
- exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -1276,7 +1280,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
override string toString() {
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
// The `concat` becomes "" if `ppReprType` has no result.
- result = f.toString() + concat(" : " + ppReprType(t))
+ result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
)
}
@@ -1293,8 +1297,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
override string toString() {
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
if len = 2
- then result = f1.toString() + ", " + f2.toString()
- else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f1.toString() + ", " + f2.toString() + "]"
+ else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -1362,12 +1366,12 @@ private predicate flowFwd0(
(
exists(Node mid |
flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
ap = node.(AccessPathNilNode).getAp() and
apf = ap.(AccessPathNil).getFront()
)
@@ -1471,13 +1475,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
ap instanceof AccessPathNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flow(mid, toReturn, ap, config)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(node, _, _, ap, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flow(mid, toReturn, nil, config) and
ap instanceof AccessPathNil
)
@@ -1618,11 +1622,14 @@ abstract class PathNode extends TPathNode {
/** Gets a successor of this node, if any. */
abstract PathNode getASuccessor();
+ /** Holds if this node is a source. */
+ abstract predicate isSource();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
exists(string s | s = this.(PathNodeMid).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -1683,12 +1690,6 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
// an intermediate step to another intermediate node
result = getSuccMid()
or
- // a final step to a sink via one or more local steps
- localFlowStepPlus(node, result.getNode(), _, config) and
- ap instanceof AccessPathNil and
- result instanceof PathNodeSink and
- result.getConfiguration() = unbind(this.getConfiguration())
- or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
mid = getSuccMid() and
@@ -1697,23 +1698,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
)
- or
- // a direct step from a source to a sink if a node is both
- this instanceof PathNodeSource and
- result instanceof PathNodeSink and
- this.getNode() = result.getNode() and
- result.getConfiguration() = unbind(this.getConfiguration())
}
-}
-/**
- * A flow graph node corresponding to a source.
- */
-private class PathNodeSource extends PathNodeMid {
- PathNodeSource() {
- getConfiguration().isSource(getNode()) and
- getCallContext() instanceof CallContextAny and
- getAp() instanceof AccessPathNil
+ override predicate isSource() {
+ config.isSource(node) and
+ cc instanceof CallContextAny and
+ ap instanceof AccessPathNil
}
}
@@ -1733,6 +1723,8 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
override Configuration getConfiguration() { result = config }
override PathNode getASuccessor() { none() }
+
+ override predicate isSource() { config.isSource(node) }
}
/**
@@ -1740,14 +1732,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
- localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- ap = mid.getAp()
- or
- localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof AccessPathNil and
- ap = node.(AccessPathNilNode).getAp()
+ exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
+ |
+ localFlowBigStep(midnode, node, true, conf, localCC) and
+ ap = ap0
+ or
+ localFlowBigStep(midnode, node, false, conf, localCC) and
+ ap0 instanceof AccessPathNil and
+ ap = node.(AccessPathNilNode).getAp()
+ )
or
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
or
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
or
- pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
- or
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
or
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
not innercc instanceof CallContextCall
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathOutOfCallable1(
- PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
+ PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
pathOutOfCallable0(mid, pos, innercc) and
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
- exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
- out = getAnOutNode(call, kind)
- )
-}
-
-private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- flow(node, unbind(mid.getConfiguration()))
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+ exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
+ out = kind.getAnOutNode(call)
)
}
@@ -1891,7 +1867,7 @@ private predicate pathIntoCallable(
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
/** Holds if data may flow from `p` to a return of kind `kind`. */
pragma[nomagic]
private predicate paramFlowsThrough(
- ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
) {
- exists(PathNodeMid mid, ReturnNode ret |
+ exists(PathNodeMid mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
)
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathThroughCallable0(
- DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
+ DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
) {
exists(ParameterNode p, CallContext innercc |
pathIntoCallable(mid, p, cc, innercc, call) and
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
- not parameterValueFlowsThrough(p, kind, innercc) and
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
mid.getAp() instanceof AccessPathNil
)
}
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(
- PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
-) {
- exists(DataFlowCall call, ReturnKind kind |
+private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
+ exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, apnil) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
@@ -1967,12 +1941,12 @@ private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallCon
* sinks.
*/
private predicate flowsTo(
- PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink,
- Configuration configuration
+ PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
) {
+ flowsource.isSource() and
flowsource.getConfiguration() = configuration and
flowsource.getNode() = source and
- pathSuccPlus(flowsource, flowsink) and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNode() = sink
}
@@ -1996,16 +1970,10 @@ private module FlowExploration {
// flow into callable
viableParamArg(_, node2, node1)
or
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
+ node2 = kind.getAnOutNode(call)
)
|
c1 = node1.getEnclosingCallable() and
@@ -2082,7 +2050,7 @@ private module FlowExploration {
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
- exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -2094,8 +2062,8 @@ private module FlowExploration {
override string toString() {
exists(Content f, int len | this = TPartialCons(f, len) |
if len = 1
- then result = f.toString()
- else result = f.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f.toString() + "]"
+ else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -2172,7 +2140,7 @@ private module FlowExploration {
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -2216,16 +2184,19 @@ private module FlowExploration {
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
) {
- localFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- or
- additionalLocalFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedRepr(node.getType())) and
- config = mid.getConfiguration()
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ (
+ localFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ ap = mid.getAp() and
+ config = mid.getConfiguration()
+ or
+ additionalLocalFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ mid.getAp() instanceof PartialAccessPathNil and
+ ap = TPartialNil(getErasedRepr(node.getType())) and
+ config = mid.getConfiguration()
+ )
or
jumpStep(mid.getNode(), node, config) and
cc instanceof CallContextAny and
@@ -2247,8 +2218,6 @@ private module FlowExploration {
apConsFwd(ap, f, ap0, config)
)
or
- partialPathOutOfArgument(mid, node, cc, ap, config)
- or
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
or
partialPathOutOfCallable(mid, node, cc, ap, config)
@@ -2307,7 +2276,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathOutOfCallable1(
- PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
+ PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -2321,36 +2290,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
- exists(ReturnKind kind, DataFlowCall call |
+ exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNode(call, kind)
- )
- }
-
- private predicate partialPathOutOfArgument(
- PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
- Configuration config
- ) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+ out = kind.getAnOutNode(call)
)
}
@@ -2389,7 +2334,7 @@ private module FlowExploration {
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -2397,10 +2342,10 @@ private module FlowExploration {
pragma[nomagic]
private predicate paramFlowsThroughInPartialPath(
- ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(PartialPathNodePriv mid, ReturnNode ret |
+ exists(PartialPathNodePriv mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -2417,23 +2362,23 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
- DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
+ DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
PartialAccessPathNil apnil, Configuration config
) {
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
- not parameterValueFlowsThrough(p, kind, innercc)
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
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 2da1d89e499..7ecb474f632 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -7,7 +7,7 @@
* on each other without introducing mutual recursion among those configurations.
*/
-private import DataFlowImplCommon
+private import DataFlowImplCommon::Public
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
- viableImpl(call) = result.getCallable() and
+private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
+ viableCallable(call) = result.getCallable() and
kind = result.getKind()
}
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
viableParamArg(_, node, arg)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- nodeCandFwd1(mid, config) and
- parameterValueFlowsToUpdate(p, mid) and
- viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
- nodeCandFwd1(ret, config) and
- getReturnPosition(ret) = viableReturnPos(call, kind) and
- node = getAnOutNode(call, kind)
+ exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
+ nodeCandFwd1ReturnPosition(pos, config) and
+ pos = viableReturnPos(call, kind) and
+ node = kind.getAnOutNode(call)
)
)
}
+pragma[noinline]
+private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(ReturnNodeExt ret |
+ nodeCandFwd1(ret, config) and
+ getReturnPosition(ret) = pos
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
exists(Node mid |
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
nodeCand1(param, config)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- parameterValueFlowsToUpdate(p, node) and
- viableParamArg(_, p, mid.getPreUpdateNode()) and
- nodeCand1(mid, config)
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind, OutNode out |
- nodeCand1(out, config) and
- getReturnPosition(node) = viableReturnPos(call, kind) and
- out = getAnOutNode(call, kind)
+ exists(ReturnPosition pos |
+ nodeCand1ReturnPosition(pos, config) and
+ getReturnPosition(node) = pos
)
)
}
+pragma[noinline]
+private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, ReturnKindExt kind, Node out |
+ nodeCand1(out, config) and
+ pos = viableReturnPos(call, kind) and
+ out = kind.getAnOutNode(call)
+ )
+}
+
/**
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
*/
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
simpleArgumentFlowsThrough(node1, node2, _, config)
}
+pragma[noinline]
+private ReturnPosition getReturnPosition1(Node node, Configuration config) {
+ result = getReturnPosition(node) and
+ nodeCand1(node, config)
+}
+
/**
* Holds if data can flow out of a callable from `node1` to `node2`, either
* through a `ReturnNode` or through an argument that has been mutated, and
* that this step is part of a path from a source to a sink.
*/
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
- nodeCand1(node1, unbind(config)) and
nodeCand1(node2, config) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
- (
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
- // flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
- )
+ exists(DataFlowCall call, ReturnKindExt kind |
+ getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
+ node2 = kind.getAnOutNode(call)
)
}
@@ -905,30 +903,35 @@ private predicate localFlowExit(Node node, Configuration config) {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
) {
- localFlowEntry(node1, config) and
+ not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowStep(node1, node2, config) and preservesValue = true
+ localFlowEntry(node1, config) and
+ (
+ localFlowStep(node1, node2, config) and preservesValue = true
+ or
+ additionalLocalFlowStep(node1, node2, config) and preservesValue = false
+ ) and
+ node1 != node2 and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ nodeCand(node2, unbind(config))
or
- additionalLocalFlowStep(node1, node2, config) and preservesValue = false
- ) and
- node1 != node2 and
- nodeCand(node2, unbind(config))
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, config) and
- localFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- nodeCand(node2, unbind(config))
- )
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, _, config) and
- additionalLocalFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- preservesValue = false and
- nodeCand(node2, unbind(config))
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, preservesValue, config, cc) and
+ localFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ nodeCand(node2, unbind(config))
+ )
+ or
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, _, config, cc) and
+ additionalLocalFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ preservesValue = false and
+ nodeCand(node2, unbind(config))
+ )
)
}
@@ -936,11 +939,11 @@ private predicate localFlowStepPlus(
* Holds if `node1` can step to `node2` in one or more local steps and this
* path can occur as a maximal subsequence of local steps in a dataflow path.
*/
-pragma[noinline]
+pragma[nomagic]
private predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, config) and
+ localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
localFlowExit(node2, config)
}
@@ -1000,7 +1003,7 @@ private class AccessPathFrontNilNode extends Node {
(
any(Configuration c).isSource(this)
or
- localFlowBigStep(_, this, false, _)
+ localFlowBigStep(_, this, false, _, _)
or
additionalJumpStep(_, this, _)
)
@@ -1023,12 +1026,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
(
exists(Node mid |
flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
apf = node.(AccessPathFrontNilNode).getApf()
)
or
@@ -1075,6 +1078,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
flowCandFwd(mid, fromArg, _, config) and
store(mid, f, node) and
nodeCand(node, unbind(config)) and
+ readStoreCand(f, unbind(config)) and
apf.headUsesContent(f)
)
or
@@ -1121,13 +1125,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
apf instanceof AccessPathFrontNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flowCand(mid, toReturn, apf, config)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(node, _, apf, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flowCand(mid, toReturn, nil, config) and
apf instanceof AccessPathFrontNil
)
@@ -1175,12 +1179,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
exists(Content f, AccessPathFront apf0 |
flowCandStore(node, f, toReturn, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, unbind(config))
+ consCand(f, apf, config)
)
or
exists(Content f, AccessPathFront apf0 |
flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, unbind(config)) and
+ consCandFwd(f, apf0, config) and
apf.headUsesContent(f)
)
}
@@ -1221,8 +1225,8 @@ private newtype TAccessPath =
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
/**
- * Conceptually a list of `Content`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
+ * 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.
@@ -1260,7 +1264,7 @@ abstract private class AccessPath extends TAccessPath {
private class AccessPathNil extends AccessPath, TNil {
override string toString() {
- exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -1276,7 +1280,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
override string toString() {
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
// The `concat` becomes "" if `ppReprType` has no result.
- result = f.toString() + concat(" : " + ppReprType(t))
+ result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
)
}
@@ -1293,8 +1297,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
override string toString() {
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
if len = 2
- then result = f1.toString() + ", " + f2.toString()
- else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f1.toString() + ", " + f2.toString() + "]"
+ else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -1362,12 +1366,12 @@ private predicate flowFwd0(
(
exists(Node mid |
flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
ap = node.(AccessPathNilNode).getAp() and
apf = ap.(AccessPathNil).getFront()
)
@@ -1471,13 +1475,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
ap instanceof AccessPathNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flow(mid, toReturn, ap, config)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(node, _, _, ap, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flow(mid, toReturn, nil, config) and
ap instanceof AccessPathNil
)
@@ -1618,11 +1622,14 @@ abstract class PathNode extends TPathNode {
/** Gets a successor of this node, if any. */
abstract PathNode getASuccessor();
+ /** Holds if this node is a source. */
+ abstract predicate isSource();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
exists(string s | s = this.(PathNodeMid).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -1683,12 +1690,6 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
// an intermediate step to another intermediate node
result = getSuccMid()
or
- // a final step to a sink via one or more local steps
- localFlowStepPlus(node, result.getNode(), _, config) and
- ap instanceof AccessPathNil and
- result instanceof PathNodeSink and
- result.getConfiguration() = unbind(this.getConfiguration())
- or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
mid = getSuccMid() and
@@ -1697,23 +1698,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
)
- or
- // a direct step from a source to a sink if a node is both
- this instanceof PathNodeSource and
- result instanceof PathNodeSink and
- this.getNode() = result.getNode() and
- result.getConfiguration() = unbind(this.getConfiguration())
}
-}
-/**
- * A flow graph node corresponding to a source.
- */
-private class PathNodeSource extends PathNodeMid {
- PathNodeSource() {
- getConfiguration().isSource(getNode()) and
- getCallContext() instanceof CallContextAny and
- getAp() instanceof AccessPathNil
+ override predicate isSource() {
+ config.isSource(node) and
+ cc instanceof CallContextAny and
+ ap instanceof AccessPathNil
}
}
@@ -1733,6 +1723,8 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
override Configuration getConfiguration() { result = config }
override PathNode getASuccessor() { none() }
+
+ override predicate isSource() { config.isSource(node) }
}
/**
@@ -1740,14 +1732,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
- localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- ap = mid.getAp()
- or
- localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof AccessPathNil and
- ap = node.(AccessPathNilNode).getAp()
+ exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
+ |
+ localFlowBigStep(midnode, node, true, conf, localCC) and
+ ap = ap0
+ or
+ localFlowBigStep(midnode, node, false, conf, localCC) and
+ ap0 instanceof AccessPathNil and
+ ap = node.(AccessPathNilNode).getAp()
+ )
or
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
or
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
or
- pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
- or
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
or
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
not innercc instanceof CallContextCall
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathOutOfCallable1(
- PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
+ PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
pathOutOfCallable0(mid, pos, innercc) and
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
- exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
- out = getAnOutNode(call, kind)
- )
-}
-
-private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- flow(node, unbind(mid.getConfiguration()))
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+ exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
+ out = kind.getAnOutNode(call)
)
}
@@ -1891,7 +1867,7 @@ private predicate pathIntoCallable(
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
/** Holds if data may flow from `p` to a return of kind `kind`. */
pragma[nomagic]
private predicate paramFlowsThrough(
- ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
) {
- exists(PathNodeMid mid, ReturnNode ret |
+ exists(PathNodeMid mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
)
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathThroughCallable0(
- DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
+ DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
) {
exists(ParameterNode p, CallContext innercc |
pathIntoCallable(mid, p, cc, innercc, call) and
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
- not parameterValueFlowsThrough(p, kind, innercc) and
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
mid.getAp() instanceof AccessPathNil
)
}
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(
- PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
-) {
- exists(DataFlowCall call, ReturnKind kind |
+private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
+ exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, apnil) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
@@ -1967,12 +1941,12 @@ private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallCon
* sinks.
*/
private predicate flowsTo(
- PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink,
- Configuration configuration
+ PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
) {
+ flowsource.isSource() and
flowsource.getConfiguration() = configuration and
flowsource.getNode() = source and
- pathSuccPlus(flowsource, flowsink) and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNode() = sink
}
@@ -1996,16 +1970,10 @@ private module FlowExploration {
// flow into callable
viableParamArg(_, node2, node1)
or
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
+ node2 = kind.getAnOutNode(call)
)
|
c1 = node1.getEnclosingCallable() and
@@ -2082,7 +2050,7 @@ private module FlowExploration {
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
- exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -2094,8 +2062,8 @@ private module FlowExploration {
override string toString() {
exists(Content f, int len | this = TPartialCons(f, len) |
if len = 1
- then result = f.toString()
- else result = f.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f.toString() + "]"
+ else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -2172,7 +2140,7 @@ private module FlowExploration {
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -2216,16 +2184,19 @@ private module FlowExploration {
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
) {
- localFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- or
- additionalLocalFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedRepr(node.getType())) and
- config = mid.getConfiguration()
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ (
+ localFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ ap = mid.getAp() and
+ config = mid.getConfiguration()
+ or
+ additionalLocalFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ mid.getAp() instanceof PartialAccessPathNil and
+ ap = TPartialNil(getErasedRepr(node.getType())) and
+ config = mid.getConfiguration()
+ )
or
jumpStep(mid.getNode(), node, config) and
cc instanceof CallContextAny and
@@ -2247,8 +2218,6 @@ private module FlowExploration {
apConsFwd(ap, f, ap0, config)
)
or
- partialPathOutOfArgument(mid, node, cc, ap, config)
- or
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
or
partialPathOutOfCallable(mid, node, cc, ap, config)
@@ -2307,7 +2276,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathOutOfCallable1(
- PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
+ PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -2321,36 +2290,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
- exists(ReturnKind kind, DataFlowCall call |
+ exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNode(call, kind)
- )
- }
-
- private predicate partialPathOutOfArgument(
- PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
- Configuration config
- ) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+ out = kind.getAnOutNode(call)
)
}
@@ -2389,7 +2334,7 @@ private module FlowExploration {
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -2397,10 +2342,10 @@ private module FlowExploration {
pragma[nomagic]
private predicate paramFlowsThroughInPartialPath(
- ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(PartialPathNodePriv mid, ReturnNode ret |
+ exists(PartialPathNodePriv mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -2417,23 +2362,23 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
- DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
+ DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
PartialAccessPathNil apnil, Configuration config
) {
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
- not parameterValueFlowsThrough(p, kind, innercc)
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
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 2da1d89e499..7ecb474f632 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -7,7 +7,7 @@
* on each other without introducing mutual recursion among those configurations.
*/
-private import DataFlowImplCommon
+private import DataFlowImplCommon::Public
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
- viableImpl(call) = result.getCallable() and
+private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
+ viableCallable(call) = result.getCallable() and
kind = result.getKind()
}
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
viableParamArg(_, node, arg)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- nodeCandFwd1(mid, config) and
- parameterValueFlowsToUpdate(p, mid) and
- viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
- nodeCandFwd1(ret, config) and
- getReturnPosition(ret) = viableReturnPos(call, kind) and
- node = getAnOutNode(call, kind)
+ exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
+ nodeCandFwd1ReturnPosition(pos, config) and
+ pos = viableReturnPos(call, kind) and
+ node = kind.getAnOutNode(call)
)
)
}
+pragma[noinline]
+private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(ReturnNodeExt ret |
+ nodeCandFwd1(ret, config) and
+ getReturnPosition(ret) = pos
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
exists(Node mid |
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
nodeCand1(param, config)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- parameterValueFlowsToUpdate(p, node) and
- viableParamArg(_, p, mid.getPreUpdateNode()) and
- nodeCand1(mid, config)
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind, OutNode out |
- nodeCand1(out, config) and
- getReturnPosition(node) = viableReturnPos(call, kind) and
- out = getAnOutNode(call, kind)
+ exists(ReturnPosition pos |
+ nodeCand1ReturnPosition(pos, config) and
+ getReturnPosition(node) = pos
)
)
}
+pragma[noinline]
+private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, ReturnKindExt kind, Node out |
+ nodeCand1(out, config) and
+ pos = viableReturnPos(call, kind) and
+ out = kind.getAnOutNode(call)
+ )
+}
+
/**
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
*/
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
simpleArgumentFlowsThrough(node1, node2, _, config)
}
+pragma[noinline]
+private ReturnPosition getReturnPosition1(Node node, Configuration config) {
+ result = getReturnPosition(node) and
+ nodeCand1(node, config)
+}
+
/**
* Holds if data can flow out of a callable from `node1` to `node2`, either
* through a `ReturnNode` or through an argument that has been mutated, and
* that this step is part of a path from a source to a sink.
*/
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
- nodeCand1(node1, unbind(config)) and
nodeCand1(node2, config) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
- (
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
- // flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
- )
+ exists(DataFlowCall call, ReturnKindExt kind |
+ getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
+ node2 = kind.getAnOutNode(call)
)
}
@@ -905,30 +903,35 @@ private predicate localFlowExit(Node node, Configuration config) {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
) {
- localFlowEntry(node1, config) and
+ not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowStep(node1, node2, config) and preservesValue = true
+ localFlowEntry(node1, config) and
+ (
+ localFlowStep(node1, node2, config) and preservesValue = true
+ or
+ additionalLocalFlowStep(node1, node2, config) and preservesValue = false
+ ) and
+ node1 != node2 and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ nodeCand(node2, unbind(config))
or
- additionalLocalFlowStep(node1, node2, config) and preservesValue = false
- ) and
- node1 != node2 and
- nodeCand(node2, unbind(config))
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, config) and
- localFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- nodeCand(node2, unbind(config))
- )
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, _, config) and
- additionalLocalFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- preservesValue = false and
- nodeCand(node2, unbind(config))
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, preservesValue, config, cc) and
+ localFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ nodeCand(node2, unbind(config))
+ )
+ or
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, _, config, cc) and
+ additionalLocalFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ preservesValue = false and
+ nodeCand(node2, unbind(config))
+ )
)
}
@@ -936,11 +939,11 @@ private predicate localFlowStepPlus(
* Holds if `node1` can step to `node2` in one or more local steps and this
* path can occur as a maximal subsequence of local steps in a dataflow path.
*/
-pragma[noinline]
+pragma[nomagic]
private predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, config) and
+ localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
localFlowExit(node2, config)
}
@@ -1000,7 +1003,7 @@ private class AccessPathFrontNilNode extends Node {
(
any(Configuration c).isSource(this)
or
- localFlowBigStep(_, this, false, _)
+ localFlowBigStep(_, this, false, _, _)
or
additionalJumpStep(_, this, _)
)
@@ -1023,12 +1026,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
(
exists(Node mid |
flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
apf = node.(AccessPathFrontNilNode).getApf()
)
or
@@ -1075,6 +1078,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
flowCandFwd(mid, fromArg, _, config) and
store(mid, f, node) and
nodeCand(node, unbind(config)) and
+ readStoreCand(f, unbind(config)) and
apf.headUsesContent(f)
)
or
@@ -1121,13 +1125,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
apf instanceof AccessPathFrontNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flowCand(mid, toReturn, apf, config)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(node, _, apf, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flowCand(mid, toReturn, nil, config) and
apf instanceof AccessPathFrontNil
)
@@ -1175,12 +1179,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
exists(Content f, AccessPathFront apf0 |
flowCandStore(node, f, toReturn, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, unbind(config))
+ consCand(f, apf, config)
)
or
exists(Content f, AccessPathFront apf0 |
flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, unbind(config)) and
+ consCandFwd(f, apf0, config) and
apf.headUsesContent(f)
)
}
@@ -1221,8 +1225,8 @@ private newtype TAccessPath =
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
/**
- * Conceptually a list of `Content`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
+ * 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.
@@ -1260,7 +1264,7 @@ abstract private class AccessPath extends TAccessPath {
private class AccessPathNil extends AccessPath, TNil {
override string toString() {
- exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -1276,7 +1280,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
override string toString() {
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
// The `concat` becomes "" if `ppReprType` has no result.
- result = f.toString() + concat(" : " + ppReprType(t))
+ result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
)
}
@@ -1293,8 +1297,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
override string toString() {
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
if len = 2
- then result = f1.toString() + ", " + f2.toString()
- else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f1.toString() + ", " + f2.toString() + "]"
+ else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -1362,12 +1366,12 @@ private predicate flowFwd0(
(
exists(Node mid |
flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
ap = node.(AccessPathNilNode).getAp() and
apf = ap.(AccessPathNil).getFront()
)
@@ -1471,13 +1475,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
ap instanceof AccessPathNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flow(mid, toReturn, ap, config)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(node, _, _, ap, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flow(mid, toReturn, nil, config) and
ap instanceof AccessPathNil
)
@@ -1618,11 +1622,14 @@ abstract class PathNode extends TPathNode {
/** Gets a successor of this node, if any. */
abstract PathNode getASuccessor();
+ /** Holds if this node is a source. */
+ abstract predicate isSource();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
exists(string s | s = this.(PathNodeMid).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -1683,12 +1690,6 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
// an intermediate step to another intermediate node
result = getSuccMid()
or
- // a final step to a sink via one or more local steps
- localFlowStepPlus(node, result.getNode(), _, config) and
- ap instanceof AccessPathNil and
- result instanceof PathNodeSink and
- result.getConfiguration() = unbind(this.getConfiguration())
- or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
mid = getSuccMid() and
@@ -1697,23 +1698,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
)
- or
- // a direct step from a source to a sink if a node is both
- this instanceof PathNodeSource and
- result instanceof PathNodeSink and
- this.getNode() = result.getNode() and
- result.getConfiguration() = unbind(this.getConfiguration())
}
-}
-/**
- * A flow graph node corresponding to a source.
- */
-private class PathNodeSource extends PathNodeMid {
- PathNodeSource() {
- getConfiguration().isSource(getNode()) and
- getCallContext() instanceof CallContextAny and
- getAp() instanceof AccessPathNil
+ override predicate isSource() {
+ config.isSource(node) and
+ cc instanceof CallContextAny and
+ ap instanceof AccessPathNil
}
}
@@ -1733,6 +1723,8 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
override Configuration getConfiguration() { result = config }
override PathNode getASuccessor() { none() }
+
+ override predicate isSource() { config.isSource(node) }
}
/**
@@ -1740,14 +1732,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
- localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- ap = mid.getAp()
- or
- localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof AccessPathNil and
- ap = node.(AccessPathNilNode).getAp()
+ exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
+ |
+ localFlowBigStep(midnode, node, true, conf, localCC) and
+ ap = ap0
+ or
+ localFlowBigStep(midnode, node, false, conf, localCC) and
+ ap0 instanceof AccessPathNil and
+ ap = node.(AccessPathNilNode).getAp()
+ )
or
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
or
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
or
- pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
- or
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
or
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
not innercc instanceof CallContextCall
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathOutOfCallable1(
- PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
+ PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
pathOutOfCallable0(mid, pos, innercc) and
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
- exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
- out = getAnOutNode(call, kind)
- )
-}
-
-private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- flow(node, unbind(mid.getConfiguration()))
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+ exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
+ out = kind.getAnOutNode(call)
)
}
@@ -1891,7 +1867,7 @@ private predicate pathIntoCallable(
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
/** Holds if data may flow from `p` to a return of kind `kind`. */
pragma[nomagic]
private predicate paramFlowsThrough(
- ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
) {
- exists(PathNodeMid mid, ReturnNode ret |
+ exists(PathNodeMid mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
)
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathThroughCallable0(
- DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
+ DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
) {
exists(ParameterNode p, CallContext innercc |
pathIntoCallable(mid, p, cc, innercc, call) and
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
- not parameterValueFlowsThrough(p, kind, innercc) and
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
mid.getAp() instanceof AccessPathNil
)
}
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(
- PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
-) {
- exists(DataFlowCall call, ReturnKind kind |
+private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
+ exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, apnil) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
@@ -1967,12 +1941,12 @@ private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallCon
* sinks.
*/
private predicate flowsTo(
- PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink,
- Configuration configuration
+ PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
) {
+ flowsource.isSource() and
flowsource.getConfiguration() = configuration and
flowsource.getNode() = source and
- pathSuccPlus(flowsource, flowsink) and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNode() = sink
}
@@ -1996,16 +1970,10 @@ private module FlowExploration {
// flow into callable
viableParamArg(_, node2, node1)
or
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
+ node2 = kind.getAnOutNode(call)
)
|
c1 = node1.getEnclosingCallable() and
@@ -2082,7 +2050,7 @@ private module FlowExploration {
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
- exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -2094,8 +2062,8 @@ private module FlowExploration {
override string toString() {
exists(Content f, int len | this = TPartialCons(f, len) |
if len = 1
- then result = f.toString()
- else result = f.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f.toString() + "]"
+ else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -2172,7 +2140,7 @@ private module FlowExploration {
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -2216,16 +2184,19 @@ private module FlowExploration {
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
) {
- localFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- or
- additionalLocalFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedRepr(node.getType())) and
- config = mid.getConfiguration()
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ (
+ localFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ ap = mid.getAp() and
+ config = mid.getConfiguration()
+ or
+ additionalLocalFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ mid.getAp() instanceof PartialAccessPathNil and
+ ap = TPartialNil(getErasedRepr(node.getType())) and
+ config = mid.getConfiguration()
+ )
or
jumpStep(mid.getNode(), node, config) and
cc instanceof CallContextAny and
@@ -2247,8 +2218,6 @@ private module FlowExploration {
apConsFwd(ap, f, ap0, config)
)
or
- partialPathOutOfArgument(mid, node, cc, ap, config)
- or
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
or
partialPathOutOfCallable(mid, node, cc, ap, config)
@@ -2307,7 +2276,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathOutOfCallable1(
- PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
+ PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -2321,36 +2290,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
- exists(ReturnKind kind, DataFlowCall call |
+ exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNode(call, kind)
- )
- }
-
- private predicate partialPathOutOfArgument(
- PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
- Configuration config
- ) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+ out = kind.getAnOutNode(call)
)
}
@@ -2389,7 +2334,7 @@ private module FlowExploration {
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -2397,10 +2342,10 @@ private module FlowExploration {
pragma[nomagic]
private predicate paramFlowsThroughInPartialPath(
- ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(PartialPathNodePriv mid, ReturnNode ret |
+ exists(PartialPathNodePriv mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -2417,23 +2362,23 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
- DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
+ DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
PartialAccessPathNil apnil, Configuration config
) {
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
- not parameterValueFlowsThrough(p, kind, innercc)
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
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 97052e80004..8b9c8f5abd3 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -3,470 +3,861 @@ import DataFlowImplSpecific::Public
private ReturnNode getAReturnNodeOfKind(ReturnKind kind) { result.getKind() = kind }
-cached
+module Public {
+ import ImplCommon
+ import FlowThrough_v2
+}
+
private module ImplCommon {
- /**
- * Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
- * The instance parameter is considered to have index `-1`.
- */
- pragma[nomagic]
- private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
- p.isParameterOf(viableCallable(call), i)
- }
+ import Cached
- /**
- * Holds if `arg` is a possible argument to `p` in `call`, taking virtual
- * dispatch into account.
- */
cached
- predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
- exists(int i |
- viableParam(call, i, p) and
- arg.argumentOf(call, i)
- )
- }
+ private module Cached {
+ /**
+ * Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
+ * The instance parameter is considered to have index `-1`.
+ */
+ pragma[nomagic]
+ private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallable(call), i)
+ }
- /**
- * Holds if `p` can flow to `node` in the same callable using only
- * value-preserving steps, not taking call contexts into account.
- */
- private predicate parameterValueFlowNoCtx(ParameterNode p, Node node) {
- p = node
- or
- exists(Node mid |
- parameterValueFlowNoCtx(p, mid) and
- simpleLocalFlowStep(mid, node) and
- compatibleTypes(p.getType(), node.getType())
- )
- or
- // flow through a callable
- exists(Node arg |
- parameterValueFlowNoCtx(p, arg) and
- argumentValueFlowsThroughNoCtx(arg, node) and
- compatibleTypes(p.getType(), node.getType())
- )
- }
-
- /**
- * Holds if `p` can flow to a return node of kind `kind` in the same
- * callable using only value-preserving steps, not taking call contexts
- * into account.
- */
- private predicate parameterValueFlowsThroughNoCtx(ParameterNode p, ReturnKind kind) {
- parameterValueFlowNoCtx(p, getAReturnNodeOfKind(kind))
- }
-
- pragma[nomagic]
- private predicate argumentValueFlowsThroughNoCtx0(
- DataFlowCall call, ArgumentNode arg, ReturnKind kind
- ) {
- exists(ParameterNode param | viableParamArg(call, param, arg) |
- parameterValueFlowsThroughNoCtx(param, kind)
- )
- }
-
- /**
- * Holds if `arg` flows to `out` through a call using only value-preserving steps,
- * not taking call contexts into account.
- */
- private predicate argumentValueFlowsThroughNoCtx(ArgumentNode arg, OutNode out) {
- exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughNoCtx0(call, arg, kind) |
- out = getAnOutNode(call, kind) and
- compatibleTypes(arg.getType(), out.getType())
- )
- }
-
- /**
- * Holds if `arg` is the `i`th argument of `call` inside the callable
- * `enclosing`, and `arg` may flow through `call`.
- */
- pragma[noinline]
- private predicate argumentOf(
- DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
- ) {
- arg.argumentOf(call, i) and
- argumentValueFlowsThroughNoCtx(arg, _) and
- enclosing = arg.getEnclosingCallable()
- }
-
- pragma[noinline]
- private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
-
- pragma[noinline]
- private predicate viableParamArg0(int i, ArgumentNode arg, CallContext outercc, DataFlowCall call) {
- exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
- outercc = TAnyCallContext()
- or
- outercc = TSomeCall(getAParameter(c), _)
- or
- exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
- reducedViableImplInCallContext(_, c, other)
+ /**
+ * Holds if `arg` is a possible argument to `p` in `call`, taking virtual
+ * dispatch into account.
+ */
+ cached
+ predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParam(call, i, p) and
+ arg.argumentOf(call, i)
)
- )
- }
+ }
- pragma[noinline]
- private predicate viableParamArg1(
- ParameterNode p, DataFlowCallable callable, int i, ArgumentNode arg, CallContext outercc,
- DataFlowCall call
- ) {
- viableParamArg0(i, arg, outercc, call) and
- callable = resolveCall(call, outercc) and
- p.isParameterOf(callable, any(int j | j <= i and j >= i))
- }
+ /*
+ * The `FlowThrough_*` modules take a `step` relation as input and provide
+ * an `argumentValueFlowsThrough` relation as output.
+ *
+ * `FlowThrough_v1` includes just `simpleLocalFlowStep`, which is then used
+ * to detect getters and setters.
+ * `FlowThrough_v2` then includes a little bit of local field flow on top
+ * of `simpleLocalFlowStep`.
+ */
- /**
- * Holds if `arg` is a possible argument to `p`, in the call `call`, and
- * `arg` may flow through `call`. The possible contexts before and after
- * entering the callable are `outercc` and `innercc`, respectively.
- */
- private predicate viableParamArg(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
- CallContextCall innercc
- ) {
- exists(int i, DataFlowCallable callable | viableParamArg1(p, callable, i, arg, outercc, call) |
- if reducedViableImplInCallContext(_, callable, call)
- then innercc = TSpecificCall(call, i, true)
- else innercc = TSomeCall(p, true)
- )
- }
+ private module FlowThrough_v1 {
+ private predicate step = simpleLocalFlowStep/2;
- private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
- result = TSomeCall(p, _)
- or
- exists(DataFlowCall call, int i, DataFlowCallable callable |
- result = TSpecificCall(call, i, _) and
- p.isParameterOf(callable, i) and
- reducedViableImplInCallContext(_, callable, call)
- )
- }
-
- /**
- * Holds if `p` can flow to `node` in the same callable using only
- * value-preserving steps, in call context `cc`.
- */
- private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
- p = node and
- parameterValueFlowsThroughNoCtx(p, _) and
- cc = getAValidCallContextForParameter(p)
- or
- exists(Node mid |
- parameterValueFlow(p, mid, cc) and
- simpleLocalFlowStep(mid, node) and
- compatibleTypes(p.getType(), node.getType())
- )
- or
- // flow through a callable
- exists(Node arg |
- parameterValueFlow(p, arg, cc) and
- argumentValueFlowsThrough(arg, node, cc) and
- compatibleTypes(p.getType(), node.getType())
- )
- }
-
- /**
- * Holds if `p` can flow to a return node of kind `kind` in the same
- * callable using only value-preserving steps, in call context `cc`.
- */
- cached
- predicate parameterValueFlowsThrough(ParameterNode p, ReturnKind kind, CallContextCall cc) {
- parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
- }
-
- pragma[nomagic]
- private predicate argumentValueFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKind kind, CallContext cc
- ) {
- exists(ParameterNode param, CallContext innercc |
- viableParamArg(call, param, arg, cc, innercc) and
- parameterValueFlowsThrough(param, kind, innercc)
- )
- }
-
- /**
- * Holds if `arg` flows to `out` through a call using only value-preserving steps,
- * in call context cc.
- */
- cached
- predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
- exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, cc) |
- out = getAnOutNode(call, kind) and
- compatibleTypes(arg.getType(), out.getType())
- )
- }
-
- /**
- * Holds if `p` can flow to the pre-update node of `n` in the same callable
- * using only value-preserving steps.
- */
- cached
- predicate parameterValueFlowsToUpdate(ParameterNode p, PostUpdateNode n) {
- parameterValueFlowNoCtx(p, n.getPreUpdateNode())
- }
-
- /**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a value-preserving method.
- */
- private predicate localValueStep(Node node1, Node node2) {
- simpleLocalFlowStep(node1, node2) or
- argumentValueFlowsThrough(node1, node2, _)
- }
-
- /*
- * Calculation of `predicate store(Node node1, Content f, Node node2)`:
- * There are four cases:
- * - The base case: A direct local assignment given by `storeStep`.
- * - A call to a method or constructor with two arguments, `arg1` and `arg2`,
- * such that the call has the side-effect `arg2.f = arg1`.
- * - A call to a method that returns an object in which an argument has been
- * stored.
- * - A reverse step through a read when the result of the read has been
- * stored into. This handles cases like `x.f1.f2 = y`.
- * `storeViaSideEffect` covers the first two cases, and `storeReturn` covers
- * the third case.
- */
-
- /**
- * Holds if data can flow from `node1` to `node2` via a direct assignment to
- * `f` or via a call that acts as a setter.
- */
- cached
- predicate store(Node node1, Content f, Node node2) {
- storeViaSideEffect(node1, f, node2) or
- storeReturn(node1, f, node2) or
- read(node2.(PostUpdateNode).getPreUpdateNode(), f, node1.(PostUpdateNode).getPreUpdateNode())
- }
-
- private predicate storeViaSideEffect(Node node1, Content f, PostUpdateNode node2) {
- storeStep(node1, f, node2) and readStep(_, f, _)
- or
- exists(DataFlowCall call, int i1, int i2 |
- setterCall(call, i1, i2, f) and
- node1.(ArgumentNode).argumentOf(call, i1) and
- node2.getPreUpdateNode().(ArgumentNode).argumentOf(call, i2) and
- compatibleTypes(node1.getTypeBound(), f.getType()) and
- compatibleTypes(node2.getTypeBound(), f.getContainerType())
- )
- }
-
- pragma[nomagic]
- private predicate setterInParam(ParameterNode p1, Content f, ParameterNode p2) {
- exists(Node n1, PostUpdateNode n2 |
- parameterValueFlowNoCtx(p1, n1) and
- storeViaSideEffect(n1, f, n2) and
- parameterValueFlowNoCtx(p2, n2.getPreUpdateNode()) and
- p1 != p2
- )
- }
-
- pragma[nomagic]
- private predicate setterCall(DataFlowCall call, int i1, int i2, Content f) {
- exists(DataFlowCallable callable, ParameterNode p1, ParameterNode p2 |
- setterInParam(p1, f, p2) and
- callable = viableCallable(call) and
- p1.isParameterOf(callable, i1) and
- p2.isParameterOf(callable, i2)
- )
- }
-
- pragma[noinline]
- private predicate storeReturn0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
- exists(ParameterNode p |
- viableParamArg(call, p, arg) and
- setterReturn(p, f, kind)
- )
- }
-
- private predicate storeReturn(Node node1, Content f, Node node2) {
- exists(DataFlowCall call, ReturnKind kind |
- storeReturn0(call, kind, node1, f) and
- node2 = getAnOutNode(call, kind) and
- compatibleTypes(node1.getTypeBound(), f.getType()) and
- compatibleTypes(node2.getTypeBound(), f.getContainerType())
- )
- }
-
- private predicate setterReturn(ParameterNode p, Content f, ReturnKind kind) {
- exists(Node n1, Node n2 |
- parameterValueFlowNoCtx(p, n1) and
- store(n1, f, n2) and
- localValueStep*(n2, getAReturnNodeOfKind(kind))
- )
- }
-
- pragma[noinline]
- private predicate read0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
- exists(ParameterNode p |
- viableParamArg(call, p, arg) and
- getter(p, f, kind)
- )
- }
-
- /**
- * Holds if data can flow from `node1` to `node2` via a direct read of `f` or
- * via a getter.
- */
- cached
- predicate read(Node node1, Content f, Node node2) {
- readStep(node1, f, node2) and storeStep(_, f, _)
- or
- exists(DataFlowCall call, ReturnKind kind |
- read0(call, kind, node1, f) and
- node2 = getAnOutNode(call, kind) and
- compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
- compatibleTypes(node2.getTypeBound(), f.getType())
- )
- }
-
- private predicate getter(ParameterNode p, Content f, ReturnKind kind) {
- exists(Node n1, Node n2 |
- parameterValueFlowNoCtx(p, n1) and
- read(n1, f, n2) and
- localValueStep*(n2, getAReturnNodeOfKind(kind))
- )
- }
-
- cached
- predicate localStoreReadStep(Node node1, Node node2) {
- exists(Node mid1, Node mid2, Content f |
- store(node1, f, mid1) and
- localValueStep*(mid1, mid2) and
- read(mid2, f, node2)
- )
- }
-
- /**
- * Holds if `call` passes an implicit or explicit instance argument, i.e., an
- * expression that reaches a `this` parameter.
- */
- private predicate callHasInstanceArgument(DataFlowCall call) {
- exists(ArgumentNode arg | arg.argumentOf(call, -1))
- }
-
- cached
- newtype TCallContext =
- TAnyCallContext() or
- TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
- reducedViableImplInCallContext(_, _, call) and
- (emptyAp = true or emptyAp = false) and
- (
- exists(call.getArgument(i))
+ /**
+ * Holds if `p` can flow to `node` in the same callable using only
+ * value-preserving steps, not taking call contexts into account.
+ */
+ private predicate parameterValueFlowCand(ParameterNode p, Node node) {
+ p = node
or
- i = -1 and callHasInstanceArgument(call)
+ exists(Node mid |
+ parameterValueFlowCand(p, mid) and
+ step(mid, node) and
+ compatibleTypes(p.getType(), node.getType())
+ )
+ or
+ // flow through a callable
+ exists(Node arg |
+ parameterValueFlowCand(p, arg) and
+ argumentValueFlowsThroughCand(arg, node) and
+ compatibleTypes(p.getType(), node.getType())
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to a return node of kind `kind` in the same
+ * callable using only value-preserving steps, not taking call contexts
+ * into account.
+ */
+ private predicate parameterValueFlowsThroughCand(ParameterNode p, ReturnKind kind) {
+ parameterValueFlowCand(p, getAReturnNodeOfKind(kind))
+ }
+
+ pragma[nomagic]
+ private predicate argumentValueFlowsThroughCand0(
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind
+ ) {
+ exists(ParameterNode param | viableParamArg(call, param, arg) |
+ parameterValueFlowsThroughCand(param, kind)
+ )
+ }
+
+ /**
+ * Holds if `arg` flows to `out` through a call using only value-preserving steps,
+ * not taking call contexts into account.
+ */
+ private predicate argumentValueFlowsThroughCand(ArgumentNode arg, OutNode out) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThroughCand0(call, arg, kind)
+ |
+ out = getAnOutNode(call, kind) and
+ compatibleTypes(arg.getType(), out.getType())
+ )
+ }
+
+ /**
+ * Holds if `arg` is the `i`th argument of `call` inside the callable
+ * `enclosing`, and `arg` may flow through `call`.
+ */
+ pragma[noinline]
+ private predicate argumentOf(
+ DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
+ ) {
+ arg.argumentOf(call, i) and
+ argumentValueFlowsThroughCand(arg, _) and
+ enclosing = arg.getEnclosingCallable()
+ }
+
+ pragma[noinline]
+ private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
+
+ pragma[noinline]
+ private predicate viableParamArg0(
+ int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
+ ) {
+ exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
+ (
+ outercc = TAnyCallContext()
+ or
+ outercc = TSomeCall(getAParameter(c), _)
+ or
+ exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
+ recordDataFlowCallSite(other, c)
+ )
+ ) and
+ not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
+ )
+ }
+
+ pragma[noinline]
+ private predicate viableParamArg1(
+ ParameterNode p, DataFlowCallable callable, int i, ArgumentNode arg, CallContext outercc,
+ DataFlowCall call
+ ) {
+ viableParamArg0(i, arg, outercc, call) and
+ callable = resolveCall(call, outercc) and
+ p.isParameterOf(callable, any(int j | j <= i and j >= i))
+ }
+
+ /**
+ * Holds if `arg` is a possible argument to `p`, in the call `call`, and
+ * `arg` may flow through `call`. The possible contexts before and after
+ * entering the callable are `outercc` and `innercc`, respectively.
+ */
+ private predicate viableParamArg(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
+ CallContextCall innercc
+ ) {
+ exists(int i, DataFlowCallable callable |
+ viableParamArg1(p, callable, i, arg, outercc, call)
+ |
+ if recordDataFlowCallSite(call, callable)
+ then innercc = TSpecificCall(call, i, true)
+ else innercc = TSomeCall(p, true)
+ )
+ }
+
+ private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
+ result = TSomeCall(p, _)
+ or
+ exists(DataFlowCall call, int i, DataFlowCallable callable |
+ result = TSpecificCall(call, i, _) and
+ p.isParameterOf(callable, i) and
+ recordDataFlowCallSite(call, callable)
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to `node` in the same callable using only
+ * value-preserving steps, in call context `cc`.
+ */
+ private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
+ p = node and
+ parameterValueFlowsThroughCand(p, _) and
+ cc = getAValidCallContextForParameter(p)
+ or
+ exists(Node mid |
+ parameterValueFlow(p, mid, cc) and
+ step(mid, node) and
+ compatibleTypes(p.getType(), node.getType()) and
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
+ )
+ or
+ // flow through a callable
+ exists(Node arg |
+ parameterValueFlow(p, arg, cc) and
+ argumentValueFlowsThrough(arg, node, cc) and
+ compatibleTypes(p.getType(), node.getType()) and
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to a return node of kind `kind` in the same
+ * callable using only value-preserving steps, in call context `cc`.
+ */
+ private predicate parameterValueFlowsThrough(
+ ParameterNode p, ReturnKind kind, CallContextCall cc
+ ) {
+ parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
+ }
+
+ pragma[nomagic]
+ private predicate argumentValueFlowsThrough0(
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind, CallContext cc
+ ) {
+ exists(ParameterNode param, CallContext innercc |
+ viableParamArg(call, param, arg, cc, innercc) and
+ parameterValueFlowsThrough(param, kind, innercc)
+ )
+ }
+
+ /**
+ * Holds if `arg` flows to `out` through a call using only value-preserving steps,
+ * in call context cc.
+ */
+ predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThrough0(call, arg, kind, cc)
+ |
+ out = getAnOutNode(call, kind) and
+ not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
+ compatibleTypes(arg.getType(), out.getType())
+ )
+ }
+ }
+
+ /**
+ * Holds if `p` can flow to the pre-update node of `n` in the same callable
+ * using only value-preserving steps.
+ */
+ cached
+ predicate parameterValueFlowsToUpdate(ParameterNode p, PostUpdateNode n) {
+ parameterValueFlowNoCtx(p, n.getPreUpdateNode())
+ }
+
+ /**
+ * Holds if data can flow from `node1` to `node2` in one local step or a step
+ * through a value-preserving method.
+ */
+ private predicate localValueStep(Node node1, Node node2) {
+ simpleLocalFlowStep(node1, node2) or
+ FlowThrough_v1::argumentValueFlowsThrough(node1, node2, _)
+ }
+
+ /**
+ * Holds if `p` can flow to `node` in the same callable allowing local flow
+ * steps and value flow through methods. Call contexts are only accounted
+ * for in the nested calls.
+ */
+ private predicate parameterValueFlowNoCtx(ParameterNode p, Node node) {
+ p = node
+ or
+ exists(Node mid |
+ parameterValueFlowNoCtx(p, mid) and
+ localValueStep(mid, node) and
+ compatibleTypes(p.getType(), node.getType())
)
- } or
- TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
- TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
+ }
- cached
- newtype TReturnPosition =
- TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
-}
+ /*
+ * Calculation of `predicate store(Node node1, Content f, Node node2)`:
+ * There are four cases:
+ * - The base case: A direct local assignment given by `storeStep`.
+ * - A call to a method or constructor with two arguments, `arg1` and `arg2`,
+ * such that the call has the side-effect `arg2.f = arg1`.
+ * - A call to a method that returns an object in which an argument has been
+ * stored.
+ * - A reverse step through a read when the result of the read has been
+ * stored into. This handles cases like `x.f1.f2 = y`.
+ * `storeViaSideEffect` covers the first two cases, and `storeReturn` covers
+ * the third case.
+ */
-import ImplCommon
+ /**
+ * Holds if data can flow from `node1` to `node2` via a direct assignment to
+ * `f` or via a call that acts as a setter.
+ */
+ cached
+ predicate store(Node node1, Content f, Node node2) {
+ storeViaSideEffect(node1, f, node2) or
+ storeReturn(node1, f, node2) or
+ read(node2.(PostUpdateNode).getPreUpdateNode(), f, node1.(PostUpdateNode).getPreUpdateNode())
+ }
-pragma[noinline]
-private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind kind) {
- c = returnNodeGetEnclosingCallable(ret) and
- kind = ret.getKind()
-}
+ private predicate storeViaSideEffect(Node node1, Content f, PostUpdateNode node2) {
+ storeStep(node1, f, node2) and readStep(_, f, _)
+ or
+ exists(DataFlowCall call, int i1, int i2 |
+ setterCall(call, i1, i2, f) and
+ node1.(ArgumentNode).argumentOf(call, i1) and
+ node2.getPreUpdateNode().(ArgumentNode).argumentOf(call, i2) and
+ compatibleTypes(node1.getTypeBound(), f.getType()) and
+ compatibleTypes(node2.getTypeBound(), f.getContainerType())
+ )
+ }
-/**
- * A call context to restrict the targets of virtual dispatch and match the
- * call sites of flow into a method with flow out of a method.
- *
- * There are four cases:
- * - `TAnyCallContext()` : No restrictions on method flow.
- * - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
- * parameter at the given `call`. This call improves the set of viable
- * dispatch targets for at least one method call in the current callable.
- * - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
- * originating call does not improve the set of dispatch targets for any
- * method call in the current callable and was therefore not recorded.
- * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
- * this dispatch target of `call` implies a reduced set of dispatch origins
- * to which data may flow if it should reach a `return` statement.
- */
-abstract class CallContext extends TCallContext {
- abstract string toString();
-}
+ pragma[nomagic]
+ private predicate setterInParam(ParameterNode p1, Content f, ParameterNode p2) {
+ exists(Node n1, PostUpdateNode n2 |
+ parameterValueFlowNoCtx(p1, n1) and
+ storeViaSideEffect(n1, f, n2) and
+ parameterValueFlowNoCtx(p2, n2.getPreUpdateNode()) and
+ p1 != p2
+ )
+ }
-class CallContextAny extends CallContext, TAnyCallContext {
- override string toString() { result = "CcAny" }
-}
+ pragma[nomagic]
+ private predicate setterCall(DataFlowCall call, int i1, int i2, Content f) {
+ exists(DataFlowCallable callable, ParameterNode p1, ParameterNode p2 |
+ setterInParam(p1, f, p2) and
+ callable = viableCallable(call) and
+ p1.isParameterOf(callable, i1) and
+ p2.isParameterOf(callable, i2)
+ )
+ }
-abstract class CallContextCall extends CallContext { }
+ pragma[noinline]
+ private predicate storeReturn0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
+ exists(ParameterNode p |
+ viableParamArg(call, p, arg) and
+ setterReturn(p, f, kind)
+ )
+ }
-class CallContextSpecificCall extends CallContextCall, TSpecificCall {
- override string toString() {
- exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
- result = "CcCall(" + call + ", " + i + ")"
+ private predicate storeReturn(Node node1, Content f, Node node2) {
+ exists(DataFlowCall call, ReturnKind kind |
+ storeReturn0(call, kind, node1, f) and
+ node2 = getAnOutNode(call, kind) and
+ compatibleTypes(node1.getTypeBound(), f.getType()) and
+ compatibleTypes(node2.getTypeBound(), f.getContainerType())
+ )
+ }
+
+ private predicate setterReturn(ParameterNode p, Content f, ReturnKind kind) {
+ exists(Node n1, Node n2 |
+ parameterValueFlowNoCtx(p, n1) and
+ store(n1, f, n2) and
+ localValueStep*(n2, getAReturnNodeOfKind(kind))
+ )
+ }
+
+ pragma[noinline]
+ private predicate read0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
+ exists(ParameterNode p |
+ viableParamArg(call, p, arg) and
+ getter(p, f, kind)
+ )
+ }
+
+ /**
+ * Holds if data can flow from `node1` to `node2` via a direct read of `f` or
+ * via a getter.
+ */
+ cached
+ predicate read(Node node1, Content f, Node node2) {
+ readStep(node1, f, node2) and storeStep(_, f, _)
+ or
+ exists(DataFlowCall call, ReturnKind kind |
+ read0(call, kind, node1, f) and
+ node2 = getAnOutNode(call, kind) and
+ compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
+ compatibleTypes(node2.getTypeBound(), f.getType())
+ )
+ }
+
+ private predicate getter(ParameterNode p, Content f, ReturnKind kind) {
+ exists(Node n1, Node n2 |
+ parameterValueFlowNoCtx(p, n1) and
+ read(n1, f, n2) and
+ localValueStep*(n2, getAReturnNodeOfKind(kind))
+ )
+ }
+
+ cached
+ predicate localStoreReadStep(Node node1, Node node2) {
+ exists(Node mid1, Node mid2, Content f |
+ store(node1, f, mid1) and
+ localValueStep*(mid1, mid2) and
+ read(mid2, f, node2) and
+ compatibleTypes(node1.getTypeBound(), node2.getTypeBound())
+ )
+ }
+
+ cached
+ module FlowThrough_v2 {
+ private predicate step(Node node1, Node node2) {
+ simpleLocalFlowStep(node1, node2) or
+ localStoreReadStep(node1, node2)
+ }
+
+ /**
+ * Holds if `p` can flow to `node` in the same callable using only
+ * value-preserving steps, not taking call contexts into account.
+ */
+ private predicate parameterValueFlowCand(ParameterNode p, Node node) {
+ p = node
+ or
+ exists(Node mid |
+ parameterValueFlowCand(p, mid) and
+ step(mid, node) and
+ compatibleTypes(p.getType(), node.getType())
+ )
+ or
+ // flow through a callable
+ exists(Node arg |
+ parameterValueFlowCand(p, arg) and
+ argumentValueFlowsThroughCand(arg, node) and
+ compatibleTypes(p.getType(), node.getType())
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to a return node of kind `kind` in the same
+ * callable using only value-preserving steps, not taking call contexts
+ * into account.
+ */
+ private predicate parameterValueFlowsThroughCand(ParameterNode p, ReturnKind kind) {
+ parameterValueFlowCand(p, getAReturnNodeOfKind(kind))
+ }
+
+ pragma[nomagic]
+ private predicate argumentValueFlowsThroughCand0(
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind
+ ) {
+ exists(ParameterNode param | viableParamArg(call, param, arg) |
+ parameterValueFlowsThroughCand(param, kind)
+ )
+ }
+
+ /**
+ * Holds if `arg` flows to `out` through a call using only value-preserving steps,
+ * not taking call contexts into account.
+ */
+ private predicate argumentValueFlowsThroughCand(ArgumentNode arg, OutNode out) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThroughCand0(call, arg, kind)
+ |
+ out = getAnOutNode(call, kind) and
+ compatibleTypes(arg.getType(), out.getType())
+ )
+ }
+
+ /**
+ * Holds if `arg` is the `i`th argument of `call` inside the callable
+ * `enclosing`, and `arg` may flow through `call`.
+ */
+ pragma[noinline]
+ private predicate argumentOf(
+ DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
+ ) {
+ arg.argumentOf(call, i) and
+ argumentValueFlowsThroughCand(arg, _) and
+ enclosing = arg.getEnclosingCallable()
+ }
+
+ pragma[noinline]
+ private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
+
+ pragma[noinline]
+ private predicate viableParamArg0(
+ int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
+ ) {
+ exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
+ (
+ outercc = TAnyCallContext()
+ or
+ outercc = TSomeCall(getAParameter(c), _)
+ or
+ exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
+ recordDataFlowCallSite(other, c)
+ )
+ ) and
+ not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
+ )
+ }
+
+ pragma[noinline]
+ private predicate viableParamArg1(
+ ParameterNode p, DataFlowCallable callable, int i, ArgumentNode arg, CallContext outercc,
+ DataFlowCall call
+ ) {
+ viableParamArg0(i, arg, outercc, call) and
+ callable = resolveCall(call, outercc) and
+ p.isParameterOf(callable, any(int j | j <= i and j >= i))
+ }
+
+ /**
+ * Holds if `arg` is a possible argument to `p`, in the call `call`, and
+ * `arg` may flow through `call`. The possible contexts before and after
+ * entering the callable are `outercc` and `innercc`, respectively.
+ */
+ private predicate viableParamArg(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
+ CallContextCall innercc
+ ) {
+ exists(int i, DataFlowCallable callable |
+ viableParamArg1(p, callable, i, arg, outercc, call)
+ |
+ if recordDataFlowCallSite(call, callable)
+ then innercc = TSpecificCall(call, i, true)
+ else innercc = TSomeCall(p, true)
+ )
+ }
+
+ private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
+ result = TSomeCall(p, _)
+ or
+ exists(DataFlowCall call, int i, DataFlowCallable callable |
+ result = TSpecificCall(call, i, _) and
+ p.isParameterOf(callable, i) and
+ recordDataFlowCallSite(call, callable)
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to `node` in the same callable using only
+ * value-preserving steps, in call context `cc`.
+ */
+ private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
+ p = node and
+ parameterValueFlowsThroughCand(p, _) and
+ cc = getAValidCallContextForParameter(p)
+ or
+ exists(Node mid |
+ parameterValueFlow(p, mid, cc) and
+ step(mid, node) and
+ compatibleTypes(p.getType(), node.getType()) and
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
+ )
+ or
+ // flow through a callable
+ exists(Node arg |
+ parameterValueFlow(p, arg, cc) and
+ argumentValueFlowsThrough(arg, node, cc) and
+ compatibleTypes(p.getType(), node.getType()) and
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to a return node of kind `kind` in the same
+ * callable using only value-preserving steps, in call context `cc`.
+ */
+ cached
+ predicate parameterValueFlowsThrough(ParameterNode p, ReturnKind kind, CallContextCall cc) {
+ parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
+ }
+
+ pragma[nomagic]
+ private predicate argumentValueFlowsThrough0(
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind, CallContext cc
+ ) {
+ exists(ParameterNode param, CallContext innercc |
+ viableParamArg(call, param, arg, cc, innercc) and
+ parameterValueFlowsThrough(param, kind, innercc)
+ )
+ }
+
+ /**
+ * Holds if `arg` flows to `out` through a call using only value-preserving steps,
+ * in call context cc.
+ */
+ cached
+ predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThrough0(call, arg, kind, cc)
+ |
+ out = getAnOutNode(call, kind) and
+ not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
+ compatibleTypes(arg.getType(), out.getType())
+ )
+ }
+ }
+
+ /**
+ * Holds if `call` passes an implicit or explicit instance argument, i.e., an
+ * expression that reaches a `this` parameter.
+ */
+ private predicate callHasInstanceArgument(DataFlowCall call) {
+ exists(ArgumentNode arg | arg.argumentOf(call, -1))
+ }
+
+ /**
+ * Holds if the call context `call` either improves virtual dispatch in
+ * `callable` or if it allows us to prune unreachable nodes in `callable`.
+ */
+ cached
+ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
+ reducedViableImplInCallContext(_, callable, call)
+ or
+ exists(Node n | n.getEnclosingCallable() = callable | isUnreachableInCall(n, call))
+ }
+
+ cached
+ newtype TCallContext =
+ TAnyCallContext() or
+ TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
+ recordDataFlowCallSite(call, _) and
+ (emptyAp = true or emptyAp = false) and
+ (
+ exists(call.getArgument(i))
+ or
+ i = -1 and callHasInstanceArgument(call)
+ )
+ } or
+ TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
+ TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
+
+ cached
+ newtype TReturnPosition =
+ TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { returnPosition(_, c, kind) }
+
+ cached
+ newtype TLocalFlowCallContext =
+ TAnyLocalCall() or
+ TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) }
+ }
+
+ pragma[noinline]
+ private predicate returnPosition(ReturnNodeExt ret, DataFlowCallable c, ReturnKindExt kind) {
+ c = returnNodeGetEnclosingCallable(ret) and
+ kind = ret.getKind()
+ }
+
+ /**
+ * A call context to restrict the targets of virtual dispatch, prune local flow,
+ * and match the call sites of flow into a method with flow out of a method.
+ *
+ * There are four cases:
+ * - `TAnyCallContext()` : No restrictions on method flow.
+ * - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
+ * parameter at the given `call`. This call improves the set of viable
+ * dispatch targets for at least one method call in the current callable
+ * or helps prune unreachable nodes in the current callable.
+ * - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
+ * originating call does not improve the set of dispatch targets for any
+ * method call in the current callable and was therefore not recorded.
+ * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
+ * this dispatch target of `call` implies a reduced set of dispatch origins
+ * to which data may flow if it should reach a `return` statement.
+ */
+ abstract class CallContext extends TCallContext {
+ abstract string toString();
+
+ /** Holds if this call context is relevant for `callable`. */
+ abstract predicate relevantFor(DataFlowCallable callable);
+ }
+
+ class CallContextAny extends CallContext, TAnyCallContext {
+ override string toString() { result = "CcAny" }
+
+ override predicate relevantFor(DataFlowCallable callable) { any() }
+ }
+
+ abstract class CallContextCall extends CallContext { }
+
+ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
+ override string toString() {
+ exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
+ result = "CcCall(" + call + ", " + i + ")"
+ )
+ }
+
+ override predicate relevantFor(DataFlowCallable callable) {
+ recordDataFlowCallSite(getCall(), callable)
+ }
+
+ DataFlowCall getCall() { this = TSpecificCall(result, _, _) }
+ }
+
+ class CallContextSomeCall extends CallContextCall, TSomeCall {
+ override string toString() { result = "CcSomeCall" }
+
+ override predicate relevantFor(DataFlowCallable callable) {
+ exists(ParameterNode p | this = TSomeCall(p, _) and p.getEnclosingCallable() = callable)
+ }
+ }
+
+ class CallContextReturn extends CallContext, TReturn {
+ override string toString() {
+ exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
+ }
+
+ override predicate relevantFor(DataFlowCallable callable) {
+ exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable)
+ }
+ }
+
+ /**
+ * A call context that is relevant for pruning local flow.
+ */
+ abstract class LocalCallContext extends TLocalFlowCallContext {
+ abstract string toString();
+
+ /** Holds if this call context is relevant for `callable`. */
+ abstract predicate relevantFor(DataFlowCallable callable);
+ }
+
+ class LocalCallContextAny extends LocalCallContext, TAnyLocalCall {
+ override string toString() { result = "LocalCcAny" }
+
+ override predicate relevantFor(DataFlowCallable callable) { any() }
+ }
+
+ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall {
+ LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) }
+
+ DataFlowCall call;
+
+ DataFlowCall getCall() { result = call }
+
+ override string toString() { result = "LocalCcCall(" + call + ")" }
+
+ override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) }
+ }
+
+ private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
+ exists(Node n | n.getEnclosingCallable() = callable and isUnreachableInCall(n, call))
+ }
+
+ /**
+ * Gets the local call context given the call context and the callable that
+ * the contexts apply to.
+ */
+ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) {
+ ctx.relevantFor(callable) and
+ if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable)
+ then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall()
+ else result instanceof LocalCallContextAny
+ }
+
+ /**
+ * A node from which flow can return to the caller. This is either a regular
+ * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
+ */
+ class ReturnNodeExt extends Node {
+ ReturnNodeExt() {
+ this instanceof ReturnNode or
+ parameterValueFlowsToUpdate(_, this)
+ }
+
+ /** Gets the kind of this returned value. */
+ ReturnKindExt getKind() {
+ result = TValueReturn(this.(ReturnNode).getKind())
+ or
+ exists(ParameterNode p, int pos |
+ parameterValueFlowsToUpdate(p, this) and
+ p.isParameterOf(_, pos) and
+ result = TParamUpdate(pos)
+ )
+ }
+ }
+
+ private newtype TReturnKindExt =
+ TValueReturn(ReturnKind kind) or
+ TParamUpdate(int pos) {
+ exists(ParameterNode p | parameterValueFlowsToUpdate(p, _) and p.isParameterOf(_, pos))
+ }
+
+ /**
+ * An extended return kind. A return kind describes how data can be returned
+ * from a callable. This can either be through a returned value or an updated
+ * parameter.
+ */
+ abstract class ReturnKindExt extends TReturnKindExt {
+ /** Gets a textual representation of this return kind. */
+ abstract string toString();
+
+ /** Gets a node corresponding to data flow out of `call`. */
+ abstract Node getAnOutNode(DataFlowCall call);
+ }
+
+ class ValueReturnKind extends ReturnKindExt, TValueReturn {
+ private ReturnKind kind;
+
+ ValueReturnKind() { this = TValueReturn(kind) }
+
+ ReturnKind getKind() { result = kind }
+
+ override string toString() { result = kind.toString() }
+
+ override Node getAnOutNode(DataFlowCall call) { result = getAnOutNode(call, this.getKind()) }
+ }
+
+ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
+ private int pos;
+
+ ParamUpdateReturnKind() { this = TParamUpdate(pos) }
+
+ int getPosition() { result = pos }
+
+ override string toString() { result = "param update " + pos }
+
+ override Node getAnOutNode(DataFlowCall call) {
+ exists(ArgumentNode arg |
+ result.(PostUpdateNode).getPreUpdateNode() = arg and
+ arg.argumentOf(call, this.getPosition())
+ )
+ }
+ }
+
+ /** A callable tagged with a relevant return kind. */
+ class ReturnPosition extends TReturnPosition0 {
+ private DataFlowCallable c;
+ private ReturnKindExt kind;
+
+ ReturnPosition() { this = TReturnPosition0(c, kind) }
+
+ /** Gets the callable. */
+ DataFlowCallable getCallable() { result = c }
+
+ /** Gets the return kind. */
+ ReturnKindExt getKind() { result = kind }
+
+ /** Gets a textual representation of this return position. */
+ string toString() { result = "[" + kind + "] " + c }
+ }
+
+ pragma[noinline]
+ DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) {
+ result = ret.getEnclosingCallable()
+ }
+
+ pragma[noinline]
+ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
+ exists(DataFlowCallable c, ReturnKindExt k | returnPosition(ret, c, k) |
+ result = TReturnPosition0(c, k)
)
}
-}
-class CallContextSomeCall extends CallContextCall, TSomeCall {
- override string toString() { result = "CcSomeCall" }
-}
+ bindingset[cc, callable]
+ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
+ cc instanceof CallContextAny and callable = viableCallable(call)
+ or
+ exists(DataFlowCallable c0, DataFlowCall call0 |
+ call0.getEnclosingCallable() = callable and
+ cc = TReturn(c0, call0) and
+ c0 = prunedViableImplInCallContextReverse(call0, call)
+ )
+ }
-class CallContextReturn extends CallContext, TReturn {
- override string toString() {
- exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
+ bindingset[call, cc]
+ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
+ exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
+ if reducedViableImplInCallContext(call, _, ctx)
+ then result = prunedViableImplInCallContext(call, ctx)
+ else result = viableCallable(call)
+ )
+ or
+ result = viableCallable(call) and cc instanceof CallContextSomeCall
+ or
+ result = viableCallable(call) and cc instanceof CallContextAny
+ or
+ result = viableCallable(call) and cc instanceof CallContextReturn
}
}
-
-/** A callable tagged with a relevant return kind. */
-class ReturnPosition extends TReturnPosition0 {
- private DataFlowCallable c;
- private ReturnKind kind;
-
- ReturnPosition() { this = TReturnPosition0(c, kind) }
-
- /** Gets the callable. */
- DataFlowCallable getCallable() { result = c }
-
- /** Gets the return kind. */
- ReturnKind getKind() { result = kind }
-
- /** Gets a textual representation of this return position. */
- string toString() { result = "[" + kind + "] " + c }
-}
-
-pragma[noinline]
-DataFlowCallable returnNodeGetEnclosingCallable(ReturnNode ret) {
- result = ret.getEnclosingCallable()
-}
-
-pragma[noinline]
-ReturnPosition getReturnPosition(ReturnNode ret) {
- exists(DataFlowCallable c, ReturnKind k | returnPosition(ret, c, k) |
- result = TReturnPosition0(c, k)
- )
-}
-
-bindingset[cc, callable]
-predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
- cc instanceof CallContextAny and callable = viableCallable(call)
- or
- exists(DataFlowCallable c0, DataFlowCall call0 |
- call0.getEnclosingCallable() = callable and
- cc = TReturn(c0, call0) and
- c0 = prunedViableImplInCallContextReverse(call0, call)
- )
-}
-
-bindingset[call, cc]
-DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
- exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
- if reducedViableImplInCallContext(call, _, ctx)
- then result = prunedViableImplInCallContext(call, ctx)
- else result = viableCallable(call)
- )
- or
- result = viableCallable(call) and cc instanceof CallContextSomeCall
- or
- result = viableCallable(call) and cc instanceof CallContextAny
- or
- result = viableCallable(call) and cc instanceof CallContextReturn
-}
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 2da1d89e499..7ecb474f632 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -7,7 +7,7 @@
* on each other without introducing mutual recursion among those configurations.
*/
-private import DataFlowImplCommon
+private import DataFlowImplCommon::Public
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
- viableImpl(call) = result.getCallable() and
+private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
+ viableCallable(call) = result.getCallable() and
kind = result.getKind()
}
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
viableParamArg(_, node, arg)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- nodeCandFwd1(mid, config) and
- parameterValueFlowsToUpdate(p, mid) and
- viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
- nodeCandFwd1(ret, config) and
- getReturnPosition(ret) = viableReturnPos(call, kind) and
- node = getAnOutNode(call, kind)
+ exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
+ nodeCandFwd1ReturnPosition(pos, config) and
+ pos = viableReturnPos(call, kind) and
+ node = kind.getAnOutNode(call)
)
)
}
+pragma[noinline]
+private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(ReturnNodeExt ret |
+ nodeCandFwd1(ret, config) and
+ getReturnPosition(ret) = pos
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
exists(Node mid |
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
nodeCand1(param, config)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- parameterValueFlowsToUpdate(p, node) and
- viableParamArg(_, p, mid.getPreUpdateNode()) and
- nodeCand1(mid, config)
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind, OutNode out |
- nodeCand1(out, config) and
- getReturnPosition(node) = viableReturnPos(call, kind) and
- out = getAnOutNode(call, kind)
+ exists(ReturnPosition pos |
+ nodeCand1ReturnPosition(pos, config) and
+ getReturnPosition(node) = pos
)
)
}
+pragma[noinline]
+private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, ReturnKindExt kind, Node out |
+ nodeCand1(out, config) and
+ pos = viableReturnPos(call, kind) and
+ out = kind.getAnOutNode(call)
+ )
+}
+
/**
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
*/
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
simpleArgumentFlowsThrough(node1, node2, _, config)
}
+pragma[noinline]
+private ReturnPosition getReturnPosition1(Node node, Configuration config) {
+ result = getReturnPosition(node) and
+ nodeCand1(node, config)
+}
+
/**
* Holds if data can flow out of a callable from `node1` to `node2`, either
* through a `ReturnNode` or through an argument that has been mutated, and
* that this step is part of a path from a source to a sink.
*/
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
- nodeCand1(node1, unbind(config)) and
nodeCand1(node2, config) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
- (
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
- // flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
- )
+ exists(DataFlowCall call, ReturnKindExt kind |
+ getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
+ node2 = kind.getAnOutNode(call)
)
}
@@ -905,30 +903,35 @@ private predicate localFlowExit(Node node, Configuration config) {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
) {
- localFlowEntry(node1, config) and
+ not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowStep(node1, node2, config) and preservesValue = true
+ localFlowEntry(node1, config) and
+ (
+ localFlowStep(node1, node2, config) and preservesValue = true
+ or
+ additionalLocalFlowStep(node1, node2, config) and preservesValue = false
+ ) and
+ node1 != node2 and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ nodeCand(node2, unbind(config))
or
- additionalLocalFlowStep(node1, node2, config) and preservesValue = false
- ) and
- node1 != node2 and
- nodeCand(node2, unbind(config))
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, config) and
- localFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- nodeCand(node2, unbind(config))
- )
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, _, config) and
- additionalLocalFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- preservesValue = false and
- nodeCand(node2, unbind(config))
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, preservesValue, config, cc) and
+ localFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ nodeCand(node2, unbind(config))
+ )
+ or
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, _, config, cc) and
+ additionalLocalFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ preservesValue = false and
+ nodeCand(node2, unbind(config))
+ )
)
}
@@ -936,11 +939,11 @@ private predicate localFlowStepPlus(
* Holds if `node1` can step to `node2` in one or more local steps and this
* path can occur as a maximal subsequence of local steps in a dataflow path.
*/
-pragma[noinline]
+pragma[nomagic]
private predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, config) and
+ localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
localFlowExit(node2, config)
}
@@ -1000,7 +1003,7 @@ private class AccessPathFrontNilNode extends Node {
(
any(Configuration c).isSource(this)
or
- localFlowBigStep(_, this, false, _)
+ localFlowBigStep(_, this, false, _, _)
or
additionalJumpStep(_, this, _)
)
@@ -1023,12 +1026,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
(
exists(Node mid |
flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
apf = node.(AccessPathFrontNilNode).getApf()
)
or
@@ -1075,6 +1078,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
flowCandFwd(mid, fromArg, _, config) and
store(mid, f, node) and
nodeCand(node, unbind(config)) and
+ readStoreCand(f, unbind(config)) and
apf.headUsesContent(f)
)
or
@@ -1121,13 +1125,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
apf instanceof AccessPathFrontNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flowCand(mid, toReturn, apf, config)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(node, _, apf, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flowCand(mid, toReturn, nil, config) and
apf instanceof AccessPathFrontNil
)
@@ -1175,12 +1179,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
exists(Content f, AccessPathFront apf0 |
flowCandStore(node, f, toReturn, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, unbind(config))
+ consCand(f, apf, config)
)
or
exists(Content f, AccessPathFront apf0 |
flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, unbind(config)) and
+ consCandFwd(f, apf0, config) and
apf.headUsesContent(f)
)
}
@@ -1221,8 +1225,8 @@ private newtype TAccessPath =
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
/**
- * Conceptually a list of `Content`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
+ * 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.
@@ -1260,7 +1264,7 @@ abstract private class AccessPath extends TAccessPath {
private class AccessPathNil extends AccessPath, TNil {
override string toString() {
- exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -1276,7 +1280,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
override string toString() {
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
// The `concat` becomes "" if `ppReprType` has no result.
- result = f.toString() + concat(" : " + ppReprType(t))
+ result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
)
}
@@ -1293,8 +1297,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
override string toString() {
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
if len = 2
- then result = f1.toString() + ", " + f2.toString()
- else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f1.toString() + ", " + f2.toString() + "]"
+ else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -1362,12 +1366,12 @@ private predicate flowFwd0(
(
exists(Node mid |
flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
ap = node.(AccessPathNilNode).getAp() and
apf = ap.(AccessPathNil).getFront()
)
@@ -1471,13 +1475,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
ap instanceof AccessPathNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flow(mid, toReturn, ap, config)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(node, _, _, ap, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flow(mid, toReturn, nil, config) and
ap instanceof AccessPathNil
)
@@ -1618,11 +1622,14 @@ abstract class PathNode extends TPathNode {
/** Gets a successor of this node, if any. */
abstract PathNode getASuccessor();
+ /** Holds if this node is a source. */
+ abstract predicate isSource();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
exists(string s | s = this.(PathNodeMid).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -1683,12 +1690,6 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
// an intermediate step to another intermediate node
result = getSuccMid()
or
- // a final step to a sink via one or more local steps
- localFlowStepPlus(node, result.getNode(), _, config) and
- ap instanceof AccessPathNil and
- result instanceof PathNodeSink and
- result.getConfiguration() = unbind(this.getConfiguration())
- or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
mid = getSuccMid() and
@@ -1697,23 +1698,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
)
- or
- // a direct step from a source to a sink if a node is both
- this instanceof PathNodeSource and
- result instanceof PathNodeSink and
- this.getNode() = result.getNode() and
- result.getConfiguration() = unbind(this.getConfiguration())
}
-}
-/**
- * A flow graph node corresponding to a source.
- */
-private class PathNodeSource extends PathNodeMid {
- PathNodeSource() {
- getConfiguration().isSource(getNode()) and
- getCallContext() instanceof CallContextAny and
- getAp() instanceof AccessPathNil
+ override predicate isSource() {
+ config.isSource(node) and
+ cc instanceof CallContextAny and
+ ap instanceof AccessPathNil
}
}
@@ -1733,6 +1723,8 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
override Configuration getConfiguration() { result = config }
override PathNode getASuccessor() { none() }
+
+ override predicate isSource() { config.isSource(node) }
}
/**
@@ -1740,14 +1732,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
- localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- ap = mid.getAp()
- or
- localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof AccessPathNil and
- ap = node.(AccessPathNilNode).getAp()
+ exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
+ |
+ localFlowBigStep(midnode, node, true, conf, localCC) and
+ ap = ap0
+ or
+ localFlowBigStep(midnode, node, false, conf, localCC) and
+ ap0 instanceof AccessPathNil and
+ ap = node.(AccessPathNilNode).getAp()
+ )
or
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
or
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
or
- pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
- or
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
or
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
not innercc instanceof CallContextCall
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathOutOfCallable1(
- PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
+ PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
pathOutOfCallable0(mid, pos, innercc) and
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
- exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
- out = getAnOutNode(call, kind)
- )
-}
-
-private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- flow(node, unbind(mid.getConfiguration()))
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+ exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
+ out = kind.getAnOutNode(call)
)
}
@@ -1891,7 +1867,7 @@ private predicate pathIntoCallable(
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
/** Holds if data may flow from `p` to a return of kind `kind`. */
pragma[nomagic]
private predicate paramFlowsThrough(
- ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
) {
- exists(PathNodeMid mid, ReturnNode ret |
+ exists(PathNodeMid mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
)
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathThroughCallable0(
- DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
+ DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
) {
exists(ParameterNode p, CallContext innercc |
pathIntoCallable(mid, p, cc, innercc, call) and
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
- not parameterValueFlowsThrough(p, kind, innercc) and
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
mid.getAp() instanceof AccessPathNil
)
}
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(
- PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
-) {
- exists(DataFlowCall call, ReturnKind kind |
+private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
+ exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, apnil) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
@@ -1967,12 +1941,12 @@ private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallCon
* sinks.
*/
private predicate flowsTo(
- PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink,
- Configuration configuration
+ PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
) {
+ flowsource.isSource() and
flowsource.getConfiguration() = configuration and
flowsource.getNode() = source and
- pathSuccPlus(flowsource, flowsink) and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNode() = sink
}
@@ -1996,16 +1970,10 @@ private module FlowExploration {
// flow into callable
viableParamArg(_, node2, node1)
or
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
+ node2 = kind.getAnOutNode(call)
)
|
c1 = node1.getEnclosingCallable() and
@@ -2082,7 +2050,7 @@ private module FlowExploration {
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
- exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -2094,8 +2062,8 @@ private module FlowExploration {
override string toString() {
exists(Content f, int len | this = TPartialCons(f, len) |
if len = 1
- then result = f.toString()
- else result = f.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f.toString() + "]"
+ else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -2172,7 +2140,7 @@ private module FlowExploration {
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -2216,16 +2184,19 @@ private module FlowExploration {
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
) {
- localFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- or
- additionalLocalFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedRepr(node.getType())) and
- config = mid.getConfiguration()
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ (
+ localFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ ap = mid.getAp() and
+ config = mid.getConfiguration()
+ or
+ additionalLocalFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ mid.getAp() instanceof PartialAccessPathNil and
+ ap = TPartialNil(getErasedRepr(node.getType())) and
+ config = mid.getConfiguration()
+ )
or
jumpStep(mid.getNode(), node, config) and
cc instanceof CallContextAny and
@@ -2247,8 +2218,6 @@ private module FlowExploration {
apConsFwd(ap, f, ap0, config)
)
or
- partialPathOutOfArgument(mid, node, cc, ap, config)
- or
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
or
partialPathOutOfCallable(mid, node, cc, ap, config)
@@ -2307,7 +2276,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathOutOfCallable1(
- PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
+ PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -2321,36 +2290,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
- exists(ReturnKind kind, DataFlowCall call |
+ exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNode(call, kind)
- )
- }
-
- private predicate partialPathOutOfArgument(
- PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
- Configuration config
- ) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+ out = kind.getAnOutNode(call)
)
}
@@ -2389,7 +2334,7 @@ private module FlowExploration {
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -2397,10 +2342,10 @@ private module FlowExploration {
pragma[nomagic]
private predicate paramFlowsThroughInPartialPath(
- ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(PartialPathNodePriv mid, ReturnNode ret |
+ exists(PartialPathNodePriv mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -2417,23 +2362,23 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
- DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
+ DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
PartialAccessPathNil apnil, Configuration config
) {
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
- not parameterValueFlowsThrough(p, kind, innercc)
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
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 ed007554d4c..7da10f030d6 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
@@ -51,7 +51,9 @@ class ArgumentNode extends Node {
DataFlowCall getCall() { this.argumentOf(result, _) }
}
-private newtype TReturnKind = TNormalReturnKind()
+private newtype TReturnKind =
+ TNormalReturnKind() or
+ TRefReturnKind(int i) { exists(Parameter parameter | i = parameter.getIndex()) }
/**
* A return kind. A return kind describes how a value can be returned
@@ -59,23 +61,54 @@ private newtype TReturnKind = TNormalReturnKind()
*/
class ReturnKind extends TReturnKind {
/** Gets a textual representation of this return kind. */
- string toString() { result = "return" }
+ string toString() {
+ this instanceof TNormalReturnKind and
+ result = "return"
+ or
+ this instanceof TRefReturnKind and
+ result = "ref"
+ }
}
-/** A data flow node that occurs as the result of a `ReturnStmt`. */
-class ReturnNode extends ExprNode {
- ReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getExpr()) }
+/** A data flow node that represents a returned value in the called function. */
+abstract class ReturnNode extends Node {
+ /** Gets the kind of this returned value. */
+ abstract ReturnKind getKind();
+}
+
+/** A `ReturnNode` that occurs as the result of a `ReturnStmt`. */
+private class NormalReturnNode extends ReturnNode, ExprNode {
+ NormalReturnNode() { exists(ReturnStmt ret | this.getExpr() = ret.getExpr()) }
/** Gets the kind of this returned value. */
- ReturnKind getKind() { result = TNormalReturnKind() }
+ override ReturnKind getKind() { result = TNormalReturnKind() }
}
-/** A data flow node that represents the output of a call. */
-class OutNode extends ExprNode {
- OutNode() { this.getExpr() instanceof Call }
+/**
+ * A `ReturnNode` that occurs as a result of a definition of a reference
+ * parameter reaching the end of a function body.
+ */
+private class RefReturnNode extends ReturnNode, RefParameterFinalValueNode {
+ /** Gets the kind of this returned value. */
+ override ReturnKind getKind() { result = TRefReturnKind(this.getParameter().getIndex()) }
+}
+
+/** A data flow node that represents the output of a call at the call site. */
+abstract class OutNode extends Node {
+ /** Gets the underlying call. */
+ abstract DataFlowCall getCall();
+}
+
+private class ExprOutNode extends OutNode, ExprNode {
+ ExprOutNode() { this.getExpr() instanceof Call }
/** Gets the underlying call. */
- DataFlowCall getCall() { result = this.getExpr() }
+ override DataFlowCall getCall() { result = this.getExpr() }
+}
+
+private class RefOutNode extends OutNode, DefinitionByReferenceNode {
+ /** Gets the underlying call. */
+ override DataFlowCall getCall() { result = this.getArgument().getParent() }
}
/**
@@ -85,6 +118,11 @@ class OutNode extends ExprNode {
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
result = call.getNode() and
kind = TNormalReturnKind()
+ or
+ exists(int i |
+ result.asDefiningArgument() = call.getArgument(i) and
+ kind = TRefReturnKind(i)
+ )
}
/**
@@ -264,3 +302,5 @@ class DataFlowCall extends Expr {
/** Gets the enclosing callable of this call. */
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
}
+
+predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
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 cc9687296f7..15a9dfc66e3 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -5,6 +5,8 @@
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.valuenumbering.GlobalValueNumbering
cached
private newtype TNode =
@@ -25,7 +27,8 @@ private newtype TNode =
not c.getTarget().getParameter(i).getUnderlyingType().(PointerType).getBaseType().isConst()
)
} or
- TUninitializedNode(LocalVariable v) { not v.hasInitializer() }
+ TUninitializedNode(LocalVariable v) { not v.hasInitializer() } or
+ TRefParameterFinalValueNode(Parameter p) { exists(FlowVar var | var.reachesRefParameter(p)) }
/**
* A node in a data flow graph.
@@ -248,6 +251,23 @@ class UninitializedNode extends Node, TUninitializedNode {
LocalVariable getLocalVariable() { result = v }
}
+/** INTERNAL: do not use. The final value of a non-const ref parameter. */
+class RefParameterFinalValueNode extends Node, TRefParameterFinalValueNode {
+ Parameter p;
+
+ RefParameterFinalValueNode() { this = TRefParameterFinalValueNode(p) }
+
+ override Function getFunction() { result = p.getFunction() }
+
+ override Type getType() { result = p.getType() }
+
+ override string toString() { result = p.toString() }
+
+ override Location getLocation() { result = p.getLocation() }
+
+ Parameter getParameter() { result = p }
+}
+
/**
* A node associated with an object after an operation that might have
* changed its state.
@@ -490,7 +510,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
or
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
) and
- varToExprStep(var, nodeTo.asExpr())
+ varToNodeStep(var, nodeTo)
)
or
// Expr -> DefinitionByReferenceNode
@@ -533,9 +553,13 @@ private predicate exprToVarStep(Expr assignedExpr, FlowVar var) {
}
/**
- * Holds if the expression `e` is an access of the variable `var`.
+ * Holds if the node `n` is an access of the variable `var`.
*/
-private predicate varToExprStep(FlowVar var, Expr e) { e = var.getAnAccess() }
+private predicate varToNodeStep(FlowVar var, Node n) {
+ n.asExpr() = var.getAnAccess()
+ or
+ var.reachesRefParameter(n.(RefParameterFinalValueNode).getParameter())
+}
/**
* Holds if data flows from `fromExpr` to `toExpr` directly, in the case
@@ -553,6 +577,10 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
or
toExpr = any(StmtExpr stmtExpr | fromExpr = stmtExpr.getResultExpr())
or
+ toExpr.(AddressOfExpr).getOperand() = fromExpr
+ or
+ toExpr.(BuiltInOperationBuiltInAddressOf).getOperand() = fromExpr
+ or
// The following case is needed to track the qualifier object for flow
// through fields. It gives flow from `T(x)` to `new T(x)`. That's not
// strictly _data_ flow but _taint_ flow because the type of `fromExpr` is
@@ -574,8 +602,8 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel, int iIn |
call.getTarget() = f and
f.hasDataFlow(inModel, outModel) and
- outModel.isOutReturnValue() and
- inModel.isInParameter(iIn) and
+ outModel.isReturnValue() and
+ inModel.isParameter(iIn) and
fromExpr = call.getArgument(iIn)
)
)
@@ -585,12 +613,12 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
call.getTarget() = f and
argOut = call.getArgument(argOutIndex) and
- outModel.isOutParameterPointer(argOutIndex) and
+ outModel.isParameterDeref(argOutIndex) and
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
- inModel.isInParameterPointer(argInIndex) and
+ inModel.isParameterDeref(argInIndex) and
call.passesByReference(argInIndex, exprIn)
or
- inModel.isInParameter(argInIndex) and
+ inModel.isParameter(argInIndex) and
exprIn = call.getArgument(argInIndex)
)
)
@@ -654,12 +682,16 @@ VariableAccess getAnAccessToAssignedVariable(Expr assign) {
*
* It is important that all extending classes in scope are disjoint.
*/
-class BarrierGuard extends Expr {
- /** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `branch`. */
- abstract deprecated predicate checks(Expr e, boolean branch);
+class BarrierGuard extends GuardCondition {
+ /** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */
+ abstract predicate checks(Expr e, boolean b);
/** Gets a node guarded by this guard. */
- final Node getAGuardedNode() {
- none() // stub
+ final ExprNode getAGuardedNode() {
+ exists(GVN value, boolean branch |
+ result.getExpr() = value.getAnExpr() and
+ this.checks(value.getAnExpr(), branch) and
+ this.controls(result.getExpr().getBasicBlock(), branch)
+ )
}
}
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 b7820b9f34e..2ed106a4940 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll
@@ -62,9 +62,20 @@ class FlowVar extends TFlowVar {
cached
abstract predicate definedByReference(Expr arg);
+ /**
+ * Holds if this `FlowVar` is a `PartialDefinition` whose defined expression
+ * is `e`.
+ */
cached
abstract predicate definedPartiallyAt(Expr e);
+ /**
+ * Holds if this `FlowVar` is a definition of a reference parameter `p` that
+ * persists until the function returns.
+ */
+ cached
+ abstract predicate reachesRefParameter(Parameter p);
+
/**
* Holds if this `FlowVar` corresponds to the initial value of `v`. The following
* is an exhaustive list of cases where this may happen.
@@ -338,6 +349,9 @@ module FlowVar_internal {
param = v
}
+ // `fullySupportedSsaVariable` excludes reference types
+ override predicate reachesRefParameter(Parameter p) { none() }
+
/**
* Holds if this `SsaVar` corresponds to a non-phi definition. Users of this
* library will never directly use an `SsaVar` that comes from a phi node,
@@ -387,6 +401,13 @@ module FlowVar_internal {
sbb = v.(Parameter).getFunction().getEntryPoint()
}
+ override predicate reachesRefParameter(Parameter p) {
+ parameterIsNonConstReference(p) and
+ p = v and
+ // This definition reaches the exit node of the function CFG
+ getAReachedBlockVarSBB(this).getANode() = p.getFunction()
+ }
+
override predicate definedByInitialValue(LocalScopeVariable lsv) {
blockVarDefinedByVariable(sbb, lsv) and
lsv = v
@@ -490,7 +511,7 @@ module FlowVar_internal {
exists(VariableAccess va |
va.getTarget() = result and
readAccess(va) and
- bbNotInLoop(va.getBasicBlock())
+ exists(BasicBlock bb | bb = va.getBasicBlock() | not this.bbInLoop(bb))
)
}
@@ -513,10 +534,8 @@ module FlowVar_internal {
bbInLoopCondition(bb)
}
- predicate bbNotInLoop(BasicBlock bb) {
- not this.bbInLoop(bb) and
- bb.getEnclosingFunction() = this.getEnclosingFunction()
- }
+ /** Holds if `sbb` is inside this loop. */
+ predicate sbbInLoop(SubBasicBlock sbb) { this.bbInLoop(sbb.getBasicBlock()) }
/**
* Holds if `bb` is a basic block inside this loop where `v` has not been
@@ -537,22 +556,19 @@ module FlowVar_internal {
}
/**
- * Holds if some loop always assigns to `v` before leaving through an edge
- * from `bbInside` in its condition to `bbOutside` outside the loop, where
- * (`sbbDef`, `v`) is a `BlockVar` defined outside the loop. Also, `v` must
- * be used outside the loop.
+ * Holds if `loop` always assigns to `v` before leaving through an edge
+ * from `bbInside` in its condition to `bbOutside` outside the loop. Also,
+ * `v` must be used outside the loop.
*/
predicate skipLoop(
- SubBasicBlock sbbInside, SubBasicBlock sbbOutside, SubBasicBlock sbbDef, Variable v
+ SubBasicBlock sbbInside, SubBasicBlock sbbOutside, Variable v, AlwaysTrueUponEntryLoop loop
) {
- exists(AlwaysTrueUponEntryLoop loop, BasicBlock bbInside, BasicBlock bbOutside |
+ exists(BasicBlock bbInside, BasicBlock bbOutside |
loop.alwaysAssignsBeforeLeavingCondition(bbInside, bbOutside, v) and
bbInside = sbbInside.getBasicBlock() and
bbOutside = sbbOutside.getBasicBlock() and
sbbInside.lastInBB() and
- sbbOutside.firstInBB() and
- loop.bbNotInLoop(sbbDef.getBasicBlock()) and
- exists(TBlockVar(sbbDef, v))
+ sbbOutside.firstInBB()
)
}
@@ -571,7 +587,7 @@ module FlowVar_internal {
start = TBlockVar(sbbDef, v) and
result = mid.getASuccessor() and
variableLiveInSBB(result, v) and
- not skipLoop(mid, result, sbbDef, v) and
+ forall(AlwaysTrueUponEntryLoop loop | skipLoop(mid, result, v, loop) | loop.sbbInLoop(sbbDef)) and
not assignmentLikeOperation(result, v, _, _)
)
}
@@ -593,12 +609,23 @@ module FlowVar_internal {
private predicate variableLiveInSBB(SubBasicBlock sbb, Variable v) {
variableAccessInSBB(v, sbb, _)
or
+ // Non-const reference parameters are live at the end of the function
+ parameterIsNonConstReference(v) and
+ sbb.contains(v.(Parameter).getFunction())
+ or
exists(SubBasicBlock succ | succ = sbb.getASuccessor() |
variableLiveInSBB(succ, v) and
not variableNotLiveBefore(succ, v)
)
}
+ predicate parameterIsNonConstReference(Parameter p) {
+ exists(ReferenceType refType |
+ refType = p.getUnderlyingType() and
+ not refType.getBaseType().isConst()
+ )
+ }
+
/**
* Holds if liveness of `v` should stop propagating backwards from `sbb`.
*/
@@ -679,10 +706,11 @@ module FlowVar_internal {
predicate dominatedByOverwrite(UninitializedLocalVariable v, VariableAccess va) {
exists(BasicBlock bb, int vaIndex |
va = bb.getNode(vaIndex) and
- va.getTarget() = v
- |
+ va.getTarget() = v and
vaIndex > indexOfFirstOverwriteInBB(v, bb)
or
+ va = bb.getNode(vaIndex) and
+ va.getTarget() = v and
bbStrictlyDominates(getAnOverwritingBB(v), bb)
)
}
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 2343bc7a5c3..a97d02fe15d 100644
--- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
@@ -122,11 +122,11 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
call.getTarget() = f and
argOut = call.getArgument(argOutIndex) and
- outModel.isOutParameterPointer(argOutIndex) and
+ outModel.isParameterDeref(argOutIndex) and
exists(int argInIndex, FunctionInput inModel | f.hasDataFlow(inModel, outModel) |
// Taint flows from a pointer to a dereference, which DataFlow does not handle
// memcpy(&dest_var, tainted_ptr, len)
- inModel.isInParameterPointer(argInIndex) and
+ inModel.isParameterDeref(argInIndex) and
exprIn = call.getArgument(argInIndex)
)
)
@@ -134,15 +134,15 @@ private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
exists(TaintFunction f, Call call, FunctionOutput outModel, int argOutIndex |
call.getTarget() = f and
argOut = call.getArgument(argOutIndex) and
- outModel.isOutParameterPointer(argOutIndex) and
+ outModel.isParameterDeref(argOutIndex) and
exists(int argInIndex, FunctionInput inModel | f.hasTaintFlow(inModel, outModel) |
- inModel.isInParameterPointer(argInIndex) and
+ inModel.isParameterDeref(argInIndex) and
exprIn = call.getArgument(argInIndex)
or
- inModel.isInParameterPointer(argInIndex) and
+ inModel.isParameterDeref(argInIndex) and
call.passesByReference(argInIndex, exprIn)
or
- inModel.isInParameter(argInIndex) and
+ inModel.isParameter(argInIndex) and
exprIn = call.getArgument(argInIndex)
)
)
diff --git a/cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatchPrototype.qll b/cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatchPrototype.qll
new file mode 100644
index 00000000000..9992f88867b
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatchPrototype.qll
@@ -0,0 +1,87 @@
+import cpp
+
+/**
+ * A module for performing simple virtual dispatch analysis.
+ */
+module VirtualDispatch {
+ /**
+ * Gets a possible implementation target when the given function is the static target of a virtual call.
+ */
+ private MemberFunction getAPossibleImplementation(MemberFunction staticTarget) {
+ /*
+ * `IUnknown` is a COM interface with many sub-types, and many overrides (tens of thousands on
+ * some databases), so we ignore any member functions defined within that interface.
+ */
+
+ not staticTarget.getDeclaringType().hasName("IUnknown") and
+ result = staticTarget.getAnOverridingFunction*()
+ }
+
+ /** Gets the static type of the qualifier expression for the given call. */
+ private Class getCallQualifierType(FunctionCall c) {
+ result = c.getQualifier().getType().stripType() and
+ /*
+ * `IUnknown` is a COM interface with many sub-types (tens of thousands on some databases), so
+ * we ignore any cases where the qualifier type is that interface.
+ */
+
+ not result.hasName("IUnknown")
+ }
+
+ /**
+ * Helper predicate for `getAViableTarget`, which computes the viable targets for
+ * virtual calls based on the qualifier type.
+ */
+ private Function getAViableVirtualCallTarget(Class qualifierType, MemberFunction staticTarget) {
+ exists(Class qualifierSubType |
+ result = getAPossibleImplementation(staticTarget) and
+ qualifierType = qualifierSubType.getABaseClass*() and
+ mayInherit(qualifierSubType, result) and
+ not cannotInherit(qualifierSubType, result)
+ )
+ }
+
+ /**
+ * Gets a viable target for the given function call.
+ *
+ * If `c` is a virtual call, then we will perform a simple virtual dispatch analysis to return
+ * the `Function` instances which might be a viable target, based on an analysis of the declared
+ * type of the qualifier expression.
+ *
+ * (This analysis is imprecise: it looks for subtypes of the declared type of the qualifier expression
+ * and the possible implementations of `c.getTarget()` that are declared or inherited by those subtypes.
+ * This does not account for virtual inheritance and the ways this affects dispatch.)
+ *
+ * If `c` is not a virtual call, the result will be `c.getTarget()`.
+ */
+ Function getAViableTarget(Call c) {
+ if c.(FunctionCall).isVirtual() and c.getTarget() instanceof MemberFunction
+ then result = getAViableVirtualCallTarget(getCallQualifierType(c), c.getTarget())
+ else result = c.getTarget()
+ }
+
+ /** Holds if `f` is declared in `c` or a transitive base class of `c`. */
+ private predicate mayInherit(Class c, MemberFunction f) {
+ f.getDeclaringType() = c.getABaseClass*()
+ }
+
+ /**
+ * Holds if `c` cannot inherit the member function `f`,
+ * that is, `c` or one of its supertypes overrides `f`.
+ */
+ private predicate cannotInherit(Class c, MemberFunction f) {
+ exists(Class overridingType, MemberFunction override |
+ cannotInheritHelper(c, f, overridingType, override) and
+ override.overrides+(f)
+ )
+ }
+
+ pragma[noinline]
+ private predicate cannotInheritHelper(
+ Class c, MemberFunction f, Class overridingType, MemberFunction override
+ ) {
+ c.getABaseClass*() = overridingType and
+ override.getDeclaringType() = overridingType and
+ overridingType.getABaseClass+() = f.getDeclaringType()
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
index cf8c2ad5998..f68eaef5d8b 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Access.qll
@@ -18,7 +18,18 @@ abstract class Access extends Expr, NameQualifiableElement {
}
/**
- * A C/C++ enum constant access expression.
+ * A C/C++ `enum` constant access expression. For example the access to
+ * `MYENUMCONST1` in `myFunction` in the following code:
+ * ```
+ * enum MyEnum {
+ * MYENUMCONST1,
+ * MYENUMCONST2
+ * };
+ *
+ * void myFunction() {
+ * MyEnum v = MYENUMCONST1;
+ * };
+ * ```
*/
class EnumConstantAccess extends Access, @varaccess {
override string getCanonicalQLClass() { result = "EnumConstantAccess" }
@@ -27,15 +38,23 @@ class EnumConstantAccess extends Access, @varaccess {
exists(EnumConstant c | varbind(underlyingElement(this), unresolveElement(c)))
}
- /** Gets the accessed enum constant. */
+ /** Gets the accessed `enum` constant. */
override EnumConstant getTarget() { varbind(underlyingElement(this), unresolveElement(result)) }
- /** Gets a textual representation of this enum constant access. */
+ /** Gets a textual representation of this `enum` constant access. */
override string toString() { result = this.getTarget().getName() }
}
/**
- * A C/C++ variable access expression.
+ * A C/C++ variable access expression. For example the accesses to
+ * `x` and `y` in `myFunction` in the following code:
+ * ```
+ * int x;
+ *
+ * void myFunction(int y) {
+ * x = y;
+ * };
+ * ```
*/
class VariableAccess extends Access, @varaccess {
override string getCanonicalQLClass() { result = "VariableAccess" }
@@ -129,7 +148,18 @@ class VariableAccess extends Access, @varaccess {
}
/**
- * A C/C++ field access expression.
+ * A C/C++ field access expression. For example the accesses to
+ * `x` and `y` in `myMethod` in the following code:
+ * ```
+ * class MyClass {
+ * public:
+ * void myMethod(MyClass &other) {
+ * x = other.y;
+ * }
+ *
+ * int x, y;
+ * };
+ * ```
*/
class FieldAccess extends VariableAccess {
override string getCanonicalQLClass() { result = "FieldAccess" }
@@ -141,8 +171,23 @@ class FieldAccess extends VariableAccess {
}
/**
- * A field access of the form `obj->field`. The type of `obj` is a pointer,
- * so this is equivalent to `(*obj).field`.
+ * A field access whose qualifier is a pointer to a class, struct or union.
+ * These typically take the form `obj->field`. Another case is a field access
+ * with an implicit `this->` qualifier, which is often a `PointerFieldAccess`
+ * (but see also `ImplicitThisFieldAccess`).
+ *
+ * For example the accesses to `x` and `y` in `myMethod` in the following code
+ * are each a `PointerFieldAccess`:
+ * ```
+ * class MyClass {
+ * public:
+ * void myMethod(MyClass *other) {
+ * other->x = y;
+ * }
+ *
+ * int x, y;
+ * };
+ * ```
*/
class PointerFieldAccess extends FieldAccess {
override string getCanonicalQLClass() { result = "PointerFieldAccess" }
@@ -169,7 +214,18 @@ class DotFieldAccess extends FieldAccess {
/**
* A field access of the form `obj.field`, where the type of `obj` is a
- * reference to a class/struct/union.
+ * reference to a class/struct/union. For example the accesses to `y` in
+ * `myMethod` in the following code:
+ * ```
+ * class MyClass {
+ * public:
+ * void myMethod(MyClass a, MyClass &b) {
+ * a.x = b.y;
+ * }
+ *
+ * int x, y;
+ * };
+ * ```
*/
class ReferenceFieldAccess extends DotFieldAccess {
override string getCanonicalQLClass() { result = "ReferenceFieldAccess" }
@@ -179,7 +235,18 @@ class ReferenceFieldAccess extends DotFieldAccess {
/**
* A field access of the form `obj.field`, where the type of `obj` is a
- * class/struct/union (and not a reference).
+ * class/struct/union (and not a reference). For example the accesses to `x`
+ * in `myMethod` in the following code:
+ * ```
+ * class MyClass {
+ * public:
+ * void myMethod(MyClass a, MyClass &b) {
+ * a.x = b.y;
+ * }
+ *
+ * int x, y;
+ * };
+ * ```
*/
class ValueFieldAccess extends DotFieldAccess {
override string getCanonicalQLClass() { result = "ValueFieldAccess" }
@@ -198,25 +265,40 @@ private predicate referenceConversion(Conversion c) {
/**
* Holds if `e` is a reference expression (that is, it has a type of the
* form `T&`), which is converted to a value. For example:
- *
* ```
* int myfcn(MyStruct &x) {
* return x.field;
* }
* ```
- *
* In this example, the type of `x` is `MyStruct&`, but it gets implicitly
* converted to `MyStruct` in the expression `x.field`.
*/
private predicate exprHasReferenceConversion(Expr e) { referenceConversion(e.getConversion+()) }
/**
- * A field access of a field of `this`. The access has no qualifier because
- * the use of `this` is implicit. For example, `field` is equivalent to
- * `this->field` if `field` is a member of `this`.
+ * A field access of a field of `this` which has no qualifier because
+ * the use of `this` is implicit. For example, in the following code the
+ * implicit call to the destructor of `A` has no qualifier because the
+ * use of `this` is implicit:
+ * ```
+ * class A {
+ * public:
+ * ~A() {
+ * // ...
+ * }
+ * };
*
+ * class B {
+ * public:
+ * A a;
+ *
+ * ~B() {
+ * // Implicit call to the destructor of `A`.
+ * }
+ * };
+ * ```
* Note: the C++ front-end often automatically desugars `field` to
- * `this->field`, so most implicit accesses of `this->field` are instances
+ * `this->field`, so most accesses of `this->field` are instances
* of `PointerFieldAccess` (with `ThisExpr` as the qualifier), not
* `ImplicitThisFieldAccess`.
*/
@@ -227,7 +309,38 @@ class ImplicitThisFieldAccess extends FieldAccess {
}
/**
- * A C/C++ function access expression.
+ * A C++ _pointer to non-static data member_ literal. For example, `&C::x` is
+ * an expression that refers to field `x` of class `C`. If the type of that
+ * field is `int`, then `&C::x` ought to have type `int C::*`. It is currently
+ * modeled in QL as having type `int`.
+ *
+ * See [dcl.mptr] in the C++17 standard or see
+ * https://en.cppreference.com/w/cpp/language/pointer#Pointers_to_data_members.
+ */
+class PointerToFieldLiteral extends ImplicitThisFieldAccess {
+ PointerToFieldLiteral() {
+ // The extractor currently emits a pointer-to-field literal as a field
+ // access without a qualifier. The only other unqualified field accesses it
+ // emits are for compiler-generated constructors and destructors. When we
+ // filter those out, there are only pointer-to-field literals left.
+ not this.isCompilerGenerated()
+ }
+
+ override predicate isConstant() { any() }
+
+ override string getCanonicalQLClass() { result = "PointerToFieldLiteral" }
+}
+
+/**
+ * A C/C++ function access expression. For example the access to
+ * `myFunctionTarget` in `myFunction` in the following code:
+ * ```
+ * int myFunctionTarget(int);
+ *
+ * void myFunction() {
+ * int (*myFunctionPointer)(int) = &myTarget;
+ * }
+ * ```
*/
class FunctionAccess extends Access, @routineexpr {
FunctionAccess() { not iscall(underlyingElement(this), _) }
@@ -246,7 +359,7 @@ class FunctionAccess extends Access, @routineexpr {
}
/**
- * An access to a parameter of a function signature for the purposes of a decltype.
+ * An access to a parameter of a function signature for the purposes of a `decltype`.
*
* For example, given the following code:
* ```
@@ -256,7 +369,7 @@ class FunctionAccess extends Access, @routineexpr {
* }
* ```
* The return type of the function is a decltype, the expression of which contains
- * an add expression, which in turn has two ParamAccessForType children.
+ * an add expression, which in turn has two `ParamAccessForType` children.
*/
class ParamAccessForType extends Expr, @param_ref {
override string toString() { result = "param access" }
@@ -264,7 +377,22 @@ class ParamAccessForType extends Expr, @param_ref {
/**
* An access to a type. This occurs in certain contexts where a built-in
- * works on types directly rather than variables, expressions etc.
+ * works on types directly rather than variables, expressions etc. For
+ * example the reference to `MyClass` in `__is_pod` in the following code:
+ * ```
+ * class MyClass {
+ * ...
+ * };
+ *
+ * void myFunction() {
+ * if (__is_pod(MyClass))
+ * {
+ * ...
+ * } else {
+ * ...
+ * }
+ * }
+ * ```
*/
class TypeName extends Expr, @type_operand {
override string getCanonicalQLClass() { result = "TypeName" }
@@ -273,9 +401,17 @@ class TypeName extends Expr, @type_operand {
}
/**
- * A C/C++ array access expression.
+ * A C/C++ array access expression. For example, the access to `as` in
+ * `myFunction` in the following code:
+ * ```
+ * int as[10];
*
- * For calls to operator[], which look syntactically identical, see OverloadedArrayExpr.
+ * void myFunction() {
+ * as[0]++;
+ * }
+ * ```
+ * For calls to `operator[]`, which look syntactically identical, see
+ * `OverloadedArrayExpr`.
*/
class ArrayExpr extends Expr, @subscriptexpr {
override string getCanonicalQLClass() { result = "ArrayExpr" }
@@ -283,14 +419,14 @@ class ArrayExpr extends Expr, @subscriptexpr {
/**
* Gets the array or pointer expression being subscripted.
*
- * This is arr in both arr[0] and 0[arr].
+ * This is `arr` in both `arr[0]` and `0[arr]`.
*/
Expr getArrayBase() { result = this.getChild(0) }
/**
* Gets the expression giving the index into the array.
*
- * This is 0 in both arr[0] and 0[arr].
+ * This is `0` in both `arr[0]` and `0[arr]`.
*/
Expr getArrayOffset() { result = this.getChild(1) }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll
index 48af2b539c7..741d63fbc44 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/ArithmeticOperation.qll
@@ -1,12 +1,17 @@
import semmle.code.cpp.exprs.Expr
/**
- * A C/C++ arithmetic operation.
+ * A C/C++ unary arithmetic operation.
+ *
+ * This is an abstract base QL class.
*/
abstract class UnaryArithmeticOperation extends UnaryOperation { }
/**
* A C/C++ unary minus expression.
+ * ```
+ * b = - a;
+ * ```
*/
class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr {
override string getOperator() { result = "-" }
@@ -18,6 +23,9 @@ class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr {
/**
* A C/C++ unary plus expression.
+ * ```
+ * b = + a;
+ * ```
*/
class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr {
override string getOperator() { result = "+" }
@@ -28,16 +36,26 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr {
}
/**
- * A C/C++ GNU conjugation expression.
+ * A C/C++ GNU conjugation expression. It operates on `_Complex` or
+ * `__complex__ `numbers, and is similar to the C99 `conj`, `conjf` and `conjl`
+ * functions.
+ * ```
+ * _Complex double a = ( 1.0, 2.0 );
+ * _Complex double b = ~ a; // ( 1.0, - 2.0 )
+ * ```
*/
class ConjugationExpr extends UnaryArithmeticOperation, @conjugation {
override string getOperator() { result = "~" }
+
+ override string getCanonicalQLClass() { result = "ConjugationExpr" }
}
/**
* A C/C++ `++` or `--` expression (either prefix or postfix).
*
- * Note that this doesn't include calls to user-defined `operator++`
+ * This is the abstract base QL class for increment and decrement operations.
+ *
+ * Note that this does not include calls to user-defined `operator++`
* or `operator--`.
*/
abstract class CrementOperation extends UnaryArithmeticOperation {
@@ -56,35 +74,38 @@ abstract class CrementOperation extends UnaryArithmeticOperation {
/**
* A C/C++ `++` expression (either prefix or postfix).
*
- * Note that this doesn't include calls to user-defined `operator++`.
+ * Note that this does not include calls to user-defined `operator++`.
*/
abstract class IncrementOperation extends CrementOperation { }
/**
* A C/C++ `--` expression (either prefix or postfix).
*
- * Note that this doesn't include calls to user-defined `operator--`.
+ * Note that this does not include calls to user-defined `operator--`.
*/
abstract class DecrementOperation extends CrementOperation { }
/**
* A C/C++ `++` or `--` prefix expression.
*
- * Note that this doesn't include calls to user-defined operators.
+ * Note that this does not include calls to user-defined operators.
*/
abstract class PrefixCrementOperation extends CrementOperation { }
/**
* A C/C++ `++` or `--` postfix expression.
*
- * Note that this doesn't include calls to user-defined operators.
+ * Note that this does not include calls to user-defined operators.
*/
abstract class PostfixCrementOperation extends CrementOperation { }
/**
* A C/C++ prefix increment expression, as in `++x`.
*
- * Note that this doesn't include calls to user-defined `operator++`.
+ * Note that this does not include calls to user-defined `operator++`.
+ * ```
+ * b = ++a;
+ * ```
*/
class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preincrexpr {
override string getOperator() { result = "++" }
@@ -97,7 +118,10 @@ class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preinc
/**
* A C/C++ prefix decrement expression, as in `--x`.
*
- * Note that this doesn't include calls to user-defined `operator--`.
+ * Note that this does not include calls to user-defined `operator--`.
+ * ```
+ * b = --a;
+ * ```
*/
class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predecrexpr {
override string getOperator() { result = "--" }
@@ -110,7 +134,10 @@ class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predec
/**
* A C/C++ postfix increment expression, as in `x++`.
*
- * Note that this doesn't include calls to user-defined `operator++`.
+ * Note that this does not include calls to user-defined `operator++`.
+ * ```
+ * b = a++;
+ * ```
*/
class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @postincrexpr {
override string getOperator() { result = "++" }
@@ -125,7 +152,10 @@ class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @post
/**
* A C/C++ postfix decrement expression, as in `x--`.
*
- * Note that this doesn't include calls to user-defined `operator--`.
+ * Note that this does not include calls to user-defined `operator--`.
+ * ```
+ * b = a--;
+ * ```
*/
class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @postdecrexpr {
override string getOperator() { result = "--" }
@@ -138,26 +168,45 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post
}
/**
- * A C/C++ GNU real part expression.
+ * A C/C++ GNU real part expression. It operates on `_Complex` or
+ * `__complex__` numbers.
+ * ```
+ * _Complex double f = { 2.0, 3.0 };
+ * double d = __real(f); // 2.0
+ * ```
*/
class RealPartExpr extends UnaryArithmeticOperation, @realpartexpr {
override string getOperator() { result = "__real" }
+
+ override string getCanonicalQLClass() { result = "RealPartExpr" }
}
/**
- * A C/C++ GNU imaginary part expression.
+ * A C/C++ GNU imaginary part expression. It operates on `_Complex` or
+ * `__complex__` numbers.
+ * ```
+ * _Complex double f = { 2.0, 3.0 };
+ * double d = __imag(f); // 3.0
+ * ```
*/
class ImaginaryPartExpr extends UnaryArithmeticOperation, @imagpartexpr {
override string getOperator() { result = "__imag" }
+
+ override string getCanonicalQLClass() { result = "ImaginaryPartExpr" }
}
/**
* A C/C++ binary arithmetic operation.
+ *
+ * This is an abstract base QL class for all binary arithmetic operations.
*/
abstract class BinaryArithmeticOperation extends BinaryOperation { }
/**
* A C/C++ add expression.
+ * ```
+ * c = a + b;
+ * ```
*/
class AddExpr extends BinaryArithmeticOperation, @addexpr {
override string getOperator() { result = "+" }
@@ -169,6 +218,9 @@ class AddExpr extends BinaryArithmeticOperation, @addexpr {
/**
* A C/C++ subtract expression.
+ * ```
+ * c = a - b;
+ * ```
*/
class SubExpr extends BinaryArithmeticOperation, @subexpr {
override string getOperator() { result = "-" }
@@ -180,6 +232,9 @@ class SubExpr extends BinaryArithmeticOperation, @subexpr {
/**
* A C/C++ multiply expression.
+ * ```
+ * c = a * b;
+ * ```
*/
class MulExpr extends BinaryArithmeticOperation, @mulexpr {
override string getOperator() { result = "*" }
@@ -191,6 +246,9 @@ class MulExpr extends BinaryArithmeticOperation, @mulexpr {
/**
* A C/C++ divide expression.
+ * ```
+ * c = a / b;
+ * ```
*/
class DivExpr extends BinaryArithmeticOperation, @divexpr {
override string getOperator() { result = "/" }
@@ -202,6 +260,9 @@ class DivExpr extends BinaryArithmeticOperation, @divexpr {
/**
* A C/C++ remainder expression.
+ * ```
+ * c = a % b;
+ * ```
*/
class RemExpr extends BinaryArithmeticOperation, @remexpr {
override string getOperator() { result = "%" }
@@ -212,71 +273,133 @@ class RemExpr extends BinaryArithmeticOperation, @remexpr {
}
/**
- * A C/C++ multiply expression with an imaginary number.
+ * A C/C++ multiply expression with an imaginary number. This is specific to
+ * C99 and later.
+ * ```
+ * double z;
+ * _Imaginary double x, y;
+ * z = x * y;
+ * ```
*/
class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
override string getOperator() { result = "*" }
+ override string getCanonicalQLClass() { result = "ImaginaryMulExpr" }
+
override int getPrecedence() { result = 13 }
}
/**
- * A C/C++ divide expression with an imaginary number.
+ * A C/C++ divide expression with an imaginary number. This is specific to
+ * C99 and later.
+ * ```
+ * double z;
+ * _Imaginary double y;
+ * z = z / y;
+ * ```
*/
class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
override string getOperator() { result = "/" }
+ override string getCanonicalQLClass() { result = "ImaginaryDivExpr" }
+
override int getPrecedence() { result = 13 }
}
/**
- * A C/C++ add expression with a real term and an imaginary term.
+ * A C/C++ add expression with a real term and an imaginary term. This is
+ * specific to C99 and later.
+ * ```
+ * double z;
+ * _Imaginary double x;
+ * _Complex double w;
+ * w = z + x;
+ * ```
*/
class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
override string getOperator() { result = "+" }
+ override string getCanonicalQLClass() { result = "RealImaginaryAddExpr" }
+
override int getPrecedence() { result = 12 }
}
/**
- * A C/C++ add expression with an imaginary term and a real term.
+ * A C/C++ add expression with an imaginary term and a real term. This is
+ * specific to C99 and later.
+ * ```
+ * double z;
+ * _Imaginary double x;
+ * _Complex double w;
+ * w = x + z;
+ * ```
*/
class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
override string getOperator() { result = "+" }
+ override string getCanonicalQLClass() { result = "ImaginaryRealAddExpr" }
+
override int getPrecedence() { result = 12 }
}
/**
- * A C/C++ subtract expression with a real term and an imaginary term.
+ * A C/C++ subtract expression with a real term and an imaginary term. This is
+ * specific to C99 and later.
+ * ```
+ * double z;
+ * _Imaginary double x;
+ * _Complex double w;
+ * w = z - x;
+ * ```
*/
class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
override string getOperator() { result = "-" }
+ override string getCanonicalQLClass() { result = "RealImaginarySubExpr" }
+
override int getPrecedence() { result = 12 }
}
/**
- * A C/C++ subtract expression with an imaginary term and a real term.
+ * A C/C++ subtract expression with an imaginary term and a real term. This is
+ * specific to C99 and later.
+ * ```
+ * double z;
+ * _Imaginary double x;
+ * _Complex double w;
+ * w = x - z;
+ * ```
*/
class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
override string getOperator() { result = "-" }
+ override string getCanonicalQLClass() { result = "ImaginaryRealSubExpr" }
+
override int getPrecedence() { result = 12 }
}
/**
* A C/C++ GNU min expression.
+ * ```
+ * c = a b;
+ * ```
*/
class MinExpr extends BinaryArithmeticOperation, @minexpr {
override string getOperator() { result = "" }
+
+ override string getCanonicalQLClass() { result = "MinExpr" }
}
/**
* A C/C++ GNU max expression.
+ * ```
+ * c = a >? b;
+ * ```
*/
class MaxExpr extends BinaryArithmeticOperation, @maxexpr {
override string getOperator() { result = ">?" }
+
+ override string getCanonicalQLClass() { result = "MaxExpr" }
}
/**
@@ -286,6 +409,10 @@ abstract class PointerArithmeticOperation extends BinaryArithmeticOperation { }
/**
* A C/C++ pointer add expression.
+ * ```
+ * foo *ptr = &f[0];
+ * ptr = ptr + 2;
+ * ```
*/
class PointerAddExpr extends PointerArithmeticOperation, @paddexpr {
override string getOperator() { result = "+" }
@@ -297,6 +424,10 @@ class PointerAddExpr extends PointerArithmeticOperation, @paddexpr {
/**
* A C/C++ pointer subtract expression.
+ * ```
+ * foo *ptr = &f[3];
+ * ptr = ptr - 2;
+ * ```
*/
class PointerSubExpr extends PointerArithmeticOperation, @psubexpr {
override string getOperator() { result = "-" }
@@ -308,6 +439,10 @@ class PointerSubExpr extends PointerArithmeticOperation, @psubexpr {
/**
* A C/C++ pointer difference expression.
+ * ```
+ * foo *start = &f[0], *end = &f[4];
+ * int size = end - size;
+ * ```
*/
class PointerDiffExpr extends PointerArithmeticOperation, @pdiffexpr {
override string getOperator() { result = "-" }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll b/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll
index c0a42d60315..aeabe10f168 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Assignment.qll
@@ -4,11 +4,13 @@ import semmle.code.cpp.exprs.BitwiseOperation
/**
* A non-overloaded binary assignment operation, including `=`, `+=`, `&=`,
- * etc. A C++ overloaded operation looks syntactically identical but is instead
+ * etc. A C++ overloaded assignment operation looks syntactically identical but is instead
* a `FunctionCall`.
+ *
+ * This is an abstract root QL class for all (non-overloaded) assignments.
*/
abstract class Assignment extends Operation {
- /** Gets the lvalue of this assignment. */
+ /** Gets the _lvalue_ of this assignment. */
Expr getLValue() { this.hasChild(result, 0) }
/** Gets the rvalue of this assignment. */
@@ -30,6 +32,9 @@ abstract class Assignment extends Operation {
/**
* A non-overloaded assignment operation with the operator `=`.
+ * ```
+ * a = b;
+ * ```
*/
class AssignExpr extends Assignment, @assignexpr {
override string getOperator() { result = "=" }
@@ -48,13 +53,16 @@ abstract class AssignOperation extends Assignment {
}
/**
- * A non-overloaded arithmetic assignment operation on a non-pointer lvalue:
+ * A non-overloaded arithmetic assignment operation on a non-pointer _lvalue_:
* `+=`, `-=`, `*=`, `/=` and `%=`.
*/
abstract class AssignArithmeticOperation extends AssignOperation { }
/**
- * A non-overloaded `+=` assignment expression on a non-pointer lvalue.
+ * A non-overloaded `+=` assignment expression on a non-pointer _lvalue_.
+ * ```
+ * a += b;
+ * ```
*/
class AssignAddExpr extends AssignArithmeticOperation, @assignaddexpr {
override string getCanonicalQLClass() { result = "AssignAddExpr" }
@@ -63,7 +71,10 @@ class AssignAddExpr extends AssignArithmeticOperation, @assignaddexpr {
}
/**
- * A non-overloaded `-=` assignment expression on a non-pointer lvalue.
+ * A non-overloaded `-=` assignment expression on a non-pointer _lvalue_.
+ * ```
+ * a -= b;
+ * ```
*/
class AssignSubExpr extends AssignArithmeticOperation, @assignsubexpr {
override string getCanonicalQLClass() { result = "AssignSubExpr" }
@@ -73,6 +84,9 @@ class AssignSubExpr extends AssignArithmeticOperation, @assignsubexpr {
/**
* A non-overloaded `*=` assignment expression.
+ * ```
+ * a *= b;
+ * ```
*/
class AssignMulExpr extends AssignArithmeticOperation, @assignmulexpr {
override string getCanonicalQLClass() { result = "AssignMulExpr" }
@@ -82,6 +96,9 @@ class AssignMulExpr extends AssignArithmeticOperation, @assignmulexpr {
/**
* A non-overloaded `/=` assignment expression.
+ * ```
+ * a /= b;
+ * ```
*/
class AssignDivExpr extends AssignArithmeticOperation, @assigndivexpr {
override string getCanonicalQLClass() { result = "AssignDivExpr" }
@@ -91,6 +108,9 @@ class AssignDivExpr extends AssignArithmeticOperation, @assigndivexpr {
/**
* A non-overloaded `%=` assignment expression.
+ * ```
+ * a %= b;
+ * ```
*/
class AssignRemExpr extends AssignArithmeticOperation, @assignremexpr {
override string getCanonicalQLClass() { result = "AssignRemExpr" }
@@ -105,7 +125,10 @@ class AssignRemExpr extends AssignArithmeticOperation, @assignremexpr {
abstract class AssignBitwiseOperation extends AssignOperation { }
/**
- * A non-overloaded `&=` assignment expression.
+ * A non-overloaded AND (`&=`) assignment expression.
+ * ```
+ * a &= b;
+ * ```
*/
class AssignAndExpr extends AssignBitwiseOperation, @assignandexpr {
override string getCanonicalQLClass() { result = "AssignAndExpr" }
@@ -114,7 +137,10 @@ class AssignAndExpr extends AssignBitwiseOperation, @assignandexpr {
}
/**
- * A non-overloaded `|=` assignment expression.
+ * A non-overloaded OR (`|=`) assignment expression.
+ * ```
+ * a |= b;
+ * ```
*/
class AssignOrExpr extends AssignBitwiseOperation, @assignorexpr {
override string getCanonicalQLClass() { result = "AssignOrExpr" }
@@ -123,7 +149,10 @@ class AssignOrExpr extends AssignBitwiseOperation, @assignorexpr {
}
/**
- * A non-overloaded `^=` assignment expression.
+ * A non-overloaded XOR (`^=`) assignment expression.
+ * ```
+ * a ^= b;
+ * ```
*/
class AssignXorExpr extends AssignBitwiseOperation, @assignxorexpr {
override string getCanonicalQLClass() { result = "AssignXorExpr" }
@@ -133,6 +162,9 @@ class AssignXorExpr extends AssignBitwiseOperation, @assignxorexpr {
/**
* A non-overloaded `<<=` assignment expression.
+ * ```
+ * a <<= b;
+ * ```
*/
class AssignLShiftExpr extends AssignBitwiseOperation, @assignlshiftexpr {
override string getCanonicalQLClass() { result = "AssignLShiftExpr" }
@@ -142,6 +174,9 @@ class AssignLShiftExpr extends AssignBitwiseOperation, @assignlshiftexpr {
/**
* A non-overloaded `>>=` assignment expression.
+ * ```
+ * a >>= b;
+ * ```
*/
class AssignRShiftExpr extends AssignBitwiseOperation, @assignrshiftexpr {
override string getCanonicalQLClass() { result = "AssignRShiftExpr" }
@@ -151,6 +186,9 @@ class AssignRShiftExpr extends AssignBitwiseOperation, @assignrshiftexpr {
/**
* A non-overloaded `+=` pointer assignment expression.
+ * ```
+ * ptr += index;
+ * ```
*/
class AssignPointerAddExpr extends AssignOperation, @assignpaddexpr {
override string getCanonicalQLClass() { result = "AssignPointerAddExpr" }
@@ -160,6 +198,9 @@ class AssignPointerAddExpr extends AssignOperation, @assignpaddexpr {
/**
* A non-overloaded `-=` pointer assignment expression.
+ * ```
+ * ptr -= index;
+ * ```
*/
class AssignPointerSubExpr extends AssignOperation, @assignpsubexpr {
override string getCanonicalQLClass() { result = "AssignPointerSubExpr" }
@@ -168,11 +209,16 @@ class AssignPointerSubExpr extends AssignOperation, @assignpsubexpr {
}
/**
- * A C++ variable declaration in an expression where a condition is expected.
- * For example, on the `ConditionDeclExpr` in `if (bool c = x < y)`,
- * `getVariableAccess()` is an access to `c` (with possible casts),
- * `getVariable()` is the variable `c` (which has an initializer `x < y`), and
- * `getInitializingExpr()` is `x < y`.
+ * A C++ variable declaration inside the conditional expression of a `while`, `if` or
+ * `for` compound statement. Declaring a variable this way narrows its lifetime and
+ * scope to be strictly the compound statement itself. For example:
+ * ```
+ * extern int x, y;
+ * if (bool c = x < y) { do_something_with(c); }
+ * // c is no longer in scope
+ * while (int d = x - y) { do_something_else_with(d); }
+ * // d is no longer is scope
+ * ```
*/
class ConditionDeclExpr extends Expr, @condition_decl {
/**
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll
index 8e6c9979b97..e7b0d8530c6 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/BitwiseOperation.qll
@@ -7,6 +7,9 @@ abstract class UnaryBitwiseOperation extends UnaryOperation { }
/**
* A C/C++ complement expression.
+ * ```
+ * unsigned c = ~a;
+ * ```
*/
class ComplementExpr extends UnaryBitwiseOperation, @complementexpr {
override string getOperator() { result = "~" }
@@ -23,6 +26,9 @@ abstract class BinaryBitwiseOperation extends BinaryOperation { }
/**
* A C/C++ left shift expression.
+ * ```
+ * unsigned c = a << b;
+ * ```
*/
class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr {
override string getOperator() { result = "<<" }
@@ -34,6 +40,9 @@ class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr {
/**
* A C/C++ right shift expression.
+ * ```
+ * unsigned c = a >> b;
+ * ```
*/
class RShiftExpr extends BinaryBitwiseOperation, @rshiftexpr {
override string getOperator() { result = ">>" }
@@ -44,7 +53,10 @@ class RShiftExpr extends BinaryBitwiseOperation, @rshiftexpr {
}
/**
- * A C/C++ bitwise and expression.
+ * A C/C++ bitwise AND expression.
+ * ```
+ * unsigned c = a & b;
+ * ```
*/
class BitwiseAndExpr extends BinaryBitwiseOperation, @andexpr {
override string getOperator() { result = "&" }
@@ -55,7 +67,10 @@ class BitwiseAndExpr extends BinaryBitwiseOperation, @andexpr {
}
/**
- * A C/C++ bitwise or expression.
+ * A C/C++ bitwise OR expression.
+ * ```
+ * unsigned c = a | b;
+ * ```
*/
class BitwiseOrExpr extends BinaryBitwiseOperation, @orexpr {
override string getOperator() { result = "|" }
@@ -66,7 +81,10 @@ class BitwiseOrExpr extends BinaryBitwiseOperation, @orexpr {
}
/**
- * A C/C++ bitwise xor expression.
+ * A C/C++ bitwise XOR expression.
+ * ```
+ * unsigned c = a ^ b;
+ * ```
*/
class BitwiseXorExpr extends BinaryBitwiseOperation, @xorexpr {
override string getOperator() { result = "^" }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll
index b87f15e6454..4f057dceae6 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/BuiltInOperations.qll
@@ -1,14 +1,20 @@
import semmle.code.cpp.exprs.Expr
/**
- * A C/C++ builtin operation.
+ * A C/C++ builtin operation. This is the root QL class encompassing
+ * built-in functionality.
*/
abstract class BuiltInOperation extends Expr {
override string getCanonicalQLClass() { result = "BuiltInOperation" }
}
/**
- * A C/C++ `__builtin_va_start` expression (used by some implementations of `va_start`).
+ * A C/C++ `__builtin_va_start` built-in operation (used by some
+ * implementations of `va_start`).
+ * ```
+ * __builtin_va_list ap;
+ * __builtin_va_start(ap, last_named_param);
+ * ```
*/
class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr {
override string toString() { result = "__builtin_va_start" }
@@ -17,7 +23,13 @@ class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr {
}
/**
- * A C/C++ `__builtin_va_end` expression (used by some implementations of `va_end`).
+ * A C/C++ `__builtin_va_end` built-in operation (used by some implementations
+ * of `va_end`).
+ * ```
+ * __builtin_va_start(ap, last_named_param);
+ * ap = __builtin_va_arg(ap, long);
+ * __builtin_va_end(ap);
+ * ```
*/
class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr {
override string toString() { result = "__builtin_va_end" }
@@ -26,7 +38,11 @@ class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr {
}
/**
- * A C/C++ `__builtin_va_arg` expression (used by some implementations of `va_arg`).
+ * A C/C++ `__builtin_va_arg` built-in operation (used by some implementations
+ * of `va_arg`).
+ * ```
+ * ap = __builtin_va_arg(ap, long);
+ * ```
*/
class BuiltInVarArg extends BuiltInOperation, @vaargexpr {
override string toString() { result = "__builtin_va_arg" }
@@ -35,7 +51,13 @@ class BuiltInVarArg extends BuiltInOperation, @vaargexpr {
}
/**
- * A C/C++ `__builtin_va_copy` expression (used by some implementations of `va_copy`).
+ * A C/C++ `__builtin_va_copy` built-in operation (used by some implementations
+ * of `va_copy`).
+ * ```
+ * va_list ap, aq;
+ * __builtin_va_start(ap, last_named_param);
+ * va_copy(aq, ap);
+ * ```
*/
class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr {
override string toString() { result = "__builtin_va_copy" }
@@ -45,6 +67,9 @@ class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr {
/**
* A Microsoft C/C++ `__noop` expression, which does nothing.
+ * ```
+ * __noop;
+ * ```
*/
class BuiltInNoOp extends BuiltInOperation, @noopexpr {
override string toString() { result = "__noop" }
@@ -53,16 +78,37 @@ class BuiltInNoOp extends BuiltInOperation, @noopexpr {
}
/**
- * A C++ `__offsetof` expression (used by some implementations of offsetof in the presence of user-defined `operator&`).
+ * DEPRECATED: Use `BuiltInOperationBuiltInOffsetOf` instead.
*/
-class BuiltInOperationOffsetOf extends BuiltInOperation, @offsetofexpr {
- override string toString() { result = "__offsetof" }
+deprecated class BuiltInOperationOffsetOf = BuiltInOperationBuiltInOffsetOf;
- override string getCanonicalQLClass() { result = "BuiltInOperationOffsetOf" }
+/**
+ * A C/C++ `__builtin_offsetof` built-in operation (used by some implementations
+ * of `offsetof`). The operation retains its semantics even in the presence
+ * of an overloaded `operator &`). This is a GNU/Clang extension.
+ * ```
+ * struct S {
+ * int a, b;
+ * };
+ * int d = __builtin_offsetof(struct S, b); // usually 4
+ * ```
+ */
+class BuiltInOperationBuiltInOffsetOf extends BuiltInOperation, @offsetofexpr {
+ override string toString() { result = "__builtin_offsetof" }
+
+ override string getCanonicalQLClass() { result = "BuiltInOperationBuiltInOffsetOf" }
}
/**
- * A C/C++ `__INTADDR__` expression, used by EDG to implement `offsetof` in the presence of user-defined `operator&`.
+ * A C/C++ `__INTADDR__` built-in operation (used by some implementations
+ * of `offsetof`). The operation retains its semantics even in the presence
+ * of an overloaded `operator &`). This is an EDG extension.
+ * ```
+ * struct S {
+ * int a, b;
+ * };
+ * int d = __INTADDR__(struct S, b); // usually 4
+ * ```
*/
class BuiltInIntAddr extends BuiltInOperation, @intaddrexpr {
override string toString() { result = "__INTADDR__" }
@@ -71,7 +117,13 @@ class BuiltInIntAddr extends BuiltInOperation, @intaddrexpr {
}
/**
- * A C++ `__has_assign` expression (used by some implementations of the type_traits header).
+ * A C++ `__has_assign` built-in operation (used by some implementations of
+ * the `` header).
+ *
+ * Returns `true` if the type has a copy assignment operator.
+ * ```
+ * bool v = __has_assign(MyType);
+ * ```
*/
class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr {
override string toString() { result = "__has_assign" }
@@ -80,7 +132,13 @@ class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr {
}
/**
- * A C++ `__has_copy` expression (used by some implementations of the type_traits header).
+ * A C++ `__has_copy` built-in operation (used by some implementations of the
+ * `` header).
+ *
+ * Returns `true` if the type has a copy constructor.
+ * ```
+ * std::integral_constant< bool, __has_copy(_Tp)> hc;
+ * ```
*/
class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr {
override string toString() { result = "__has_copy" }
@@ -89,7 +147,14 @@ class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr {
}
/**
- * A C++ `__has_nothrow_assign` expression (used by some implementations of the type_traits header).
+ * A C++ `__has_nothrow_assign` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if a copy assignment operator has an empty exception
+ * specification.
+ * ```
+ * std::integral_constant< bool, __has_nothrow_assign(_Tp)> hnta;
+ * ```
*/
class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassign {
override string toString() { result = "__has_nothrow_assign" }
@@ -98,7 +163,14 @@ class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassi
}
/**
- * A C++ `__has_nothrow_constructor` expression (used by some implementations of the type_traits header).
+ * A C++ `__has_nothrow_constructor` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the default constructor has an empty exception
+ * specification.
+ * ```
+ * bool v = __has_nothrow_constructor(MyType);
+ * ```
*/
class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothrowconstr {
override string toString() { result = "__has_nothrow_constructor" }
@@ -107,7 +179,13 @@ class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothro
}
/**
- * A C++ `__has_nothrow_copy` expression (used by some implementations of the type_traits header).
+ * A C++ `__has_nothrow_copy` built-in operation (used by some implementations
+ * of the `` header).
+ *
+ * Returns `true` if the copy constructor has an empty exception specification.
+ * ```
+ * std::integral_constant< bool, __has_nothrow_copy(MyType) >;
+ * ```
*/
class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy {
override string toString() { result = "__has_nothrow_copy" }
@@ -116,7 +194,14 @@ class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy {
}
/**
- * A C++ `__has_trivial_assign` expression (used by some implementations of the type_traits header).
+ * A C++ `__has_trivial_assign` built-in operation (used by some implementations
+ * of the `` header).
+ *
+ * Returns `true` if the type has a trivial assignment
+ * operator (`operator =`).
+ * ```
+ * bool v = __has_trivial_assign(MyType);
+ * ```
*/
class BuiltInOperationHasTrivialAssign extends BuiltInOperation, @hastrivialassign {
override string toString() { result = "__has_trivial_assign" }
@@ -125,7 +210,13 @@ class BuiltInOperationHasTrivialAssign extends BuiltInOperation, @hastrivialassi
}
/**
- * A C++ `__has_trivial_constructor` expression (used by some implementations of the type_traits header).
+ * A C++ `__has_trivial_constructor` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the type has a trivial constructor.
+ * ```
+ * bool v = __has_trivial_constructor(MyType);
+ * ```
*/
class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivialconstr {
override string toString() { result = "__has_trivial_constructor" }
@@ -134,7 +225,13 @@ class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivia
}
/**
- * A C++ `__has_trivial_copy` expression (used by some implementations of the type_traits header).
+ * A C++ `__has_trivial_copy` built-in operation (used by some implementations
+ * of the `` header).
+ *
+ * Returns true if the type has a trivial copy constructor.
+ * ```
+ * std::integral_constant< bool, __has_trivial_copy(MyType) > htc;
+ * ```
*/
class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy {
override string toString() { result = "__has_trivial_copy" }
@@ -143,7 +240,13 @@ class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy {
}
/**
- * A C++ `__has_trivial_destructor` expression (used by some implementations of the type_traits header).
+ * A C++ `__has_trivial_destructor` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the type has a trivial destructor.
+ * ```
+ * bool v = __has_trivial_destructor(MyType);
+ * ```
*/
class BuiltInOperationHasTrivialDestructor extends BuiltInOperation, @hastrivialdestructor {
override string toString() { result = "__has_trivial_destructor" }
@@ -152,7 +255,13 @@ class BuiltInOperationHasTrivialDestructor extends BuiltInOperation, @hastrivial
}
/**
- * A C++ `__has_user_destructor` expression (used by some implementations of the type_traits header).
+ * A C++ `__has_user_destructor` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns true if the type has a user-declared destructor.
+ * ```
+ * bool v = __has_user_destructor(MyType);
+ * ```
*/
class BuiltInOperationHasUserDestructor extends BuiltInOperation, @hasuserdestr {
override string toString() { result = "__has_user_destructor" }
@@ -161,7 +270,16 @@ class BuiltInOperationHasUserDestructor extends BuiltInOperation, @hasuserdestr
}
/**
- * A C++ `__has_virtual_destructor` expression (used by some implementations of the type_traits header).
+ * A C++ `__has_virtual_destructor` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the type has a virtual destructor.
+ * ```
+ * template
+ * struct has_virtual_destructor
+ * : public integral_constant
+ * { };
+ * ```
*/
class BuiltInOperationHasVirtualDestructor extends BuiltInOperation, @hasvirtualdestr {
override string toString() { result = "__has_virtual_destructor" }
@@ -170,7 +288,13 @@ class BuiltInOperationHasVirtualDestructor extends BuiltInOperation, @hasvirtual
}
/**
- * A C++ `__is_abstract` expression (used by some implementations of the type_traits header).
+ * A C++ `__is_abstract` built-in operation (used by some implementations of the
+ * `` header).
+ *
+ * Returns `true` if the class has at least one pure virtual function.
+ * ```
+ * bool v = __is_abstract(MyType);
+ * ```
*/
class BuiltInOperationIsAbstract extends BuiltInOperation, @isabstractexpr {
override string toString() { result = "__is_abstract" }
@@ -179,7 +303,13 @@ class BuiltInOperationIsAbstract extends BuiltInOperation, @isabstractexpr {
}
/**
- * A C++ `__is_base_of` expression (used by some implementations of the type_traits header).
+ * A C++ `__is_base_of` built-in operation (used by some implementations of the
+ * `` header).
+ *
+ * Returns `true` if the first type is a base class of the second type, of if both types are the same.
+ * ```
+ * bool v = __is_base_of(MyType, OtherType);
+ * ```
*/
class BuiltInOperationIsBaseOf extends BuiltInOperation, @isbaseofexpr {
override string toString() { result = "__is_base_of" }
@@ -188,7 +318,13 @@ class BuiltInOperationIsBaseOf extends BuiltInOperation, @isbaseofexpr {
}
/**
- * A C++ `__is_class` expression (used by some implementations of the type_traits header).
+ * A C++ `__is_class` built-in operation (used by some implementations of the
+ * `` header).
+ *
+ * Returns `true` if the type is a `class` or a `struct`.
+ * ```
+ * bool v = __is_class(MyType);
+ * ```
*/
class BuiltInOperationIsClass extends BuiltInOperation, @isclassexpr {
override string toString() { result = "__is_class" }
@@ -197,7 +333,13 @@ class BuiltInOperationIsClass extends BuiltInOperation, @isclassexpr {
}
/**
- * A C++ `__is_convertible_to` expression (used by some implementations of the type_traits header).
+ * A C++ `__is_convertible_to` built-in operation (used by some implementations
+ * of the `` header).
+ *
+ * Returns `true` if the first type can be converted to the second type.
+ * ```
+ * bool v = __is_convertible_to(MyType, OtherType);
+ * ```
*/
class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr {
override string toString() { result = "__is_convertible_to" }
@@ -206,7 +348,13 @@ class BuiltInOperationIsConvertibleTo extends BuiltInOperation, @isconvtoexpr {
}
/**
- * A C++ `__is_empty` expression (used by some implementations of the type_traits header).
+ * A C++ `__is_empty` built-in operation (used by some implementations of the
+ * `` header).
+ *
+ * Returns `true` if the type has no instance data members.
+ * ```
+ * bool v = __is_empty(MyType);
+ * ```
*/
class BuiltInOperationIsEmpty extends BuiltInOperation, @isemptyexpr {
override string toString() { result = "__is_empty" }
@@ -215,7 +363,13 @@ class BuiltInOperationIsEmpty extends BuiltInOperation, @isemptyexpr {
}
/**
- * A C++ `__is_enum` expression (used by some implementations of the type_traits header).
+ * A C++ `__is_enum` built-in operation (used by some implementations of the
+ * `` header).
+ *
+ * Returns true if the type is an `enum`.
+ * ```
+ * bool v = __is_enum(MyType);
+ * ```
*/
class BuiltInOperationIsEnum extends BuiltInOperation, @isenumexpr {
override string toString() { result = "__is_enum" }
@@ -224,7 +378,15 @@ class BuiltInOperationIsEnum extends BuiltInOperation, @isenumexpr {
}
/**
- * A C++ `__is_pod` expression (used by some implementations of the type_traits header).
+ * A C++ `__is_pod` built-in operation (used by some implementations of the
+ * `` header).
+ *
+ * Returns `true` if the type is a `class`, `struct` or `union`, WITHOUT
+ * (1) constructors, (2) private or protected non-static members, (3) base
+ * classes, or (4) virtual functions.
+ * ```
+ * bool v = __is_pod(MyType);
+ * ```
*/
class BuiltInOperationIsPod extends BuiltInOperation, @ispodexpr {
override string toString() { result = "__is_pod" }
@@ -233,7 +395,13 @@ class BuiltInOperationIsPod extends BuiltInOperation, @ispodexpr {
}
/**
- * A C++ `__is_polymorphic` expression (used by some implementations of the type_traits header).
+ * A C++ `__is_polymorphic` built-in operation (used by some implementations
+ * of the `` header).
+ *
+ * Returns `true` if the type has at least one virtual function.
+ * ```
+ * bool v = __is_polymorphic(MyType);
+ * ```
*/
class BuiltInOperationIsPolymorphic extends BuiltInOperation, @ispolyexpr {
override string toString() { result = "__is_polymorphic" }
@@ -242,7 +410,13 @@ class BuiltInOperationIsPolymorphic extends BuiltInOperation, @ispolyexpr {
}
/**
- * A C++ `__is_union` expression (used by some implementations of the type_traits header).
+ * A C++ `__is_union` built-in operation (used by some implementations of the
+ * `` header).
+ *
+ * Returns `true` if the type is a `union`.
+ * ```
+ * bool v = __is_union(MyType);
+ * ```
*/
class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr {
override string toString() { result = "__is_union" }
@@ -256,7 +430,16 @@ class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr {
deprecated class BuiltInOperationBuiltInTypes = BuiltInOperationBuiltInTypesCompatibleP;
/**
- * A C++ `__builtin_types_compatible_p` expression (used by some implementations of the type_traits header).
+ * A C++ `__builtin_types_compatible_p` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the two types are the same (modulo qualifiers).
+ * ```
+ * template
+ * struct types_compatible
+ * : public integral_constant
+ * { };
+ * ```
*/
class BuiltInOperationBuiltInTypesCompatibleP extends BuiltInOperation, @typescompexpr {
override string toString() { result = "__builtin_types_compatible_p" }
@@ -264,6 +447,15 @@ class BuiltInOperationBuiltInTypesCompatibleP extends BuiltInOperation, @typesco
/**
* A clang `__builtin_shufflevector` expression.
+ *
+ * It outputs a permutation of elements from one or two input vectors.
+ * Please see
+ * https://releases.llvm.org/3.7.0/tools/clang/docs/LanguageExtensions.html#langext-builtin-shufflevector
+ * for more information.
+ * ```
+ * // Concatenate every other element of 4-element vectors V1 and V2.
+ * V3 = __builtin_shufflevector(V1, V2, 0, 2, 4, 6);
+ * ```
*/
class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshufflevector {
override string toString() { result = "__builtin_shufflevector" }
@@ -273,6 +465,17 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu
/**
* A clang `__builtin_convertvector` expression.
+ *
+ * Allows for conversion of vectors of equal element count and compatible
+ * element types. Please see
+ * https://releases.llvm.org/3.7.0/tools/clang/docs/LanguageExtensions.html#builtin-convertvector
+ * for more information.
+ * ```
+ * float vf __attribute__((__vector_size__(16)));
+ * typedef double vector4double __attribute__((__vector_size__(32)));
+ * // convert from a vector of 4 floats to a vector of 4 doubles.
+ * vector4double vd = __builtin_convertvector(vf, vector4double);
+ * ```
*/
class BuiltInOperationBuiltInConvertVector extends BuiltInOperation, @builtinconvertvector {
override string toString() { result = "__builtin_convertvector" }
@@ -281,7 +484,14 @@ class BuiltInOperationBuiltInConvertVector extends BuiltInOperation, @builtincon
}
/**
- * A clang `__builtin_addressof` expression (can be used to implement C++'s std::addressof).
+ * A clang `__builtin_addressof` function (can be used to implement C++'s
+ * `std::addressof`).
+ *
+ * This function disregards any overloads created for `operator &`.
+ * ```
+ * int a = 1;
+ * int *b = __builtin_addressof(a);
+ * ```
*/
class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation, @builtinaddressof {
/** Gets the function or variable whose address is taken. */
@@ -298,7 +508,17 @@ class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation,
}
/**
- * The `__is_trivially_constructible` type trait.
+ * The `__is_trivially_constructible` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the type has a trivial default
+ * constructor, copy constructor or move constructor.
+ * ```
+ * template
+ * struct is_trivially_constructible
+ * : public integral_constant
+ * { };
+ * ```
*/
class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation,
@istriviallyconstructibleexpr {
@@ -308,7 +528,15 @@ class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation,
}
/**
- * The `__is_destructible` type trait.
+ * The `__is_destructible` built-in operation (used by some implementations
+ * of the `` header).
+ *
+ * Returns `true` if the type's destructor is not `delete`d and is accessible
+ * in derived `class`es, and whose base `class` and all non-static data members
+ * are also destructible.
+ * ```
+ * bool v = __is_destructible(MyType);
+ * ```
*/
class BuiltInOperationIsDestructible extends BuiltInOperation, @isdestructibleexpr {
override string toString() { result = "__is_destructible" }
@@ -317,7 +545,15 @@ class BuiltInOperationIsDestructible extends BuiltInOperation, @isdestructibleex
}
/**
- * The `__is_nothrow_destructible` type trait.
+ * The `__is_nothrow_destructible` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the type is destructible and whose destructor, and those
+ * of member data and any super`class`es all have an empty exception
+ * specification.
+ * ```
+ * bool v = __is_nothrow_destructible(MyType);
+ * ```
*/
class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrowdestructibleexpr {
override string toString() { result = "__is_nothrow_destructible" }
@@ -326,7 +562,14 @@ class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrow
}
/**
- * The `__is_trivially_destructible` type trait.
+ * The `__is_trivially_destructible` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the type is destructible and whose destructor, and those
+ * of member data and any superclasses are all trivial.
+ * ```
+ * bool v = __is_trivially_destructible(MyType);
+ * ```
*/
class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istriviallydestructibleexpr {
override string toString() { result = "__is_trivially_destructible" }
@@ -335,7 +578,17 @@ class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istrivi
}
/**
- * The `__is_trivially_assignable` type trait.
+ * The `__is_trivially_assignable` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the assignment operator `C::operator =(const C& c)` is
+ * trivial.
+ * ```
+ * template
+ * struct is_trivially_assignable
+ * : public integral_constant
+ * { };
+ * ```
*/
class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istriviallyassignableexpr {
override string toString() { result = "__is_trivially_assignable" }
@@ -344,7 +597,14 @@ class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istrivial
}
/**
- * The `__is_nothrow_assignable` type trait.
+ * The `__is_nothrow_assignable` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns true if there exists a `C::operator =(const C& c) nothrow`
+ * assignment operator (i.e, with an empty exception specification).
+ * ```
+ * bool v = __is_nothrow_assignable(MyType);
+ * ```
*/
class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowassignableexpr {
override string toString() { result = "__is_nothrow_assignable" }
@@ -353,7 +613,18 @@ class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowas
}
/**
- * The `__is_standard_layout` type trait.
+ * The `__is_standard_layout` built-in operation (used by some implementations
+ * of the `` header).
+ *
+ * Returns `true` if the type is a primitive type, or a `class`, `struct` or
+ * `union` WITHOUT (1) virtual functions or base classes, (2) reference member
+ * variable or (3) multiple occurrences of base `class` objects, among other
+ * restrictions. Please see
+ * https://en.cppreference.com/w/cpp/named_req/StandardLayoutType
+ * for more information.
+ * ```
+ * bool v = __is_standard_layout(MyType);
+ * ```
*/
class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayoutexpr {
override string toString() { result = "__is_standard_layout" }
@@ -362,7 +633,12 @@ class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayo
}
/**
- * The `__is_trivially_copyable` type trait.
+ * The `__is_trivially_copyable` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if instances of this type can be copied by trivial
+ * means. The copying is done in a manner similar to the `memcpy`
+ * function.
*/
class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istriviallycopyableexpr {
override string toString() { result = "__is_trivially_copyable" }
@@ -371,7 +647,18 @@ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istrivially
}
/**
- * The `__is_literal_type` type trait.
+ * The `__is_literal_type` built-in operation (used by some implementations of
+ * the `` header).
+ *
+ * Returns `true` if the type is a scalar type, a reference type or an array of
+ * literal types, among others. Please see
+ * https://en.cppreference.com/w/cpp/named_req/LiteralType
+ * for more information.
+ *
+ * ```
+ * template
+ * std::integral_constant< bool, __is_literal_type(_Tp)> ilt;
+ * ```
*/
class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr {
override string toString() { result = "__is_literal_type" }
@@ -380,7 +667,15 @@ class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr
}
/**
- * The `__has_trivial_move_constructor` type trait.
+ * The `__has_trivial_move_constructor` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns true if the move (`&&`) constructor can be generated by the
+ * compiler, with semantics of the `memcpy` operation.
+ * ```
+ * template
+ * std::integral_constant< bool, __has_trivial_move_constructor(_Tp)> htmc;
+ * ```
*/
class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation,
@hastrivialmoveconstructorexpr {
@@ -390,7 +685,16 @@ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation,
}
/**
- * The `__has_trivial_move_assign` type trait.
+ * The `__has_trivial_move_assign` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns if the move-assign operator `C::operator =(C &&c)` is trivial.
+ * ```
+ * template
+ * struct has_trivial_move_assign
+ * : public integral_constant
+ * { };
+ * ```
*/
class BuiltInOperationHasTrivialMoveAssign extends BuiltInOperation, @hastrivialmoveassignexpr {
override string toString() { result = "__has_trivial_move_assign" }
@@ -399,7 +703,14 @@ class BuiltInOperationHasTrivialMoveAssign extends BuiltInOperation, @hastrivial
}
/**
- * The `__has_nothrow_move_assign` type trait.
+ * The `__has_nothrow_move_assign` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the type has a `C::operator=(C&& c) nothrow`, that is,
+ * an assignment operator with an empty exception specification.
+ * ```
+ * bool v = __has_nothrow_move_assign(MyType);
+ * ```
*/
class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrowmoveassignexpr {
override string toString() { result = "__has_nothrow_move_assign" }
@@ -408,7 +719,17 @@ class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrow
}
/**
- * The `__is_constructible` type trait.
+ * The `__is_constructible` built-in operation (used by some implementations
+ * of the `` header).
+ *
+ * Returns `true` if the type can be constructed using specified arguments
+ * (or none).
+ * ```
+ * template
+ * struct is_constructible
+ * : public integral_constant
+ * { };
+ * ```
*/
class BuiltInOperationIsConstructible extends BuiltInOperation, @isconstructibleexpr {
override string toString() { result = "__is_constructible" }
@@ -417,7 +738,14 @@ class BuiltInOperationIsConstructible extends BuiltInOperation, @isconstructible
}
/**
- * The `__is_nothrow_constructible` type trait.
+ * The `__is_nothrow_constructible` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the type is constructable and all its constructors have an
+ * empty exception specification (i.e., are declared with `nothrow`);
+ * ```
+ * bool v = __is_nothrow_constructible(MyType);
+ * ```
*/
class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothrowconstructibleexpr {
override string toString() { result = "__is_nothrow_constructible" }
@@ -426,7 +754,13 @@ class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothro
}
/**
- * The `__has_finalizer` type trait.
+ * The `__has_finalizer` built-in operation. This is a Microsoft extension.
+ *
+ * Returns `true` if the type defines a _finalizer_ `C::!C(void)`, to be called
+ * from either the regular destructor or the garbage collector.
+ * ```
+ * bool v = __has_finalizer(MyType);
+ * ```
*/
class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr {
override string toString() { result = "__has_finalizer" }
@@ -435,7 +769,12 @@ class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr {
}
/**
- * The `__is_delegate` type trait.
+ * The `__is_delegate` built-in operation. This is a Microsoft extension.
+ *
+ * Returns `true` if the function has been declared as a `delegate`, used in
+ * message forwarding. Please see
+ * https://docs.microsoft.com/en-us/cpp/extensions/delegate-cpp-component-extensions
+ * for more information.
*/
class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr {
override string toString() { result = "__is_delegate" }
@@ -444,7 +783,11 @@ class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr {
}
/**
- * The `__is_interface_class` type trait.
+ * The `__is_interface_class` built-in operation. This is a Microsoft extension.
+ *
+ * Returns `true` if the type has been declared as an `interface`. Please see
+ * https://docs.microsoft.com/en-us/cpp/extensions/interface-class-cpp-component-extensions
+ * for more information.
*/
class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfaceclassexpr {
override string toString() { result = "__is_interface_class" }
@@ -453,7 +796,15 @@ class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfacecla
}
/**
- * The `__is_ref_array` type trait.
+ * The `__is_ref_array` built-in operation. This is a Microsoft extension.
+ *
+ * Returns `true` if the object passed in is a _platform array_. Please see
+ * https://docs.microsoft.com/en-us/cpp/extensions/arrays-cpp-component-extensions
+ * for more information.
+ * ```
+ * array^ x = gcnew array(10);
+ * bool b = __is_ref_array(array);
+ * ```
*/
class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr {
override string toString() { result = "__is_ref_array" }
@@ -462,7 +813,15 @@ class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr {
}
/**
- * The `__is_ref_class` type trait.
+ * The `__is_ref_class` built-in operation. This is a Microsoft extension.
+ *
+ * Returns `true` if the type is a _reference class_. Please see
+ * https://docs.microsoft.com/en-us/cpp/extensions/classes-and-structs-cpp-component-extensions
+ * for more information.
+ * ```
+ * ref class R {};
+ * bool b = __is_ref_class(R);
+ * ```
*/
class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr {
override string toString() { result = "__is_ref_class" }
@@ -471,7 +830,16 @@ class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr {
}
/**
- * The `__is_sealed` type trait.
+ * The `__is_sealed` built-in operation. This is a Microsoft extension.
+ *
+ * Returns `true` if a given class or virtual function is marked as `sealed`,
+ * meaning that it cannot be extended or overridden. The `sealed` keyword
+ * is similar to the C++11 `final` keyword.
+ * ```
+ * ref class X sealed {
+ * virtual void f() sealed { }
+ * };
+ * ```
*/
class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr {
override string toString() { result = "__is_sealed" }
@@ -480,7 +848,17 @@ class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr {
}
/**
- * The `__is_simple_value_class` type trait.
+ * The `__is_simple_value_class` built-in operation. This is a Microsoft extension.
+ *
+ * Returns `true` if passed a value type that contains no references to the
+ * garbage-collected heap.
+ * ```
+ * ref class R {}; // __is_simple_value_class(R) == false
+ * value struct V {}; // __is_simple_value_class(V) == true
+ * value struct V2 { // __is_simple_value_class(V2) == false
+ * R ^ r; // not a simple value type
+ * };
+ * ```
*/
class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalueclassexpr {
override string toString() { result = "__is_simple_value_class" }
@@ -489,7 +867,15 @@ class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalu
}
/**
- * The `__is_value_class` type trait.
+ * The `__is_value_class` built-in operation. This is a Microsoft extension.
+ *
+ * Returns `true` if passed a value type. Please see
+ * https://docs.microsoft.com/en-us/cpp/extensions/classes-and-structs-cpp-component-extensions
+ * For more information.
+ * ```
+ * value struct V {};
+ * bool v = __is_value_class(V);
+ * ```
*/
class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr {
override string toString() { result = "__is_value_class" }
@@ -498,7 +884,16 @@ class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr {
}
/**
- * The `__is_final` type trait.
+ * The `__is_final` built-in operation (used by some implementations of the
+ * `` header).
+ *
+ * Returns `true` if the `class` has been marked with the `final` specifier.
+ * ```
+ * template
+ * struct is_final
+ * : public integral_constant
+ * { };
+ * ```
*/
class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr {
override string toString() { result = "__is_final" }
@@ -507,15 +902,48 @@ class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr {
}
/**
- * The `__builtin_choose_expr` type trait.
+ * The `__builtin_choose_expr` expression. This is a GNU/Clang extension.
+ *
+ * The expression functions similarly to the ternary `?:` operator, except
+ * that it is evaluated at compile-time.
+ * ```
+ * int sz = __builtin_choose_expr(__builtin_types_compatible_p(int, long), 4, 8);
+ * ```
*/
class BuiltInChooseExpr extends BuiltInOperation, @builtinchooseexpr {
override string toString() { result = "__builtin_choose_expr" }
+
+ override string getCanonicalQLClass() { result = "BuiltInChooseExpr" }
}
/**
- * Fill operation on a GNU vector.
+ * Fill operation on a vector. This is a GNU extension.
+ *
+ * A single scalar value is used to populate all the elements in a vector.
+ * In the example below, the scalar value is `25`:
+ * ```
+ * typedef int v16i __attribute__((vector_size(16)));
+ * v16i src, dst;
+ * dst = src << 25;
+ * ```
*/
class VectorFillOperation extends UnaryOperation, @vec_fill {
override string getOperator() { result = "(vector fill)" }
+
+ override string getCanonicalQLClass() { result = "VectorFillOperation" }
+}
+
+/**
+ * The GNU `__builtin_complex` operation.
+ */
+class BuiltInComplexOperation extends BuiltInOperation, @builtincomplex {
+ override string toString() { result = "__builtin_complex" }
+
+ override string getCanonicalQLClass() { result = "BuiltInComplexOperation" }
+
+ /** Gets the operand corresponding to the real part of the complex number. */
+ Expr getRealOperand() { this.hasChild(result, 0) }
+
+ /** Gets the operand corresponding to the imaginary part of the complex number. */
+ Expr getImaginaryOperand() { this.hasChild(result, 1) }
}
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll
index 882918316f6..ec7244c88bc 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Call.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Call.qll
@@ -4,6 +4,8 @@ private import semmle.code.cpp.dataflow.EscapesTree
/**
* A C/C++ call.
+ *
+ * This is the abstract root QL class for all types of calls.
*/
abstract class Call extends Expr, NameQualifiableElement {
/**
@@ -139,17 +141,29 @@ class FunctionCall extends Call, @funbindexpr {
override string getCanonicalQLClass() { result = "FunctionCall" }
/** Gets an explicit template argument for this call. */
- Type getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
+ Locatable getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
+
+ /** Gets an explicit template argument value for this call. */
+ Locatable getAnExplicitTemplateArgumentKind() { result = getExplicitTemplateArgumentKind(_) }
/** Gets a template argument for this call. */
- Type getATemplateArgument() { result = getTarget().getATemplateArgument() }
+ Locatable getATemplateArgument() { result = getTarget().getATemplateArgument() }
+
+ /** Gets a template argument value for this call. */
+ Locatable getATemplateArgumentKind() { result = getTarget().getATemplateArgumentKind() }
/** Gets the nth explicit template argument for this call. */
- Type getExplicitTemplateArgument(int n) {
+ Locatable getExplicitTemplateArgument(int n) {
n < getNumberOfExplicitTemplateArguments() and
result = getTemplateArgument(n)
}
+ /** Gets the nth explicit template argument value for this call. */
+ Locatable getExplicitTemplateArgumentKind(int n) {
+ n < getNumberOfExplicitTemplateArguments() and
+ result = getTemplateArgumentKind(n)
+ }
+
/** Gets the number of explicit template arguments for this call. */
int getNumberOfExplicitTemplateArguments() {
if numtemplatearguments(underlyingElement(this), _)
@@ -161,7 +175,10 @@ class FunctionCall extends Call, @funbindexpr {
int getNumberOfTemplateArguments() { result = count(int i | exists(getTemplateArgument(i))) }
/** Gets the nth template argument for this call (indexed from 0). */
- Type getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) }
+ Locatable getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) }
+
+ /** Gets the nth template argument value for this call (indexed from 0). */
+ Locatable getTemplateArgumentKind(int n) { result = getTarget().getTemplateArgumentKind(n) }
/** Holds if any template arguments for this call are implicit / deduced. */
predicate hasImplicitTemplateArguments() {
@@ -213,7 +230,7 @@ class FunctionCall extends Call, @funbindexpr {
* Gets the function called by this call.
*
* In the case of virtual function calls, the result is the most-specific function in the override tree (as
- * determined by the compiler) such that the target at runtime will be one of result.getAnOverridingFunction*().
+ * determined by the compiler) such that the target at runtime will be one of `result.getAnOverridingFunction*()`.
*/
override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) }
@@ -258,7 +275,11 @@ class FunctionCall extends Call, @funbindexpr {
}
/**
- * An instance of unary operator * applied to a user-defined type.
+ * An instance of a _user-defined_ unary `operator*` applied to its argument.
+ * ```
+ * T1 operator*(const T2 &);
+ * T1 a; T2 b;
+ * a = *b;
*/
class OverloadedPointerDereferenceExpr extends FunctionCall {
OverloadedPointerDereferenceExpr() {
@@ -266,6 +287,8 @@ class OverloadedPointerDereferenceExpr extends FunctionCall {
getTarget().getEffectiveNumberOfParameters() = 1
}
+ override string getCanonicalQLClass() { result = "OverloadedPointerDereferenceExpr" }
+
/**
* Gets the expression this operator * applies to.
*/
@@ -302,11 +325,18 @@ class OverloadedPointerDereferenceExpr extends FunctionCall {
}
/**
- * An instance of operator [] applied to a user-defined type.
+ * An instance of a _user-defined_ binary `operator[]` applied to its arguments.
+ * ```
+ * struct T2 { T1 operator[](const T3 &); };
+ * T1 a; T2 b; T3 c;
+ * a = b[c];
+ * ```
*/
class OverloadedArrayExpr extends FunctionCall {
OverloadedArrayExpr() { getTarget().hasName("operator[]") }
+ override string getCanonicalQLClass() { result = "OverloadedArrayExpr" }
+
/**
* Gets the expression being subscripted.
*/
@@ -324,6 +354,12 @@ class OverloadedArrayExpr extends FunctionCall {
/**
* A C/C++ call which is performed through a function pointer.
+ *
+ * In the call below, `(*funcptr)` may be simplified to just `funcptr`.
+ * ```
+ * extern int (*funcptr)(int a, int b);
+ * int c = (*funcptr)(1, 2);
+ * ```
*/
class ExprCall extends Call, @callexpr {
/**
@@ -346,6 +382,11 @@ class ExprCall extends Call, @callexpr {
/**
* A C/C++ call which is performed through a variable of function pointer type.
+ * ```
+ * int call_via_ptr(int (*pfn)(int)) {
+ * return pfn(5);
+ * }
+ * ```
*/
class VariableCall extends ExprCall {
VariableCall() { this.getExpr() instanceof VariableAccess }
@@ -360,6 +401,10 @@ class VariableCall extends ExprCall {
/**
* A call to a constructor.
+ * ```
+ * struct S { S(void) {} };
+ * S s;
+ * ```
*/
class ConstructorCall extends FunctionCall {
ConstructorCall() { super.getTarget() instanceof Constructor }
@@ -372,6 +417,9 @@ class ConstructorCall extends FunctionCall {
/**
* A C++ `throw` expression.
+ * ```
+ * throw Exc(2);
+ * ```
*/
class ThrowExpr extends Expr, @throw_expr {
/**
@@ -389,6 +437,9 @@ class ThrowExpr extends Expr, @throw_expr {
/**
* A C++ `throw` expression with no argument (which causes the current exception to be re-thrown).
+ * ```
+ * throw;
+ * ```
*/
class ReThrowExpr extends ThrowExpr {
ReThrowExpr() { this.getType() instanceof VoidType }
@@ -400,6 +451,10 @@ class ReThrowExpr extends ThrowExpr {
/**
* A call to a destructor.
+ * ```
+ * struct S { ~S(void) {} } *s;
+ * s->~S();
+ * ```
*/
class DestructorCall extends FunctionCall {
DestructorCall() { super.getTarget() instanceof Destructor }
@@ -416,6 +471,11 @@ class DestructorCall extends FunctionCall {
* For example, given a plain old data type `pod_t`, the syntax `ptr->~pod_t()` is
* a vacuous destructor call, as `~pod_t` isn't actually a function. This can also
* occur in instantiated templates, as `ptr->~T()` becomes vacuous when `T` is `int`.
+ * ```
+ * typedef int pod_t;
+ * pod_t *s;
+ * s->~pod_t();
+ * ```
*/
class VacuousDestructorCall extends Expr, @vacuous_destructor_call {
/**
@@ -431,6 +491,9 @@ class VacuousDestructorCall extends Expr, @vacuous_destructor_call {
/**
* An initialization of a base class or member variable performed as part
* of a constructor's explicit initializer list or implicit actions.
+ *
+ * This is a QL root class for reprenting various types of constructor
+ * initializations.
*/
class ConstructorInit extends Expr, @ctorinit {
override string getCanonicalQLClass() { result = "ConstructorInit" }
@@ -447,6 +510,15 @@ class ConstructorBaseInit extends ConstructorInit, ConstructorCall {
/**
* A call to a constructor of a direct non-virtual base class as part of a
* constructor's initializer list or compiler-generated actions.
+ * ```
+ * struct S {
+ * int a;
+ * S(int b): a(b) {}
+ * };
+ * struct T: S {
+ * T(): S(33) {} // S(33) is a constructor call
+ * };
+ * ```
*/
class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit {
override string getCanonicalQLClass() { result = "ConstructorDirectInit" }
@@ -458,6 +530,15 @@ class ConstructorDirectInit extends ConstructorBaseInit, @ctordirectinit {
*
* If the virtual base class has already been initialized, then this
* call won't be performed.
+ * ```
+ * struct S {
+ * int a;
+ * S(int b): a(b) {}
+ * };
+ * struct T: virtual S {
+ * T(): S(33) {} // S(33) is a call to a virtual base constructor
+ * };
+ * ```
*/
class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit {
override string getCanonicalQLClass() { result = "ConstructorVirtualInit" }
@@ -466,6 +547,13 @@ class ConstructorVirtualInit extends ConstructorBaseInit, @ctorvirtualinit {
/**
* A call to a constructor of the same class as part of a constructor's
* initializer list, which delegates object construction (C++11 only).
+ * ```
+ * struct S {
+ * int a;
+ * S(int b): a(b) { }
+ * S(): S(0) { } // delegation to another constructor
+ * };
+ * ```
*/
class ConstructorDelegationInit extends ConstructorBaseInit, @ctordelegatinginit {
override string getCanonicalQLClass() { result = "ConstructorDelegationInit" }
@@ -474,6 +562,14 @@ class ConstructorDelegationInit extends ConstructorBaseInit, @ctordelegatinginit
/**
* An initialization of a member variable performed as part of a
* constructor's explicit initializer list or implicit actions.
+ * In the example below, member variable `b` is being initialized by
+ * constructor parameter `a`:
+ * ```
+ * struct S {
+ * int b;
+ * S(int a): b(a) {}
+ * } s(2);
+ * ```
*/
class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit {
/** Gets the field being initialized. */
@@ -515,6 +611,12 @@ class DestructorBaseDestruction extends DestructorCall, DestructorDestruction {
/**
* A call to a destructor of a direct non-virtual base class as part of a
* destructor's compiler-generated actions.
+ * ```
+ * struct S { ~S(void) {} };
+ * struct T: S {
+ * ~T(void) {} // will call ~S()
+ * };
+ * ```
*/
class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirectdestruct {
override string getCanonicalQLClass() { result = "DestructorDirectDestruction" }
@@ -526,6 +628,12 @@ class DestructorDirectDestruction extends DestructorBaseDestruction, @dtordirect
*
* If the virtual base class wasn't initialized by the ConstructorVirtualInit
* in the corresponding constructor, then this call won't be performed.
+ * ```
+ * struct S { ~S(void) {} };
+ * struct T: virtual S {
+ * ~T(void) {} // will call ~S()
+ * };
+ * ```
*/
class DestructorVirtualDestruction extends DestructorBaseDestruction, @dtorvirtualdestruct {
override string getCanonicalQLClass() { result = "DestructorVirtualDestruction" }
@@ -534,6 +642,13 @@ class DestructorVirtualDestruction extends DestructorBaseDestruction, @dtorvirtu
/**
* A destruction of a member variable performed as part of a
* destructor's compiler-generated actions.
+ * ```
+ * struct S { ~S(void) {} };
+ * struct T {
+ * S s;
+ * ~T(void) {} // will call s.~S()
+ * };
+ * ```
*/
class DestructorFieldDestruction extends DestructorDestruction, @dtorfielddestruct {
/** Gets the field being destructed. */
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
index df34515bd46..301fcdac9b5 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Cast.qll
@@ -22,7 +22,7 @@ abstract class Conversion extends Expr {
/**
* A C/C++ cast expression.
*
- * To get the type which the expression is being cast to, use getType().
+ * To get the type which the expression is being cast to, use `Cast::getType()`.
*
* There are two groups of subtypes of `Cast`. The first group differentiates
* between the different cast syntax forms, e.g. `CStyleCast`, `StaticCast`,
@@ -33,6 +33,9 @@ abstract class Conversion extends Expr {
* cast that is syntactically as `CStyleCast` may also be an `IntegralConversion`,
* a `PointerBaseClassConversion`, or some other semantic conversion. Similarly,
* a `PointerDerivedClassConversion` may also be a `CStyleCast` or a `StaticCast`.
+ *
+ * This is an abstract root QL class representing the different casts. For
+ * specific examples, consult the documentation for any of QL classes mentioned above.
*/
abstract class Cast extends Conversion, @cast {
/**
@@ -71,6 +74,10 @@ module CastSanity {
/**
* A cast expression in C, or a C-style cast expression in C++.
+ * ```
+ * float f = 3.0f;
+ * int i = (int)f;
+ * ```
*/
class CStyleCast extends Cast, @c_style_cast {
override string toString() { result = "(" + this.getType().getName() + ")..." }
@@ -82,6 +89,14 @@ class CStyleCast extends Cast, @c_style_cast {
/**
* A C++ `static_cast` expression.
+ *
+ * Please see https://en.cppreference.com/w/cpp/language/static_cast for
+ * more information.
+ * ```
+ * struct T: S {};
+ * struct S *s = get_S();
+ * struct T *t = static_cast(s); // downcast
+ * ```
*/
class StaticCast extends Cast, @static_cast {
override string toString() { result = "static_cast<" + this.getType().getName() + ">..." }
@@ -93,6 +108,13 @@ class StaticCast extends Cast, @static_cast {
/**
* A C++ `const_cast` expression.
+ *
+ * Please see https://en.cppreference.com/w/cpp/language/const_cast for
+ * more information.
+ * ```
+ * const struct S *s = get_S();
+ * struct S *t = const_cast(s);
+ * ```
*/
class ConstCast extends Cast, @const_cast {
override string toString() { result = "const_cast<" + this.getType().getName() + ">..." }
@@ -104,6 +126,13 @@ class ConstCast extends Cast, @const_cast {
/**
* A C++ `reinterpret_cast` expression.
+ *
+ * Please see https://en.cppreference.com/w/cpp/language/reinterpret_cast for
+ * more information.
+ * ```
+ * struct S *s = get_S();
+ * std::uintptr_t p = reinterpret_cast(s);
+ * ```
*/
class ReinterpretCast extends Cast, @reinterpret_cast {
override string toString() { result = "reinterpret_cast<" + this.getType().getName() + ">..." }
@@ -135,7 +164,11 @@ private predicate isPointerToMemberOrNullPointer(Type type) {
}
/**
- * A conversion from one arithmetic or enum type to another.
+ * A conversion from one arithmetic or `enum` type to another.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class ArithmeticConversion extends Cast {
ArithmeticConversion() {
@@ -149,6 +182,10 @@ class ArithmeticConversion extends Cast {
/**
* A conversion from one integral or enum type to another.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class IntegralConversion extends ArithmeticConversion {
IntegralConversion() {
@@ -164,7 +201,11 @@ class IntegralConversion extends ArithmeticConversion {
}
/**
- * A conversion from one floating point type to another.
+ * A conversion from one floating point type.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class FloatingPointConversion extends ArithmeticConversion {
FloatingPointConversion() {
@@ -181,6 +222,10 @@ class FloatingPointConversion extends ArithmeticConversion {
/**
* A conversion from a floating point type to an integral or enum type.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class FloatingPointToIntegralConversion extends ArithmeticConversion {
FloatingPointToIntegralConversion() {
@@ -197,6 +242,10 @@ class FloatingPointToIntegralConversion extends ArithmeticConversion {
/**
* A conversion from an integral or enum type to a floating point type.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class IntegralToFloatingPointConversion extends ArithmeticConversion {
IntegralToFloatingPointConversion() {
@@ -212,9 +261,15 @@ class IntegralToFloatingPointConversion extends ArithmeticConversion {
}
/**
- * A conversion from one pointer type to another. The conversion does
+ * A conversion from one pointer type to another.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
+ *
+ * The conversion does
* not modify the value of the pointer. For pointer conversions involving
- * casts between base and derived classes, see `BaseClassConversion` and
+ * casts between base and derived classes, please see see `BaseClassConversion` or
* `DerivedClassConversion`.
*/
class PointerConversion extends Cast {
@@ -232,10 +287,16 @@ class PointerConversion extends Cast {
}
/**
- * A conversion from one pointer-to-member type to another. The conversion
- * does not modify the value of the pointer-to-member. For pointer-to-member
- * conversions involving casts between base and derived classes, see
- * `PointerToMemberBaseClassConversion` and `PointerToMemberDerivedClassConversion`.
+ * A conversion from one pointer-to-member type to another.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
+ *
+ * The conversion does not modify the value of the pointer-to-member.
+ * For pointer-to-member conversions involving casts between base and
+ * derived classes, please see `PointerToMemberBaseClassConversion`
+ * or `PointerToMemberDerivedClassConversion`.
*/
class PointerToMemberConversion extends Cast {
PointerToMemberConversion() {
@@ -263,6 +324,10 @@ class PointerToMemberConversion extends Cast {
/**
* A conversion from a pointer type to an integral or enum type.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class PointerToIntegralConversion extends Cast {
PointerToIntegralConversion() {
@@ -280,6 +345,10 @@ class PointerToIntegralConversion extends Cast {
/**
* A conversion from an integral or enum type to a pointer type.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class IntegralToPointerConversion extends Cast {
IntegralToPointerConversion() {
@@ -297,7 +366,11 @@ class IntegralToPointerConversion extends Cast {
/**
* A conversion to `bool`. Returns `false` if the source value is zero,
- * false, or nullptr. Returns `true` otherwise.
+ * `false`, or `nullptr`. Returns `true` otherwise.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class BoolConversion extends Cast {
BoolConversion() { conversionkinds(underlyingElement(this), 1) }
@@ -309,6 +382,10 @@ class BoolConversion extends Cast {
/**
* A conversion to `void`.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class VoidConversion extends Cast {
VoidConversion() {
@@ -322,11 +399,16 @@ class VoidConversion extends Cast {
}
/**
- * A conversion between two pointers or glvalues related by inheritance. The
- * base class will always be either a direct base class of the derived class,
+ * A conversion between two pointers or _glvalue_s related by inheritance.
+ *
+ * The base class will always be either a direct base class of the derived class,
* or a virtual base class of the derived class. A conversion to an indirect
* non-virtual base class will be represented as a sequence of conversions to
* direct base classes.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class InheritanceConversion extends Cast {
InheritanceConversion() {
@@ -377,8 +459,12 @@ private Class getConversionClass(Expr expr) {
}
/**
- * A conversion from a pointer or glvalue of a derived class to a pointer or
- * glvalue of a direct or virtual base class.
+ * A conversion from a pointer or _glvalue_ of a derived class to a pointer or
+ * _glvalue_ of a direct or virtual base class.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class BaseClassConversion extends InheritanceConversion {
BaseClassConversion() { conversionkinds(underlyingElement(this), 2) }
@@ -400,8 +486,12 @@ class BaseClassConversion extends InheritanceConversion {
}
/**
- * A conversion from a pointer or glvalue to a base class to a pointer or glvalue
+ * A conversion from a pointer or _glvalue_ to a base class to a pointer or _glvalue_
* to a direct derived class.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class DerivedClassConversion extends InheritanceConversion {
DerivedClassConversion() { conversionkinds(underlyingElement(this), 3) }
@@ -420,6 +510,10 @@ class DerivedClassConversion extends InheritanceConversion {
/**
* A conversion from a pointer-to-member of a derived class to a pointer-to-member
* of an immediate base class.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class PointerToMemberBaseClassConversion extends Cast {
PointerToMemberBaseClassConversion() { conversionkinds(underlyingElement(this), 4) }
@@ -436,6 +530,10 @@ class PointerToMemberBaseClassConversion extends Cast {
/**
* A conversion from a pointer-to-member of a base class to a pointer-to-member
* of an immediate derived class.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class PointerToMemberDerivedClassConversion extends Cast {
PointerToMemberDerivedClassConversion() { conversionkinds(underlyingElement(this), 5) }
@@ -450,9 +548,13 @@ class PointerToMemberDerivedClassConversion extends Cast {
}
/**
- * A conversion of a glvalue from one type to another. The conversion does not
- * modify the address of the glvalue. For glvalue conversions involving base and
+ * A conversion of a _glvalue_ from one type to another. The conversion does not
+ * modify the address of the _glvalue_. For _glvalue_ conversions involving base and
* derived classes, see `BaseClassConversion` and `DerivedClassConversion`.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class GlvalueConversion extends Cast {
GlvalueConversion() { conversionkinds(underlyingElement(this), 6) }
@@ -465,18 +567,22 @@ class GlvalueConversion extends Cast {
}
/**
- * The adjustment of the type of a class prvalue. Most commonly seen in code
+ * The adjustment of the type of a class _prvalue_. Most commonly seen in code
* similar to:
- *
+ * ```
* class String { ... };
* String func();
* void caller() {
* const String& r = func();
* }
- *
- * In the above example, the result of the call to `func` is a prvalue of type
+ * ```
+ * In the above example, the result of the call to `func` is a _prvalue_ of type
* `String`, which will be adjusted to type `const String` before being bound
* to the reference.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class PrvalueAdjustmentConversion extends Cast {
PrvalueAdjustmentConversion() { conversionkinds(underlyingElement(this), 7) }
@@ -490,6 +596,14 @@ class PrvalueAdjustmentConversion extends Cast {
/**
* A C++ `dynamic_cast` expression.
+ *
+ * Please see https://en.cppreference.com/w/cpp/language/dynamic_cast for
+ * more information.
+ * ```
+ * struct T: S {};
+ * struct S *s = get_S();
+ * struct T *t = dynamic_cast(s); // downcast
+ * ```
*/
class DynamicCast extends Cast, @dynamic_cast {
override string toString() { result = "dynamic_cast<" + this.getType().getName() + ">..." }
@@ -504,6 +618,11 @@ class DynamicCast extends Cast, @dynamic_cast {
/**
* A Microsoft C/C++ `__uuidof` expression that returns the UUID of a type, as
* specified by the `__declspec(uuid)` attribute.
+ * ```
+ * struct UUID { char a[16]; };
+ * struct __declspec(uuid("{01234567-89ab-cdef-0123-456789ABCDEF}")) S {};
+ * UUID uuid = __uuidof(S);
+ * ```
*/
class UuidofOperator extends Expr, @uuidof {
override string toString() {
@@ -519,22 +638,18 @@ class UuidofOperator extends Expr, @uuidof {
}
/**
- * A C++ `typeid` expression which provides runtime type information
- * about an expression or type.
+ * A C++ `typeid` expression which provides run-time type information (RTTI)
+ * about its argument.
+ *
+ * Please see https://en.cppreference.com/w/cpp/language/typeid for more
+ * information.
+ * ```
+ * Base *ptr = new Derived;
+ * const std::type_info &info1 = typeid(ptr);
+ * printf("the type of ptr is: %s\n", typeid(ptr).name());
+ * ```
*/
class TypeidOperator extends Expr, @type_id {
- /**
- * Gets the type that is returned by this typeid expression.
- *
- * For example in the following code the `typeid` returns the
- * type `MyClass *`.
- *
- * ```
- * MyClass *ptr;
- *
- * printf("the type of ptr is: %s\n", typeid(ptr).name);
- * ```
- */
Type getResultType() { typeid_bind(underlyingElement(this), unresolveElement(result)) }
/**
@@ -566,6 +681,10 @@ class TypeidOperator extends Expr, @type_id {
*
* This expression only appears in templates themselves - in any actual
* instantiations, "sizeof...(x)" will be replaced by its integer value.
+ * ```
+ * template < typename... T >
+ * int count ( T &&... t ) { return sizeof... ( t ); }
+ * ```
*/
class SizeofPackOperator extends Expr, @sizeof_pack {
override string toString() { result = "sizeof...(...)" }
@@ -586,6 +705,9 @@ abstract class SizeofOperator extends Expr, @runtime_sizeof {
/**
* A C/C++ sizeof expression whose operand is an expression.
+ * ```
+ * if (sizeof(a) == sizeof(b)) { c = (b)a; }
+ * ```
*/
class SizeofExprOperator extends SizeofOperator {
SizeofExprOperator() { exists(Expr e | this.getChild(0) = e) }
@@ -611,6 +733,9 @@ class SizeofExprOperator extends SizeofOperator {
/**
* A C/C++ sizeof expression whose operand is a type name.
+ * ```
+ * int szlong = sizeof(int) == sizeof(long)? 4 : 8;
+ * ```
*/
class SizeofTypeOperator extends SizeofOperator {
SizeofTypeOperator() { sizeof_bind(underlyingElement(this), _) }
@@ -643,6 +768,9 @@ abstract class AlignofOperator extends Expr, @runtime_alignof {
/**
* A C++11 `alignof` expression whose operand is an expression.
+ * ```
+ * int addrMask = ~(alignof(expr) - 1);
+ * ```
*/
class AlignofExprOperator extends AlignofOperator {
AlignofExprOperator() { exists(Expr e | this.getChild(0) = e) }
@@ -662,6 +790,9 @@ class AlignofExprOperator extends AlignofOperator {
/**
* A C++11 `alignof` expression whose operand is a type name.
+ * ```
+ * bool proper_alignment = (alingof(T) == alignof(T[0]);
+ * ```
*/
class AlignofTypeOperator extends AlignofOperator {
AlignofTypeOperator() { sizeof_bind(underlyingElement(this), _) }
@@ -679,6 +810,10 @@ class AlignofTypeOperator extends AlignofOperator {
/**
* A C/C++ array to pointer conversion.
+ *
+ * The conversion is either implicit or underlies a particular cast.
+ * Please see `CStyleCast`, `StaticCast`, `ConstCast`
+ * or `ReinterpretCast` for more information.
*/
class ArrayToPointerConversion extends Conversion, @array_to_pointer {
/** Gets a textual representation of this conversion. */
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll
index c8f1e215656..241bd102d7b 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/ComparisonOperation.qll
@@ -2,6 +2,8 @@ import semmle.code.cpp.exprs.Expr
/**
* A C/C++ comparison operation, that is, either an equality operation or a relational operation.
+ *
+ * This is a QL abstract base class for all comparisons.
*/
abstract class ComparisonOperation extends BinaryOperation { }
@@ -14,6 +16,9 @@ abstract class EqualityOperation extends ComparisonOperation {
/**
* A C/C++ equal expression.
+ * ```
+ * bool c = (a == b);
+ * ```
*/
class EQExpr extends EqualityOperation, @eqexpr {
override string getCanonicalQLClass() { result = "EQExpr" }
@@ -23,6 +28,9 @@ class EQExpr extends EqualityOperation, @eqexpr {
/**
* A C/C++ not equal expression.
+ * ```
+ * bool c = (a != b);
+ * ```
*/
class NEExpr extends EqualityOperation, @neexpr {
override string getCanonicalQLClass() { result = "NEExpr" }
@@ -65,6 +73,9 @@ abstract class RelationalOperation extends ComparisonOperation {
/**
* A C/C++ greater than expression.
+ * ```
+ * bool c = (a > b);
+ * ```
*/
class GTExpr extends RelationalOperation, @gtexpr {
override string getCanonicalQLClass() { result = "GTExpr" }
@@ -77,7 +88,10 @@ class GTExpr extends RelationalOperation, @gtexpr {
}
/**
- * A C/C++ lesser than expression.
+ * A C/C++ less than expression.
+ * ```
+ * bool c = (a < b);
+ * ```
*/
class LTExpr extends RelationalOperation, @ltexpr {
override string getCanonicalQLClass() { result = "LTExpr" }
@@ -91,6 +105,9 @@ class LTExpr extends RelationalOperation, @ltexpr {
/**
* A C/C++ greater than or equal expression.
+ * ```
+ * bool c = (a >= b);
+ * ```
*/
class GEExpr extends RelationalOperation, @geexpr {
override string getCanonicalQLClass() { result = "GEExpr" }
@@ -103,7 +120,10 @@ class GEExpr extends RelationalOperation, @geexpr {
}
/**
- * A C/C++ lesser than or equal expression.
+ * A C/C++ less than or equal expression.
+ * ```
+ * bool c = (a <= b);
+ * ```
*/
class LEExpr extends RelationalOperation, @leexpr {
override string getCanonicalQLClass() { result = "LEExpr" }
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
index 63141d9d4a1..22b54987a5d 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll
@@ -5,6 +5,8 @@ private import semmle.code.cpp.internal.AddressConstantExpression
/**
* A C/C++ expression.
+ *
+ * This is the root QL class for all expressions.
*/
class Expr extends StmtParent, @expr {
/** Gets the nth child of this expression. */
@@ -131,6 +133,8 @@ class Expr extends StmtParent, @expr {
valuebind(_, underlyingElement(this))
or
addressConstantExpression(this)
+ or
+ constantTemplateLiteral(this)
}
/**
@@ -161,36 +165,36 @@ class Expr extends StmtParent, @expr {
predicate mayBeGloballyImpure() { any() }
/**
- * Holds if this expression is an lvalue. An lvalue is an expression that
+ * Holds if this expression is an _lvalue_. An _lvalue_ is an expression that
* represents a location, rather than a value.
* See [basic.lval] for more about lvalues.
*/
predicate isLValueCategory() { expr_types(underlyingElement(this), _, 3) }
/**
- * Holds if this expression is an xvalue. An xvalue is a location whose
- * lifetime is about to end (e.g. an rvalue reference returned from a function
+ * Holds if this expression is an _xvalue_. An _xvalue_ is a location whose
+ * lifetime is about to end (e.g. an _rvalue_ reference returned from a function
* call).
* See [basic.lval] for more about xvalues.
*/
predicate isXValueCategory() { expr_types(underlyingElement(this), _, 2) }
/**
- * Holds if this expression is a prvalue. A prvalue is an expression that
+ * Holds if this expression is a _prvalue_. A _prvalue_ is an expression that
* represents a value, rather than a location.
* See [basic.lval] for more about prvalues.
*/
predicate isPRValueCategory() { expr_types(underlyingElement(this), _, 1) }
/**
- * Holds if this expression is a glvalue. A glvalue is either an lvalue or an
- * xvalue.
+ * Holds if this expression is a _glvalue_. A _glvalue_ is either an _lvalue_ or an
+ * _xvalue_.
*/
predicate isGLValueCategory() { isLValueCategory() or isXValueCategory() }
/**
- * Holds if this expression is an rvalue. An rvalue is either a prvalue or an
- * xvalue.
+ * Holds if this expression is an _rvalue_. An _rvalue_ is either a _prvalue_ or an
+ * _xvalue_.
*/
predicate isRValueCategory() { isPRValueCategory() or isXValueCategory() }
@@ -203,7 +207,7 @@ class Expr extends StmtParent, @expr {
* - "prvalue"
* - "prvalue(load)"
*
- * The "prvalue(load)" string is used when the expression is a prvalue, but
+ * The "prvalue(load)" string is used when the expression is a _prvalue_, but
* `hasLValueToRvalueConversion()` holds.
*/
string getValueCategoryString() {
@@ -250,32 +254,32 @@ class Expr extends StmtParent, @expr {
}
/**
- * Holds if this expression has undergone an lvalue-to-rvalue conversion to
+ * Holds if this expression has undergone an _lvalue_-to-_rvalue_ conversion to
* extract its value.
* for example:
* ```
* y = x;
* ```
- * The VariableAccess for `x` is a prvalue, and hasLValueToRValueConversion()
+ * The `VariableAccess` for `x` is a _prvalue_, and `hasLValueToRValueConversion()`
* holds because the value of `x` was loaded from the location of `x`.
- * The VariableAccess for `y` is an lvalue, and hasLValueToRValueConversion()
+ * The `VariableAccess` for `y` is an _lvalue_, and `hasLValueToRValueConversion()`
* does not hold because the value of `y` was not extracted.
*
- * See [conv.lval] for more about the lvalue-to-rvalue conversion
+ * See [conv.lval] for more about the _lvalue_-to-_rvalue_ conversion
*/
predicate hasLValueToRValueConversion() { expr_isload(underlyingElement(this)) }
/**
- * Holds if this expression is an LValue, in the sense of having an address.
+ * Holds if this expression is an _lvalue_, in the sense of having an address.
*
- * Being an LValue is best approximated as having an address.
- * This is a strict superset of modifiable LValues, which are best approximated by things which could be on the left-hand side of an assignment.
- * This is also a strict superset of expressions which provide an LValue, which is best approximated by things whose address is important.
+ * Being an _lvalue_ is best approximated as having an address.
+ * This is a strict superset of modifiable _lvalue_s, which are best approximated by things which could be on the left-hand side of an assignment.
+ * This is also a strict superset of expressions which provide an _lvalue_, which is best approximated by things whose address is important.
*
* See [basic.lval] in the C++ language specification.
- * In C++03, every expression is either an LValue or an RValue.
- * In C++11, every expression is exactly one of an LValue, an XValue, or a PRValue (with RValues being the union of XValues and PRValues).
- * Using the C++11 terminology, this predicate selects expressions whose value category is lvalue.
+ * In C++03, every expression is either an _lvalue_ or an _rvalue_.
+ * In C++11, every expression is exactly one of an _lvalue_, an _xvalue_, or a _prvalue_ (with _rvalue_s being the union of _xvalue_s and _prvalue_s).
+ * Using the C++11 terminology, this predicate selects expressions whose value category is _lvalue_.
*/
predicate isLValue() {
// C++ n3337 - 5.1.1 clause 1
@@ -456,6 +460,8 @@ class Expr extends StmtParent, @expr {
/**
* A C/C++ operation.
+ *
+ * This is the QL abstract root class for all operations.
*/
abstract class Operation extends Expr {
/** Gets the operator of this operation. */
@@ -507,7 +513,14 @@ abstract class BinaryOperation extends Operation {
*
* This is used to represent particular syntax within templates where the final
* form of the expression is not known. In actual instantiations, it will have
- * been turned into a constructor call or aggregate initializer or similar.
+ * been turned into either a constructor call or an aggregate initializer or similar.
+ * ```
+ * template
+ * struct S {
+ * T member;
+ * S() { member = T({ arg1, arg2 }); }
+ * };
+ * ```
*/
class ParenthesizedBracedInitializerList extends Expr, @braced_init_list {
override string toString() { result = "({...})" }
@@ -517,6 +530,12 @@ class ParenthesizedBracedInitializerList extends Expr, @braced_init_list {
/**
* A C/C++ parenthesis expression.
+ *
+ * It is typically used to raise the syntactic precedence of the subexpression that
+ * it contains. For example:
+ * ```
+ * int d = a & ( b | c );
+ * ```
*/
class ParenthesisExpr extends Conversion, @parexpr {
override string toString() { result = "(...)" }
@@ -526,6 +545,8 @@ class ParenthesisExpr extends Conversion, @parexpr {
/**
* A C/C++ expression that has not been resolved.
+ *
+ * It is assigned `ErroneousType` as its type.
*/
class ErrorExpr extends Expr, @errorexpr {
override string toString() { result = "" }
@@ -535,13 +556,29 @@ class ErrorExpr extends Expr, @errorexpr {
/**
* A Microsoft C/C++ __assume expression.
+ *
+ * Unlike `assert`, `__assume` is evaluated at compile time and
+ * is treated as a hint to the optimizer
+ * ```
+ * __assume(ptr < end_buf);
+ * ```
*/
class AssumeExpr extends Expr, @assume {
override string toString() { result = "__assume(...)" }
+
+ override string getCanonicalQLClass() { result = "AssumeExpr" }
+
+ /**
+ * Gets the operand of the `__assume` expressions.
+ */
+ Expr getOperand() { this.hasChild(result, 0) }
}
/**
* A C/C++ comma expression.
+ * ```
+ * int c = compute1(), compute2(), resulting_value;
+ * ```
*/
class CommaExpr extends Expr, @commaexpr {
override string getCanonicalQLClass() { result = "CommaExpr" }
@@ -574,6 +611,9 @@ class CommaExpr extends Expr, @commaexpr {
/**
* A C/C++ address-of expression.
+ * ```
+ * int *ptr = &var;
+ * ```
*/
class AddressOfExpr extends UnaryOperation, @address_of {
override string getCanonicalQLClass() { result = "AddressOfExpr" }
@@ -596,11 +636,14 @@ class AddressOfExpr extends UnaryOperation, @address_of {
}
/**
- * An implicit conversion from type T to type T&.
+ * An implicit conversion from type `T` to type `T &`.
*
- * This typically occurs when an expression of type T is used to initialize a variable or parameter of
- * type T&, and is to reference types what AddressOfExpr is to pointer types - though this class is
+ * This typically occurs when an expression of type `T` is used to initialize a variable or parameter of
+ * type `T &`, and is to reference types what `AddressOfExpr` is to pointer types, though this class is
* considered to be a conversion rather than an operation, and as such doesn't occur in the main AST.
+ * ```
+ * int &var_ref = var;
+ * ```
*/
class ReferenceToExpr extends Conversion, @reference_to {
override string toString() { result = "(reference to)" }
@@ -611,9 +654,12 @@ class ReferenceToExpr extends Conversion, @reference_to {
}
/**
- * An instance of unary operator * applied to a built-in type.
+ * An instance of the built-in unary `operator *` applied to a type.
*
- * For user-defined types, see OverloadedPointerDereferenceExpr.
+ * For user-defined overloads of `operator *`, see `OverloadedPointerDereferenceExpr`.
+ * ```
+ * int var = *varptr;
+ * ```
*/
class PointerDereferenceExpr extends UnaryOperation, @indirect {
override string getCanonicalQLClass() { result = "PointerDereferenceExpr" }
@@ -641,11 +687,15 @@ class PointerDereferenceExpr extends UnaryOperation, @indirect {
}
/**
- * An implicit conversion from type T& to type T.
+ * An implicit conversion from type `T &` to type `T`.
*
- * This typically occurs when an variable of type T& is used in a context which expects type T, and
- * is to reference types what PointerDereferenceExpr is to pointer types - though this class is
+ * This typically occurs when an variable of type `T &` is used in a context which expects type `T`, and
+ * is to reference types what `PointerDereferenceExpr` is to pointer types - though this class is
* considered to be a conversion rather than an operation, and as such doesn't occur in the main AST.
+ * ```
+ * float &f_ref = get_ref();
+ * float f = f_ref;
+ * ```
*/
class ReferenceDereferenceExpr extends Conversion, @ref_indirect {
override string toString() { result = "(reference dereference)" }
@@ -747,6 +797,9 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
/**
* A C++ `new` (non-array) expression.
+ * ```
+ * Foo *ptr = new Foo(3);
+ * ```
*/
class NewExpr extends NewOrNewArrayExpr, @new_expr {
override string toString() { result = "new" }
@@ -773,6 +826,10 @@ class NewExpr extends NewOrNewArrayExpr, @new_expr {
/**
* A C++ `new[]` (array) expression.
+ * ```
+ * Foo *foo = new Foo[]{1, 3, 5};
+ * Bar *bar = new Bar[5];
+ * ```
*/
class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr {
override string toString() { result = "new[]" }
@@ -818,6 +875,9 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr {
/**
* A C++ `delete` (non-array) expression.
+ * ```
+ * delete ptr;
+ * ```
*/
class DeleteExpr extends Expr, @delete_expr {
override string toString() { result = "delete" }
@@ -893,6 +953,9 @@ class DeleteExpr extends Expr, @delete_expr {
/**
* A C++ `delete[]` (array) expression.
+ * ```
+ * delete[] arr;
+ * ```
*/
class DeleteArrayExpr extends Expr, @delete_array_expr {
override string toString() { result = "delete[]" }
@@ -968,6 +1031,10 @@ class DeleteArrayExpr extends Expr, @delete_array_expr {
/**
* A compound statement enclosed in parentheses used as an expression (a GNU extension to C/C++).
+ * In the example below, `b` is the return value from the compound statement.
+ * ```
+ * int a = ({ int b = c + d; b; });
+ * ```
*/
class StmtExpr extends Expr, @expr_stmt {
override string toString() { result = "(statement expression)" }
@@ -997,7 +1064,7 @@ private Expr getStmtResultExpr(Stmt stmt) {
}
/**
- * A C/C++ this expression.
+ * A C++ `this` expression.
*/
class ThisExpr extends Expr, @thisaccess {
override string toString() { result = "this" }
@@ -1010,8 +1077,10 @@ class ThisExpr extends Expr, @thisaccess {
}
/**
- * A code block expression, for example `^ int (int x, int y) {return x + y;}`.
- *
+ * A code block expression, for example:
+ * ```
+ * ^ int (int x, int y) {return x + y;}
+ * ```
* Blocks are a language extension supported by Clang, and by Apple's
* branch of GCC.
*/
@@ -1027,7 +1096,11 @@ class BlockExpr extends Literal {
}
/**
- * A C++11 `noexcept` expression, for example `noexcept(1 + 2)`.
+ * A C++11 `noexcept` expression, returning `true` if its subexpression is guaranteed
+ * not to `throw` exceptions. For example:
+ * ```
+ * if (noexcept(func_1() + func_2())) { }
+ * ```
*/
class NoExceptExpr extends Expr, @noexceptexpr {
override string toString() { result = "noexcept(...)" }
@@ -1043,6 +1116,10 @@ class NoExceptExpr extends Expr, @noexceptexpr {
/**
* A C++17 fold expression. This will only appear in an uninstantiated template; any instantiations
* of the template will instead contain the sequence of expressions given by expanding the fold.
+ * ```
+ * template < typename... T >
+ * auto sum ( T... t ) { return ( t + ... + 0 ); }
+ * ```
*/
class FoldExpr extends Expr, @foldexpr {
override string toString() {
@@ -1119,3 +1196,17 @@ private predicate isStandardPlacementNewAllocator(Function operatorNew) {
// Pulled out for performance. See QL-796.
private predicate hasNoConversions(Expr e) { not e.hasConversion() }
+
+/**
+ * Holds if `e` is a literal of unknown value in a template, or a cast thereof.
+ * We assume that such literals are constant.
+ */
+private predicate constantTemplateLiteral(Expr e) {
+ // Unknown literals in uninstantiated templates could be enum constant
+ // accesses or pointer-to-member literals.
+ e instanceof Literal and
+ e.isFromUninstantiatedTemplate(_) and
+ not exists(e.getValue())
+ or
+ constantTemplateLiteral(e.(Cast).getExpr())
+}
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll
index f8d2b56190d..211b65c809d 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/Literal.qll
@@ -2,6 +2,8 @@ import semmle.code.cpp.exprs.Expr
/**
* A C/C++ literal.
+ *
+ * The is the QL root class for all literals.
*/
class Literal extends Expr, @literal {
/** Gets a textual representation of this literal. */
@@ -26,9 +28,7 @@ class Literal extends Expr, @literal {
* For example:
* ```
* void *label_ptr = &&myLabel; // &&myLabel is a LabelLiteral
- *
* goto *label_ptr; // this is a ComputedGotoStmt
- *
* myLabel: // this is a LabelStmt
* ```
*/
@@ -84,7 +84,11 @@ abstract class TextLiteral extends Literal {
}
/**
- * A character literal, for example `'a'` or `L'a'`.
+ * A character literal. For example:
+ * ```
+ * char c1 = 'a';
+ * wchar_t c2 = L'b';
+ * ```
*/
class CharLiteral extends TextLiteral {
CharLiteral() { this.getValueText().regexpMatch("(?s)\\s*L?'.*") }
@@ -98,7 +102,11 @@ class CharLiteral extends TextLiteral {
}
/**
- * A string literal, for example `"abcdef"` or `L"123456"`.
+ * A string literal. For example:
+ * ```
+ * const char *s1 = "abcdef";
+ * const wchar_t *s2 = L"123456";
+ * ```
*/
class StringLiteral extends TextLiteral {
StringLiteral() {
@@ -111,7 +119,11 @@ class StringLiteral extends TextLiteral {
}
/**
- * An octal literal.
+ * An octal literal. For example:
+ * ```
+ * char esc = 033;
+ * ```
+ * Octal literals must always start with the digit `0`.
*/
class OctalLiteral extends Literal {
OctalLiteral() { super.getValueText().regexpMatch("\\s*0[0-7]+[uUlL]*\\s*") }
@@ -121,6 +133,9 @@ class OctalLiteral extends Literal {
/**
* A hexadecimal literal.
+ * ```
+ * unsigned int32_t minus2 = 0xfffffffe;
+ * ```
*/
class HexLiteral extends Literal {
HexLiteral() { super.getValueText().regexpMatch("\\s*0[xX][0-9a-fA-F]+[uUlL]*\\s*") }
@@ -130,9 +145,10 @@ class HexLiteral extends Literal {
/**
* A C/C++ aggregate literal.
+ *
+ * For example:
*/
class AggregateLiteral extends Expr, @aggregateliteral {
- // if this is turned into a Literal we need to change mayBeImpure
override string getCanonicalQLClass() { result = "AggregateLiteral" }
/**
@@ -145,12 +161,20 @@ class AggregateLiteral extends Expr, @aggregateliteral {
result = this.(ClassAggregateLiteral).getFieldExpr(f)
}
+ override predicate mayBeImpure() { this.getAChild().mayBeImpure() }
+
+ override predicate mayBeGloballyImpure() { this.getAChild().mayBeGloballyImpure() }
+
/** Gets a textual representation of this aggregate literal. */
override string toString() { result = "{...}" }
}
/**
- * A C/C++ aggregate literal that initializes a class, struct, or union
+ * A C/C++ aggregate literal that initializes a `class`, `struct`, or `union`.
+ * For example:
+ * ```
+ * S s = { arg1, arg2, { arg3, arg4 }, arg5 };
+ * ```
*/
class ClassAggregateLiteral extends AggregateLiteral {
Class classType;
@@ -268,6 +292,9 @@ class ArrayOrVectorAggregateLiteral extends AggregateLiteral {
/**
* A C/C++ aggregate literal that initializes an array
+ * ```
+ * S s[4] = { s_1, s_2, s_3, s_n };
+ * ```
*/
class ArrayAggregateLiteral extends ArrayOrVectorAggregateLiteral {
ArrayType arrayType;
@@ -283,6 +310,15 @@ class ArrayAggregateLiteral extends ArrayOrVectorAggregateLiteral {
/**
* A C/C++ aggregate literal that initializes a GNU vector type.
+ *
+ * Braced initializer lists are used, similarly to what is done
+ * for arrays.
+ * ```
+ * typedef int v4si __attribute__ (( vector_size(4*sizeof(int)) ));
+ * v4si v = (v4si){ 1, 2, 3, 4 };
+ * typedef float float4 __attribute__((ext_vector_type(4)));
+ * float4 vf = {1.0f, 2.0f, 3.0f, 4.0f};
+ * ```
*/
class VectorAggregateLiteral extends ArrayOrVectorAggregateLiteral {
GNUVectorType vectorType;
diff --git a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll
index 902aab6358a..b85223f1213 100644
--- a/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll
+++ b/cpp/ql/src/semmle/code/cpp/exprs/LogicalOperation.qll
@@ -7,6 +7,9 @@ abstract class UnaryLogicalOperation extends UnaryOperation { }
/**
* A C/C++ logical not expression.
+ * ```
+ * c = !a;
+ * ```
*/
class NotExpr extends UnaryLogicalOperation, @notexpr {
override string getOperator() { result = "!" }
@@ -35,7 +38,10 @@ abstract class BinaryLogicalOperation extends BinaryOperation {
}
/**
- * A C/C++ logical and expression.
+ * A C/C++ logical AND expression.
+ * ```
+ * if (a && b) { }
+ * ```
*/
class LogicalAndExpr extends BinaryLogicalOperation, @andlogicalexpr {
override string getOperator() { result = "&&" }
@@ -53,7 +59,10 @@ class LogicalAndExpr extends BinaryLogicalOperation, @andlogicalexpr {
}
/**
- * A C/C++ logical or expression.
+ * A C/C++ logical OR expression.
+ * ```
+ * if (a || b) { }
+ * ```
*/
class LogicalOrExpr extends BinaryLogicalOperation, @orlogicalexpr {
override string getOperator() { result = "||" }
@@ -72,6 +81,9 @@ class LogicalOrExpr extends BinaryLogicalOperation, @orlogicalexpr {
/**
* A C/C++ conditional ternary expression.
+ * ```
+ * a = (b > c ? d : e);
+ * ```
*/
class ConditionalExpr extends Operation, @conditionalexpr {
/** Gets the condition of this conditional expression. */
diff --git a/cpp/ql/src/semmle/code/cpp/internal/AddressConstantExpression.qll b/cpp/ql/src/semmle/code/cpp/internal/AddressConstantExpression.qll
index 2d58f9a6f1a..a04aa839d88 100644
--- a/cpp/ql/src/semmle/code/cpp/internal/AddressConstantExpression.qll
+++ b/cpp/ql/src/semmle/code/cpp/internal/AddressConstantExpression.qll
@@ -32,6 +32,11 @@ private predicate constantAddressLValue(Expr lvalue) {
// tells us how it's going to be used.
lvalue.(FunctionAccess).getType() instanceof RoutineType
or
+ // Pointer-to-member literals in uninstantiated templates
+ lvalue instanceof Literal and
+ not exists(lvalue.getValue()) and
+ lvalue.isFromUninstantiatedTemplate(_)
+ or
// String literals have array types and undergo array-to-pointer conversion.
lvalue instanceof StringLiteral
or
@@ -61,6 +66,10 @@ private predicate constantAddressPointer(Expr pointer) {
// tells us how it's going to be used.
pointer.(FunctionAccess).getType() instanceof FunctionPointerType
or
+ // Pointer to member function. These accesses are always pointers even though
+ // their type is `RoutineType`.
+ pointer.(FunctionAccess).getTarget() instanceof MemberFunction
+ or
addressConstantVariable(pointer.(VariableAccess).getTarget()) and
pointer.getType().getUnderlyingType() instanceof PointerType
or
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 9a4f144df49..e0135e0ad2f 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -2,27 +2,20 @@ import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.IR
+private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
/**
- * A predictable expression is one where an external user can predict
+ * A predictable instruction is one where an external user can predict
* the value. For example, a literal in the source code is considered
* predictable.
*/
-// TODO: Change to use Instruction instead of Expr. Naive attempt breaks
-// TaintedAllocationSize qltest.
-private predicate predictable(Expr expr) {
- expr instanceof Literal
- or
- exists(BinaryOperation binop | binop = expr |
- predictable(binop.getLeftOperand()) and predictable(binop.getRightOperand())
- )
- or
- exists(UnaryOperation unop | unop = expr | predictable(unop.getOperand()))
-}
-
-// TODO: remove when `predictable` has an `Instruction` parameter instead of `Expr`.
private predicate predictableInstruction(Instruction instr) {
- predictable(DataFlow::instructionNode(instr).asExpr())
+ instr instanceof ConstantInstruction
+ or
+ instr instanceof StringConstantInstruction
+ or
+ // This could be a conversion on a string literal
+ predictableInstruction(instr.(UnaryInstruction).getUnary())
}
private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
@@ -45,7 +38,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
}
private predicate accessesVariable(CopyInstruction copy, Variable var) {
- exists(VariableAddressInstruction va | va.getVariable().getAST() = var |
+ exists(VariableAddressInstruction va | va.getASTVariable() = var |
copy.(StoreInstruction).getDestinationAddress() = va
or
copy.(LoadInstruction).getSourceAddress() = va
@@ -90,10 +83,10 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
predictableInstruction(i2.getAnOperand().getDef()) and
i1 = i2.getAnOperand().getDef()
)
- // TODO: Check that we have flow from `a` to `a[i]`. It may work for constant
- // `i` because there is flow through `predictable` `BinaryInstruction` and
- // through `LoadInstruction`.
- //
+ or
+ // This is part of the translation of `a[i]`, where we want taint to flow
+ // from `a`.
+ i2.(PointerAddInstruction).getLeft() = i1
// TODO: Flow from argument to return of known functions: Port missing parts
// of `returnArgument` to the `interfaces.Taint` and `interfaces.DataFlow`
// libraries.
@@ -153,7 +146,8 @@ GlobalOrNamespaceVariable globalVarFromId(string id) {
}
Function resolveCall(Call call) {
- // TODO: improve virtual dispatch. This will help in the test for
- // `UncontrolledProcessOperation.ql`.
- result = call.getTarget()
+ exists(CallInstruction callInstruction |
+ callInstruction.getAST() = call and
+ result = Dispatch::viableCallable(callInstruction)
+ )
}
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 d0325a28d3e..9572639de59 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
@@ -1,5 +1,6 @@
private import cpp
private import semmle.code.cpp.ir.IR
+private import semmle.code.cpp.ir.dataflow.DataFlow
Function viableImpl(CallInstruction call) { result = viableCallable(call) }
@@ -20,6 +21,58 @@ Function viableCallable(CallInstruction call) {
functionSignatureWithBody(qualifiedName, nparams, result) and
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
)
+ or
+ // Rudimentary virtual dispatch support. It's essentially local data flow
+ // where the source is a derived-to-base conversion and the target is the
+ // qualifier of a call.
+ exists(Class derived, DataFlow::Node thisArgument |
+ nodeMayHaveClass(derived, thisArgument) and
+ overrideMayAffectCall(derived, thisArgument, _, result, call)
+ )
+}
+
+/**
+ * Holds if `call` is a virtual function call with qualifier `thisArgument` in
+ * `enclosingFunction`, whose static target is overridden by
+ * `overridingFunction` in `overridingClass`.
+ */
+pragma[noinline]
+private predicate overrideMayAffectCall(
+ Class overridingClass, DataFlow::Node thisArgument, Function enclosingFunction,
+ MemberFunction overridingFunction, CallInstruction call
+) {
+ call.getEnclosingFunction() = enclosingFunction and
+ overridingFunction.getAnOverriddenFunction+() = call.getStaticCallTarget() and
+ overridingFunction.getDeclaringType() = overridingClass and
+ thisArgument = DataFlow::instructionNode(call.getThisArgument())
+}
+
+/**
+ * Holds if `node` may have dynamic class `derived`, where `derived` is a class
+ * that may affect virtual dispatch within the enclosing function.
+ *
+ * For the sake of performance, this recursion is written out manually to make
+ * it a relation on `Class x Node` rather than `Node x Node` or `MemberFunction
+ * x Node`, both of which would be larger. It's a forward search since there
+ * should usually be fewer classes than calls.
+ *
+ * If a value is cast several classes up in the hierarchy, that will be modeled
+ * as a chain of `ConvertToBaseInstruction`s and will cause the search to start
+ * from each of them and pass through subsequent ones. There might be
+ * performance to gain by stopping before a second upcast and reconstructing
+ * the full chain in a "big-step" recursion after this one.
+ */
+private predicate nodeMayHaveClass(Class derived, DataFlow::Node node) {
+ exists(ConvertToBaseInstruction toBase |
+ derived = toBase.getDerivedClass() and
+ overrideMayAffectCall(derived, _, toBase.getEnclosingFunction(), _, _) and
+ node.asInstruction() = toBase
+ )
+ or
+ exists(DataFlow::Node prev |
+ nodeMayHaveClass(derived, prev) and
+ DataFlow::localFlowStep(prev, node)
+ )
}
/**
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 2da1d89e499..7ecb474f632 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
@@ -7,7 +7,7 @@
* on each other without introducing mutual recursion among those configurations.
*/
-private import DataFlowImplCommon
+private import DataFlowImplCommon::Public
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
- viableImpl(call) = result.getCallable() and
+private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
+ viableCallable(call) = result.getCallable() and
kind = result.getKind()
}
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
viableParamArg(_, node, arg)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- nodeCandFwd1(mid, config) and
- parameterValueFlowsToUpdate(p, mid) and
- viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
- nodeCandFwd1(ret, config) and
- getReturnPosition(ret) = viableReturnPos(call, kind) and
- node = getAnOutNode(call, kind)
+ exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
+ nodeCandFwd1ReturnPosition(pos, config) and
+ pos = viableReturnPos(call, kind) and
+ node = kind.getAnOutNode(call)
)
)
}
+pragma[noinline]
+private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(ReturnNodeExt ret |
+ nodeCandFwd1(ret, config) and
+ getReturnPosition(ret) = pos
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
exists(Node mid |
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
nodeCand1(param, config)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- parameterValueFlowsToUpdate(p, node) and
- viableParamArg(_, p, mid.getPreUpdateNode()) and
- nodeCand1(mid, config)
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind, OutNode out |
- nodeCand1(out, config) and
- getReturnPosition(node) = viableReturnPos(call, kind) and
- out = getAnOutNode(call, kind)
+ exists(ReturnPosition pos |
+ nodeCand1ReturnPosition(pos, config) and
+ getReturnPosition(node) = pos
)
)
}
+pragma[noinline]
+private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, ReturnKindExt kind, Node out |
+ nodeCand1(out, config) and
+ pos = viableReturnPos(call, kind) and
+ out = kind.getAnOutNode(call)
+ )
+}
+
/**
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
*/
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
simpleArgumentFlowsThrough(node1, node2, _, config)
}
+pragma[noinline]
+private ReturnPosition getReturnPosition1(Node node, Configuration config) {
+ result = getReturnPosition(node) and
+ nodeCand1(node, config)
+}
+
/**
* Holds if data can flow out of a callable from `node1` to `node2`, either
* through a `ReturnNode` or through an argument that has been mutated, and
* that this step is part of a path from a source to a sink.
*/
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
- nodeCand1(node1, unbind(config)) and
nodeCand1(node2, config) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
- (
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
- // flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
- )
+ exists(DataFlowCall call, ReturnKindExt kind |
+ getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
+ node2 = kind.getAnOutNode(call)
)
}
@@ -905,30 +903,35 @@ private predicate localFlowExit(Node node, Configuration config) {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
) {
- localFlowEntry(node1, config) and
+ not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowStep(node1, node2, config) and preservesValue = true
+ localFlowEntry(node1, config) and
+ (
+ localFlowStep(node1, node2, config) and preservesValue = true
+ or
+ additionalLocalFlowStep(node1, node2, config) and preservesValue = false
+ ) and
+ node1 != node2 and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ nodeCand(node2, unbind(config))
or
- additionalLocalFlowStep(node1, node2, config) and preservesValue = false
- ) and
- node1 != node2 and
- nodeCand(node2, unbind(config))
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, config) and
- localFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- nodeCand(node2, unbind(config))
- )
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, _, config) and
- additionalLocalFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- preservesValue = false and
- nodeCand(node2, unbind(config))
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, preservesValue, config, cc) and
+ localFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ nodeCand(node2, unbind(config))
+ )
+ or
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, _, config, cc) and
+ additionalLocalFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ preservesValue = false and
+ nodeCand(node2, unbind(config))
+ )
)
}
@@ -936,11 +939,11 @@ private predicate localFlowStepPlus(
* Holds if `node1` can step to `node2` in one or more local steps and this
* path can occur as a maximal subsequence of local steps in a dataflow path.
*/
-pragma[noinline]
+pragma[nomagic]
private predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, config) and
+ localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
localFlowExit(node2, config)
}
@@ -1000,7 +1003,7 @@ private class AccessPathFrontNilNode extends Node {
(
any(Configuration c).isSource(this)
or
- localFlowBigStep(_, this, false, _)
+ localFlowBigStep(_, this, false, _, _)
or
additionalJumpStep(_, this, _)
)
@@ -1023,12 +1026,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
(
exists(Node mid |
flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
apf = node.(AccessPathFrontNilNode).getApf()
)
or
@@ -1075,6 +1078,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
flowCandFwd(mid, fromArg, _, config) and
store(mid, f, node) and
nodeCand(node, unbind(config)) and
+ readStoreCand(f, unbind(config)) and
apf.headUsesContent(f)
)
or
@@ -1121,13 +1125,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
apf instanceof AccessPathFrontNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flowCand(mid, toReturn, apf, config)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(node, _, apf, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flowCand(mid, toReturn, nil, config) and
apf instanceof AccessPathFrontNil
)
@@ -1175,12 +1179,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
exists(Content f, AccessPathFront apf0 |
flowCandStore(node, f, toReturn, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, unbind(config))
+ consCand(f, apf, config)
)
or
exists(Content f, AccessPathFront apf0 |
flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, unbind(config)) and
+ consCandFwd(f, apf0, config) and
apf.headUsesContent(f)
)
}
@@ -1221,8 +1225,8 @@ private newtype TAccessPath =
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
/**
- * Conceptually a list of `Content`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
+ * 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.
@@ -1260,7 +1264,7 @@ abstract private class AccessPath extends TAccessPath {
private class AccessPathNil extends AccessPath, TNil {
override string toString() {
- exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -1276,7 +1280,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
override string toString() {
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
// The `concat` becomes "" if `ppReprType` has no result.
- result = f.toString() + concat(" : " + ppReprType(t))
+ result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
)
}
@@ -1293,8 +1297,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
override string toString() {
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
if len = 2
- then result = f1.toString() + ", " + f2.toString()
- else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f1.toString() + ", " + f2.toString() + "]"
+ else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -1362,12 +1366,12 @@ private predicate flowFwd0(
(
exists(Node mid |
flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
ap = node.(AccessPathNilNode).getAp() and
apf = ap.(AccessPathNil).getFront()
)
@@ -1471,13 +1475,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
ap instanceof AccessPathNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flow(mid, toReturn, ap, config)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(node, _, _, ap, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flow(mid, toReturn, nil, config) and
ap instanceof AccessPathNil
)
@@ -1618,11 +1622,14 @@ abstract class PathNode extends TPathNode {
/** Gets a successor of this node, if any. */
abstract PathNode getASuccessor();
+ /** Holds if this node is a source. */
+ abstract predicate isSource();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
exists(string s | s = this.(PathNodeMid).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -1683,12 +1690,6 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
// an intermediate step to another intermediate node
result = getSuccMid()
or
- // a final step to a sink via one or more local steps
- localFlowStepPlus(node, result.getNode(), _, config) and
- ap instanceof AccessPathNil and
- result instanceof PathNodeSink and
- result.getConfiguration() = unbind(this.getConfiguration())
- or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
mid = getSuccMid() and
@@ -1697,23 +1698,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
)
- or
- // a direct step from a source to a sink if a node is both
- this instanceof PathNodeSource and
- result instanceof PathNodeSink and
- this.getNode() = result.getNode() and
- result.getConfiguration() = unbind(this.getConfiguration())
}
-}
-/**
- * A flow graph node corresponding to a source.
- */
-private class PathNodeSource extends PathNodeMid {
- PathNodeSource() {
- getConfiguration().isSource(getNode()) and
- getCallContext() instanceof CallContextAny and
- getAp() instanceof AccessPathNil
+ override predicate isSource() {
+ config.isSource(node) and
+ cc instanceof CallContextAny and
+ ap instanceof AccessPathNil
}
}
@@ -1733,6 +1723,8 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
override Configuration getConfiguration() { result = config }
override PathNode getASuccessor() { none() }
+
+ override predicate isSource() { config.isSource(node) }
}
/**
@@ -1740,14 +1732,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
- localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- ap = mid.getAp()
- or
- localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof AccessPathNil and
- ap = node.(AccessPathNilNode).getAp()
+ exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
+ |
+ localFlowBigStep(midnode, node, true, conf, localCC) and
+ ap = ap0
+ or
+ localFlowBigStep(midnode, node, false, conf, localCC) and
+ ap0 instanceof AccessPathNil and
+ ap = node.(AccessPathNilNode).getAp()
+ )
or
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
or
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
or
- pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
- or
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
or
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
not innercc instanceof CallContextCall
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathOutOfCallable1(
- PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
+ PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
pathOutOfCallable0(mid, pos, innercc) and
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
- exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
- out = getAnOutNode(call, kind)
- )
-}
-
-private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- flow(node, unbind(mid.getConfiguration()))
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+ exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
+ out = kind.getAnOutNode(call)
)
}
@@ -1891,7 +1867,7 @@ private predicate pathIntoCallable(
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
/** Holds if data may flow from `p` to a return of kind `kind`. */
pragma[nomagic]
private predicate paramFlowsThrough(
- ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
) {
- exists(PathNodeMid mid, ReturnNode ret |
+ exists(PathNodeMid mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
)
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathThroughCallable0(
- DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
+ DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
) {
exists(ParameterNode p, CallContext innercc |
pathIntoCallable(mid, p, cc, innercc, call) and
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
- not parameterValueFlowsThrough(p, kind, innercc) and
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
mid.getAp() instanceof AccessPathNil
)
}
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(
- PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
-) {
- exists(DataFlowCall call, ReturnKind kind |
+private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
+ exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, apnil) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
@@ -1967,12 +1941,12 @@ private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallCon
* sinks.
*/
private predicate flowsTo(
- PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink,
- Configuration configuration
+ PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
) {
+ flowsource.isSource() and
flowsource.getConfiguration() = configuration and
flowsource.getNode() = source and
- pathSuccPlus(flowsource, flowsink) and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNode() = sink
}
@@ -1996,16 +1970,10 @@ private module FlowExploration {
// flow into callable
viableParamArg(_, node2, node1)
or
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
+ node2 = kind.getAnOutNode(call)
)
|
c1 = node1.getEnclosingCallable() and
@@ -2082,7 +2050,7 @@ private module FlowExploration {
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
- exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -2094,8 +2062,8 @@ private module FlowExploration {
override string toString() {
exists(Content f, int len | this = TPartialCons(f, len) |
if len = 1
- then result = f.toString()
- else result = f.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f.toString() + "]"
+ else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -2172,7 +2140,7 @@ private module FlowExploration {
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -2216,16 +2184,19 @@ private module FlowExploration {
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
) {
- localFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- or
- additionalLocalFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedRepr(node.getType())) and
- config = mid.getConfiguration()
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ (
+ localFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ ap = mid.getAp() and
+ config = mid.getConfiguration()
+ or
+ additionalLocalFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ mid.getAp() instanceof PartialAccessPathNil and
+ ap = TPartialNil(getErasedRepr(node.getType())) and
+ config = mid.getConfiguration()
+ )
or
jumpStep(mid.getNode(), node, config) and
cc instanceof CallContextAny and
@@ -2247,8 +2218,6 @@ private module FlowExploration {
apConsFwd(ap, f, ap0, config)
)
or
- partialPathOutOfArgument(mid, node, cc, ap, config)
- or
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
or
partialPathOutOfCallable(mid, node, cc, ap, config)
@@ -2307,7 +2276,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathOutOfCallable1(
- PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
+ PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -2321,36 +2290,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
- exists(ReturnKind kind, DataFlowCall call |
+ exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNode(call, kind)
- )
- }
-
- private predicate partialPathOutOfArgument(
- PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
- Configuration config
- ) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+ out = kind.getAnOutNode(call)
)
}
@@ -2389,7 +2334,7 @@ private module FlowExploration {
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -2397,10 +2342,10 @@ private module FlowExploration {
pragma[nomagic]
private predicate paramFlowsThroughInPartialPath(
- ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(PartialPathNodePriv mid, ReturnNode ret |
+ exists(PartialPathNodePriv mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -2417,23 +2362,23 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
- DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
+ DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
PartialAccessPathNil apnil, Configuration config
) {
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
- not parameterValueFlowsThrough(p, kind, innercc)
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
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 2da1d89e499..7ecb474f632 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
@@ -7,7 +7,7 @@
* on each other without introducing mutual recursion among those configurations.
*/
-private import DataFlowImplCommon
+private import DataFlowImplCommon::Public
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
- viableImpl(call) = result.getCallable() and
+private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
+ viableCallable(call) = result.getCallable() and
kind = result.getKind()
}
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
viableParamArg(_, node, arg)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- nodeCandFwd1(mid, config) and
- parameterValueFlowsToUpdate(p, mid) and
- viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
- nodeCandFwd1(ret, config) and
- getReturnPosition(ret) = viableReturnPos(call, kind) and
- node = getAnOutNode(call, kind)
+ exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
+ nodeCandFwd1ReturnPosition(pos, config) and
+ pos = viableReturnPos(call, kind) and
+ node = kind.getAnOutNode(call)
)
)
}
+pragma[noinline]
+private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(ReturnNodeExt ret |
+ nodeCandFwd1(ret, config) and
+ getReturnPosition(ret) = pos
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
exists(Node mid |
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
nodeCand1(param, config)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- parameterValueFlowsToUpdate(p, node) and
- viableParamArg(_, p, mid.getPreUpdateNode()) and
- nodeCand1(mid, config)
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind, OutNode out |
- nodeCand1(out, config) and
- getReturnPosition(node) = viableReturnPos(call, kind) and
- out = getAnOutNode(call, kind)
+ exists(ReturnPosition pos |
+ nodeCand1ReturnPosition(pos, config) and
+ getReturnPosition(node) = pos
)
)
}
+pragma[noinline]
+private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, ReturnKindExt kind, Node out |
+ nodeCand1(out, config) and
+ pos = viableReturnPos(call, kind) and
+ out = kind.getAnOutNode(call)
+ )
+}
+
/**
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
*/
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
simpleArgumentFlowsThrough(node1, node2, _, config)
}
+pragma[noinline]
+private ReturnPosition getReturnPosition1(Node node, Configuration config) {
+ result = getReturnPosition(node) and
+ nodeCand1(node, config)
+}
+
/**
* Holds if data can flow out of a callable from `node1` to `node2`, either
* through a `ReturnNode` or through an argument that has been mutated, and
* that this step is part of a path from a source to a sink.
*/
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
- nodeCand1(node1, unbind(config)) and
nodeCand1(node2, config) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
- (
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
- // flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
- )
+ exists(DataFlowCall call, ReturnKindExt kind |
+ getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
+ node2 = kind.getAnOutNode(call)
)
}
@@ -905,30 +903,35 @@ private predicate localFlowExit(Node node, Configuration config) {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
) {
- localFlowEntry(node1, config) and
+ not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowStep(node1, node2, config) and preservesValue = true
+ localFlowEntry(node1, config) and
+ (
+ localFlowStep(node1, node2, config) and preservesValue = true
+ or
+ additionalLocalFlowStep(node1, node2, config) and preservesValue = false
+ ) and
+ node1 != node2 and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ nodeCand(node2, unbind(config))
or
- additionalLocalFlowStep(node1, node2, config) and preservesValue = false
- ) and
- node1 != node2 and
- nodeCand(node2, unbind(config))
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, config) and
- localFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- nodeCand(node2, unbind(config))
- )
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, _, config) and
- additionalLocalFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- preservesValue = false and
- nodeCand(node2, unbind(config))
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, preservesValue, config, cc) and
+ localFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ nodeCand(node2, unbind(config))
+ )
+ or
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, _, config, cc) and
+ additionalLocalFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ preservesValue = false and
+ nodeCand(node2, unbind(config))
+ )
)
}
@@ -936,11 +939,11 @@ private predicate localFlowStepPlus(
* Holds if `node1` can step to `node2` in one or more local steps and this
* path can occur as a maximal subsequence of local steps in a dataflow path.
*/
-pragma[noinline]
+pragma[nomagic]
private predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, config) and
+ localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
localFlowExit(node2, config)
}
@@ -1000,7 +1003,7 @@ private class AccessPathFrontNilNode extends Node {
(
any(Configuration c).isSource(this)
or
- localFlowBigStep(_, this, false, _)
+ localFlowBigStep(_, this, false, _, _)
or
additionalJumpStep(_, this, _)
)
@@ -1023,12 +1026,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
(
exists(Node mid |
flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
apf = node.(AccessPathFrontNilNode).getApf()
)
or
@@ -1075,6 +1078,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
flowCandFwd(mid, fromArg, _, config) and
store(mid, f, node) and
nodeCand(node, unbind(config)) and
+ readStoreCand(f, unbind(config)) and
apf.headUsesContent(f)
)
or
@@ -1121,13 +1125,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
apf instanceof AccessPathFrontNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flowCand(mid, toReturn, apf, config)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(node, _, apf, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flowCand(mid, toReturn, nil, config) and
apf instanceof AccessPathFrontNil
)
@@ -1175,12 +1179,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
exists(Content f, AccessPathFront apf0 |
flowCandStore(node, f, toReturn, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, unbind(config))
+ consCand(f, apf, config)
)
or
exists(Content f, AccessPathFront apf0 |
flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, unbind(config)) and
+ consCandFwd(f, apf0, config) and
apf.headUsesContent(f)
)
}
@@ -1221,8 +1225,8 @@ private newtype TAccessPath =
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
/**
- * Conceptually a list of `Content`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
+ * 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.
@@ -1260,7 +1264,7 @@ abstract private class AccessPath extends TAccessPath {
private class AccessPathNil extends AccessPath, TNil {
override string toString() {
- exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -1276,7 +1280,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
override string toString() {
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
// The `concat` becomes "" if `ppReprType` has no result.
- result = f.toString() + concat(" : " + ppReprType(t))
+ result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
)
}
@@ -1293,8 +1297,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
override string toString() {
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
if len = 2
- then result = f1.toString() + ", " + f2.toString()
- else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f1.toString() + ", " + f2.toString() + "]"
+ else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -1362,12 +1366,12 @@ private predicate flowFwd0(
(
exists(Node mid |
flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
ap = node.(AccessPathNilNode).getAp() and
apf = ap.(AccessPathNil).getFront()
)
@@ -1471,13 +1475,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
ap instanceof AccessPathNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flow(mid, toReturn, ap, config)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(node, _, _, ap, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flow(mid, toReturn, nil, config) and
ap instanceof AccessPathNil
)
@@ -1618,11 +1622,14 @@ abstract class PathNode extends TPathNode {
/** Gets a successor of this node, if any. */
abstract PathNode getASuccessor();
+ /** Holds if this node is a source. */
+ abstract predicate isSource();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
exists(string s | s = this.(PathNodeMid).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -1683,12 +1690,6 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
// an intermediate step to another intermediate node
result = getSuccMid()
or
- // a final step to a sink via one or more local steps
- localFlowStepPlus(node, result.getNode(), _, config) and
- ap instanceof AccessPathNil and
- result instanceof PathNodeSink and
- result.getConfiguration() = unbind(this.getConfiguration())
- or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
mid = getSuccMid() and
@@ -1697,23 +1698,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
)
- or
- // a direct step from a source to a sink if a node is both
- this instanceof PathNodeSource and
- result instanceof PathNodeSink and
- this.getNode() = result.getNode() and
- result.getConfiguration() = unbind(this.getConfiguration())
}
-}
-/**
- * A flow graph node corresponding to a source.
- */
-private class PathNodeSource extends PathNodeMid {
- PathNodeSource() {
- getConfiguration().isSource(getNode()) and
- getCallContext() instanceof CallContextAny and
- getAp() instanceof AccessPathNil
+ override predicate isSource() {
+ config.isSource(node) and
+ cc instanceof CallContextAny and
+ ap instanceof AccessPathNil
}
}
@@ -1733,6 +1723,8 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
override Configuration getConfiguration() { result = config }
override PathNode getASuccessor() { none() }
+
+ override predicate isSource() { config.isSource(node) }
}
/**
@@ -1740,14 +1732,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
- localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- ap = mid.getAp()
- or
- localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof AccessPathNil and
- ap = node.(AccessPathNilNode).getAp()
+ exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
+ |
+ localFlowBigStep(midnode, node, true, conf, localCC) and
+ ap = ap0
+ or
+ localFlowBigStep(midnode, node, false, conf, localCC) and
+ ap0 instanceof AccessPathNil and
+ ap = node.(AccessPathNilNode).getAp()
+ )
or
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
or
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
or
- pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
- or
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
or
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
not innercc instanceof CallContextCall
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathOutOfCallable1(
- PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
+ PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
pathOutOfCallable0(mid, pos, innercc) and
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
- exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
- out = getAnOutNode(call, kind)
- )
-}
-
-private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- flow(node, unbind(mid.getConfiguration()))
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+ exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
+ out = kind.getAnOutNode(call)
)
}
@@ -1891,7 +1867,7 @@ private predicate pathIntoCallable(
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
/** Holds if data may flow from `p` to a return of kind `kind`. */
pragma[nomagic]
private predicate paramFlowsThrough(
- ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
) {
- exists(PathNodeMid mid, ReturnNode ret |
+ exists(PathNodeMid mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
)
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathThroughCallable0(
- DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
+ DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
) {
exists(ParameterNode p, CallContext innercc |
pathIntoCallable(mid, p, cc, innercc, call) and
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
- not parameterValueFlowsThrough(p, kind, innercc) and
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
mid.getAp() instanceof AccessPathNil
)
}
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(
- PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
-) {
- exists(DataFlowCall call, ReturnKind kind |
+private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
+ exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, apnil) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
@@ -1967,12 +1941,12 @@ private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallCon
* sinks.
*/
private predicate flowsTo(
- PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink,
- Configuration configuration
+ PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
) {
+ flowsource.isSource() and
flowsource.getConfiguration() = configuration and
flowsource.getNode() = source and
- pathSuccPlus(flowsource, flowsink) and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNode() = sink
}
@@ -1996,16 +1970,10 @@ private module FlowExploration {
// flow into callable
viableParamArg(_, node2, node1)
or
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
+ node2 = kind.getAnOutNode(call)
)
|
c1 = node1.getEnclosingCallable() and
@@ -2082,7 +2050,7 @@ private module FlowExploration {
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
- exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -2094,8 +2062,8 @@ private module FlowExploration {
override string toString() {
exists(Content f, int len | this = TPartialCons(f, len) |
if len = 1
- then result = f.toString()
- else result = f.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f.toString() + "]"
+ else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -2172,7 +2140,7 @@ private module FlowExploration {
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -2216,16 +2184,19 @@ private module FlowExploration {
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
) {
- localFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- or
- additionalLocalFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedRepr(node.getType())) and
- config = mid.getConfiguration()
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ (
+ localFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ ap = mid.getAp() and
+ config = mid.getConfiguration()
+ or
+ additionalLocalFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ mid.getAp() instanceof PartialAccessPathNil and
+ ap = TPartialNil(getErasedRepr(node.getType())) and
+ config = mid.getConfiguration()
+ )
or
jumpStep(mid.getNode(), node, config) and
cc instanceof CallContextAny and
@@ -2247,8 +2218,6 @@ private module FlowExploration {
apConsFwd(ap, f, ap0, config)
)
or
- partialPathOutOfArgument(mid, node, cc, ap, config)
- or
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
or
partialPathOutOfCallable(mid, node, cc, ap, config)
@@ -2307,7 +2276,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathOutOfCallable1(
- PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
+ PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -2321,36 +2290,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
- exists(ReturnKind kind, DataFlowCall call |
+ exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNode(call, kind)
- )
- }
-
- private predicate partialPathOutOfArgument(
- PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
- Configuration config
- ) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+ out = kind.getAnOutNode(call)
)
}
@@ -2389,7 +2334,7 @@ private module FlowExploration {
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -2397,10 +2342,10 @@ private module FlowExploration {
pragma[nomagic]
private predicate paramFlowsThroughInPartialPath(
- ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(PartialPathNodePriv mid, ReturnNode ret |
+ exists(PartialPathNodePriv mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -2417,23 +2362,23 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
- DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
+ DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
PartialAccessPathNil apnil, Configuration config
) {
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
- not parameterValueFlowsThrough(p, kind, innercc)
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
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 2da1d89e499..7ecb474f632 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
@@ -7,7 +7,7 @@
* on each other without introducing mutual recursion among those configurations.
*/
-private import DataFlowImplCommon
+private import DataFlowImplCommon::Public
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
- viableImpl(call) = result.getCallable() and
+private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
+ viableCallable(call) = result.getCallable() and
kind = result.getKind()
}
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
viableParamArg(_, node, arg)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- nodeCandFwd1(mid, config) and
- parameterValueFlowsToUpdate(p, mid) and
- viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
- nodeCandFwd1(ret, config) and
- getReturnPosition(ret) = viableReturnPos(call, kind) and
- node = getAnOutNode(call, kind)
+ exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
+ nodeCandFwd1ReturnPosition(pos, config) and
+ pos = viableReturnPos(call, kind) and
+ node = kind.getAnOutNode(call)
)
)
}
+pragma[noinline]
+private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(ReturnNodeExt ret |
+ nodeCandFwd1(ret, config) and
+ getReturnPosition(ret) = pos
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
exists(Node mid |
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
nodeCand1(param, config)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- parameterValueFlowsToUpdate(p, node) and
- viableParamArg(_, p, mid.getPreUpdateNode()) and
- nodeCand1(mid, config)
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind, OutNode out |
- nodeCand1(out, config) and
- getReturnPosition(node) = viableReturnPos(call, kind) and
- out = getAnOutNode(call, kind)
+ exists(ReturnPosition pos |
+ nodeCand1ReturnPosition(pos, config) and
+ getReturnPosition(node) = pos
)
)
}
+pragma[noinline]
+private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, ReturnKindExt kind, Node out |
+ nodeCand1(out, config) and
+ pos = viableReturnPos(call, kind) and
+ out = kind.getAnOutNode(call)
+ )
+}
+
/**
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
*/
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
simpleArgumentFlowsThrough(node1, node2, _, config)
}
+pragma[noinline]
+private ReturnPosition getReturnPosition1(Node node, Configuration config) {
+ result = getReturnPosition(node) and
+ nodeCand1(node, config)
+}
+
/**
* Holds if data can flow out of a callable from `node1` to `node2`, either
* through a `ReturnNode` or through an argument that has been mutated, and
* that this step is part of a path from a source to a sink.
*/
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
- nodeCand1(node1, unbind(config)) and
nodeCand1(node2, config) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
- (
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
- // flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
- )
+ exists(DataFlowCall call, ReturnKindExt kind |
+ getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
+ node2 = kind.getAnOutNode(call)
)
}
@@ -905,30 +903,35 @@ private predicate localFlowExit(Node node, Configuration config) {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
) {
- localFlowEntry(node1, config) and
+ not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowStep(node1, node2, config) and preservesValue = true
+ localFlowEntry(node1, config) and
+ (
+ localFlowStep(node1, node2, config) and preservesValue = true
+ or
+ additionalLocalFlowStep(node1, node2, config) and preservesValue = false
+ ) and
+ node1 != node2 and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ nodeCand(node2, unbind(config))
or
- additionalLocalFlowStep(node1, node2, config) and preservesValue = false
- ) and
- node1 != node2 and
- nodeCand(node2, unbind(config))
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, config) and
- localFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- nodeCand(node2, unbind(config))
- )
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, _, config) and
- additionalLocalFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- preservesValue = false and
- nodeCand(node2, unbind(config))
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, preservesValue, config, cc) and
+ localFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ nodeCand(node2, unbind(config))
+ )
+ or
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, _, config, cc) and
+ additionalLocalFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ preservesValue = false and
+ nodeCand(node2, unbind(config))
+ )
)
}
@@ -936,11 +939,11 @@ private predicate localFlowStepPlus(
* Holds if `node1` can step to `node2` in one or more local steps and this
* path can occur as a maximal subsequence of local steps in a dataflow path.
*/
-pragma[noinline]
+pragma[nomagic]
private predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, config) and
+ localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
localFlowExit(node2, config)
}
@@ -1000,7 +1003,7 @@ private class AccessPathFrontNilNode extends Node {
(
any(Configuration c).isSource(this)
or
- localFlowBigStep(_, this, false, _)
+ localFlowBigStep(_, this, false, _, _)
or
additionalJumpStep(_, this, _)
)
@@ -1023,12 +1026,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
(
exists(Node mid |
flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
apf = node.(AccessPathFrontNilNode).getApf()
)
or
@@ -1075,6 +1078,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
flowCandFwd(mid, fromArg, _, config) and
store(mid, f, node) and
nodeCand(node, unbind(config)) and
+ readStoreCand(f, unbind(config)) and
apf.headUsesContent(f)
)
or
@@ -1121,13 +1125,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
apf instanceof AccessPathFrontNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flowCand(mid, toReturn, apf, config)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(node, _, apf, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flowCand(mid, toReturn, nil, config) and
apf instanceof AccessPathFrontNil
)
@@ -1175,12 +1179,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
exists(Content f, AccessPathFront apf0 |
flowCandStore(node, f, toReturn, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, unbind(config))
+ consCand(f, apf, config)
)
or
exists(Content f, AccessPathFront apf0 |
flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, unbind(config)) and
+ consCandFwd(f, apf0, config) and
apf.headUsesContent(f)
)
}
@@ -1221,8 +1225,8 @@ private newtype TAccessPath =
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
/**
- * Conceptually a list of `Content`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
+ * 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.
@@ -1260,7 +1264,7 @@ abstract private class AccessPath extends TAccessPath {
private class AccessPathNil extends AccessPath, TNil {
override string toString() {
- exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -1276,7 +1280,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
override string toString() {
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
// The `concat` becomes "" if `ppReprType` has no result.
- result = f.toString() + concat(" : " + ppReprType(t))
+ result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
)
}
@@ -1293,8 +1297,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
override string toString() {
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
if len = 2
- then result = f1.toString() + ", " + f2.toString()
- else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f1.toString() + ", " + f2.toString() + "]"
+ else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -1362,12 +1366,12 @@ private predicate flowFwd0(
(
exists(Node mid |
flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
ap = node.(AccessPathNilNode).getAp() and
apf = ap.(AccessPathNil).getFront()
)
@@ -1471,13 +1475,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
ap instanceof AccessPathNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flow(mid, toReturn, ap, config)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(node, _, _, ap, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flow(mid, toReturn, nil, config) and
ap instanceof AccessPathNil
)
@@ -1618,11 +1622,14 @@ abstract class PathNode extends TPathNode {
/** Gets a successor of this node, if any. */
abstract PathNode getASuccessor();
+ /** Holds if this node is a source. */
+ abstract predicate isSource();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
exists(string s | s = this.(PathNodeMid).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -1683,12 +1690,6 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
// an intermediate step to another intermediate node
result = getSuccMid()
or
- // a final step to a sink via one or more local steps
- localFlowStepPlus(node, result.getNode(), _, config) and
- ap instanceof AccessPathNil and
- result instanceof PathNodeSink and
- result.getConfiguration() = unbind(this.getConfiguration())
- or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
mid = getSuccMid() and
@@ -1697,23 +1698,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
)
- or
- // a direct step from a source to a sink if a node is both
- this instanceof PathNodeSource and
- result instanceof PathNodeSink and
- this.getNode() = result.getNode() and
- result.getConfiguration() = unbind(this.getConfiguration())
}
-}
-/**
- * A flow graph node corresponding to a source.
- */
-private class PathNodeSource extends PathNodeMid {
- PathNodeSource() {
- getConfiguration().isSource(getNode()) and
- getCallContext() instanceof CallContextAny and
- getAp() instanceof AccessPathNil
+ override predicate isSource() {
+ config.isSource(node) and
+ cc instanceof CallContextAny and
+ ap instanceof AccessPathNil
}
}
@@ -1733,6 +1723,8 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
override Configuration getConfiguration() { result = config }
override PathNode getASuccessor() { none() }
+
+ override predicate isSource() { config.isSource(node) }
}
/**
@@ -1740,14 +1732,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
- localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- ap = mid.getAp()
- or
- localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof AccessPathNil and
- ap = node.(AccessPathNilNode).getAp()
+ exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
+ |
+ localFlowBigStep(midnode, node, true, conf, localCC) and
+ ap = ap0
+ or
+ localFlowBigStep(midnode, node, false, conf, localCC) and
+ ap0 instanceof AccessPathNil and
+ ap = node.(AccessPathNilNode).getAp()
+ )
or
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
or
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
or
- pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
- or
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
or
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
not innercc instanceof CallContextCall
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathOutOfCallable1(
- PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
+ PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
pathOutOfCallable0(mid, pos, innercc) and
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
- exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
- out = getAnOutNode(call, kind)
- )
-}
-
-private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- flow(node, unbind(mid.getConfiguration()))
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+ exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
+ out = kind.getAnOutNode(call)
)
}
@@ -1891,7 +1867,7 @@ private predicate pathIntoCallable(
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
/** Holds if data may flow from `p` to a return of kind `kind`. */
pragma[nomagic]
private predicate paramFlowsThrough(
- ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
) {
- exists(PathNodeMid mid, ReturnNode ret |
+ exists(PathNodeMid mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
)
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathThroughCallable0(
- DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
+ DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
) {
exists(ParameterNode p, CallContext innercc |
pathIntoCallable(mid, p, cc, innercc, call) and
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
- not parameterValueFlowsThrough(p, kind, innercc) and
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
mid.getAp() instanceof AccessPathNil
)
}
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(
- PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
-) {
- exists(DataFlowCall call, ReturnKind kind |
+private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
+ exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, apnil) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
@@ -1967,12 +1941,12 @@ private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallCon
* sinks.
*/
private predicate flowsTo(
- PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink,
- Configuration configuration
+ PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
) {
+ flowsource.isSource() and
flowsource.getConfiguration() = configuration and
flowsource.getNode() = source and
- pathSuccPlus(flowsource, flowsink) and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNode() = sink
}
@@ -1996,16 +1970,10 @@ private module FlowExploration {
// flow into callable
viableParamArg(_, node2, node1)
or
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
+ node2 = kind.getAnOutNode(call)
)
|
c1 = node1.getEnclosingCallable() and
@@ -2082,7 +2050,7 @@ private module FlowExploration {
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
- exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -2094,8 +2062,8 @@ private module FlowExploration {
override string toString() {
exists(Content f, int len | this = TPartialCons(f, len) |
if len = 1
- then result = f.toString()
- else result = f.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f.toString() + "]"
+ else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -2172,7 +2140,7 @@ private module FlowExploration {
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -2216,16 +2184,19 @@ private module FlowExploration {
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
) {
- localFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- or
- additionalLocalFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedRepr(node.getType())) and
- config = mid.getConfiguration()
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ (
+ localFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ ap = mid.getAp() and
+ config = mid.getConfiguration()
+ or
+ additionalLocalFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ mid.getAp() instanceof PartialAccessPathNil and
+ ap = TPartialNil(getErasedRepr(node.getType())) and
+ config = mid.getConfiguration()
+ )
or
jumpStep(mid.getNode(), node, config) and
cc instanceof CallContextAny and
@@ -2247,8 +2218,6 @@ private module FlowExploration {
apConsFwd(ap, f, ap0, config)
)
or
- partialPathOutOfArgument(mid, node, cc, ap, config)
- or
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
or
partialPathOutOfCallable(mid, node, cc, ap, config)
@@ -2307,7 +2276,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathOutOfCallable1(
- PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
+ PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -2321,36 +2290,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
- exists(ReturnKind kind, DataFlowCall call |
+ exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNode(call, kind)
- )
- }
-
- private predicate partialPathOutOfArgument(
- PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
- Configuration config
- ) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+ out = kind.getAnOutNode(call)
)
}
@@ -2389,7 +2334,7 @@ private module FlowExploration {
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -2397,10 +2342,10 @@ private module FlowExploration {
pragma[nomagic]
private predicate paramFlowsThroughInPartialPath(
- ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(PartialPathNodePriv mid, ReturnNode ret |
+ exists(PartialPathNodePriv mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -2417,23 +2362,23 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
- DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
+ DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
PartialAccessPathNil apnil, Configuration config
) {
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
- not parameterValueFlowsThrough(p, kind, innercc)
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
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 2da1d89e499..7ecb474f632 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
@@ -7,7 +7,7 @@
* on each other without introducing mutual recursion among those configurations.
*/
-private import DataFlowImplCommon
+private import DataFlowImplCommon::Public
private import DataFlowImplSpecific::Private
import DataFlowImplSpecific::Public
@@ -258,8 +258,8 @@ private predicate additionalJumpStep(Node node1, Node node2, Configuration confi
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
pragma[noinline]
-private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKind kind) {
- viableImpl(call) = result.getCallable() and
+private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) {
+ viableCallable(call) = result.getCallable() and
kind = result.getKind()
}
@@ -313,22 +313,23 @@ private predicate nodeCandFwd1(Node node, Configuration config) {
viableParamArg(_, node, arg)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- nodeCandFwd1(mid, config) and
- parameterValueFlowsToUpdate(p, mid) and
- viableParamArg(_, p, node.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnNode ret, ReturnKind kind |
- nodeCandFwd1(ret, config) and
- getReturnPosition(ret) = viableReturnPos(call, kind) and
- node = getAnOutNode(call, kind)
+ exists(DataFlowCall call, ReturnPosition pos, ReturnKindExt kind |
+ nodeCandFwd1ReturnPosition(pos, config) and
+ pos = viableReturnPos(call, kind) and
+ node = kind.getAnOutNode(call)
)
)
}
+pragma[noinline]
+private predicate nodeCandFwd1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(ReturnNodeExt ret |
+ nodeCandFwd1(ret, config) and
+ getReturnPosition(ret) = pos
+ )
+}
+
pragma[nomagic]
private predicate nodeCandFwd1Read(Content f, Node node, Configuration config) {
exists(Node mid |
@@ -403,22 +404,23 @@ private predicate nodeCand1(Node node, Configuration config) {
nodeCand1(param, config)
)
or
- // flow out of an argument
- exists(PostUpdateNode mid, ParameterNode p |
- parameterValueFlowsToUpdate(p, node) and
- viableParamArg(_, p, mid.getPreUpdateNode()) and
- nodeCand1(mid, config)
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind, OutNode out |
- nodeCand1(out, config) and
- getReturnPosition(node) = viableReturnPos(call, kind) and
- out = getAnOutNode(call, kind)
+ exists(ReturnPosition pos |
+ nodeCand1ReturnPosition(pos, config) and
+ getReturnPosition(node) = pos
)
)
}
+pragma[noinline]
+private predicate nodeCand1ReturnPosition(ReturnPosition pos, Configuration config) {
+ exists(DataFlowCall call, ReturnKindExt kind, Node out |
+ nodeCand1(out, config) and
+ pos = viableReturnPos(call, kind) and
+ out = kind.getAnOutNode(call)
+ )
+}
+
/**
* Holds if `f` is the target of a read in the flow covered by `nodeCand1`.
*/
@@ -565,28 +567,24 @@ private predicate additionalLocalFlowStepOrFlowThroughCallable(
simpleArgumentFlowsThrough(node1, node2, _, config)
}
+pragma[noinline]
+private ReturnPosition getReturnPosition1(Node node, Configuration config) {
+ result = getReturnPosition(node) and
+ nodeCand1(node, config)
+}
+
/**
* Holds if data can flow out of a callable from `node1` to `node2`, either
* through a `ReturnNode` or through an argument that has been mutated, and
* that this step is part of a path from a source to a sink.
*/
private predicate flowOutOfCallable(Node node1, Node node2, Configuration config) {
- nodeCand1(node1, unbind(config)) and
nodeCand1(node2, config) and
not outBarrier(node1, config) and
not inBarrier(node2, config) and
- (
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
- // flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
- getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
- )
+ exists(DataFlowCall call, ReturnKindExt kind |
+ getReturnPosition1(node1, unbind(config)) = viableReturnPos(call, kind) and
+ node2 = kind.getAnOutNode(call)
)
}
@@ -905,30 +903,35 @@ private predicate localFlowExit(Node node, Configuration config) {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext cc
) {
- localFlowEntry(node1, config) and
+ not isUnreachableInCall(node2, cc.(LocalCallContextSpecificCall).getCall()) and
(
- localFlowStep(node1, node2, config) and preservesValue = true
+ localFlowEntry(node1, config) and
+ (
+ localFlowStep(node1, node2, config) and preservesValue = true
+ or
+ additionalLocalFlowStep(node1, node2, config) and preservesValue = false
+ ) and
+ node1 != node2 and
+ cc.relevantFor(node1.getEnclosingCallable()) and
+ not isUnreachableInCall(node1, cc.(LocalCallContextSpecificCall).getCall()) and
+ nodeCand(node2, unbind(config))
or
- additionalLocalFlowStep(node1, node2, config) and preservesValue = false
- ) and
- node1 != node2 and
- nodeCand(node2, unbind(config))
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, preservesValue, config) and
- localFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- nodeCand(node2, unbind(config))
- )
- or
- exists(Node mid |
- localFlowStepPlus(node1, mid, _, config) and
- additionalLocalFlowStep(mid, node2, config) and
- not mid instanceof CastNode and
- preservesValue = false and
- nodeCand(node2, unbind(config))
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, preservesValue, config, cc) and
+ localFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ nodeCand(node2, unbind(config))
+ )
+ or
+ exists(Node mid |
+ localFlowStepPlus(node1, mid, _, config, cc) and
+ additionalLocalFlowStep(mid, node2, config) and
+ not mid instanceof CastNode and
+ preservesValue = false and
+ nodeCand(node2, unbind(config))
+ )
)
}
@@ -936,11 +939,11 @@ private predicate localFlowStepPlus(
* Holds if `node1` can step to `node2` in one or more local steps and this
* path can occur as a maximal subsequence of local steps in a dataflow path.
*/
-pragma[noinline]
+pragma[nomagic]
private predicate localFlowBigStep(
- Node node1, Node node2, boolean preservesValue, Configuration config
+ Node node1, Node node2, boolean preservesValue, Configuration config, LocalCallContext callContext
) {
- localFlowStepPlus(node1, node2, preservesValue, config) and
+ localFlowStepPlus(node1, node2, preservesValue, config, callContext) and
localFlowExit(node2, config)
}
@@ -1000,7 +1003,7 @@ private class AccessPathFrontNilNode extends Node {
(
any(Configuration c).isSource(this)
or
- localFlowBigStep(_, this, false, _)
+ localFlowBigStep(_, this, false, _, _)
or
additionalJumpStep(_, this, _)
)
@@ -1023,12 +1026,12 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
(
exists(Node mid |
flowCandFwd(mid, fromArg, apf, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(mid, fromArg, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
apf = node.(AccessPathFrontNilNode).getApf()
)
or
@@ -1075,6 +1078,7 @@ private predicate flowCandFwd0(Node node, boolean fromArg, AccessPathFront apf,
flowCandFwd(mid, fromArg, _, config) and
store(mid, f, node) and
nodeCand(node, unbind(config)) and
+ readStoreCand(f, unbind(config)) and
apf.headUsesContent(f)
)
or
@@ -1121,13 +1125,13 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
apf instanceof AccessPathFrontNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flowCand(mid, toReturn, apf, config)
)
or
exists(Node mid, AccessPathFrontNil nil |
flowCandFwd(node, _, apf, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flowCand(mid, toReturn, nil, config) and
apf instanceof AccessPathFrontNil
)
@@ -1175,12 +1179,12 @@ private predicate flowCand0(Node node, boolean toReturn, AccessPathFront apf, Co
exists(Content f, AccessPathFront apf0 |
flowCandStore(node, f, toReturn, apf0, config) and
apf0.headUsesContent(f) and
- consCand(f, apf, unbind(config))
+ consCand(f, apf, config)
)
or
exists(Content f, AccessPathFront apf0 |
flowCandRead(node, f, toReturn, apf0, config) and
- consCandFwd(f, apf0, unbind(config)) and
+ consCandFwd(f, apf0, config) and
apf.headUsesContent(f)
)
}
@@ -1221,8 +1225,8 @@ private newtype TAccessPath =
TConsCons(Content f1, Content f2, int len) { consCand(f1, TFrontHead(f2), _) and len in [2 .. 5] }
/**
- * Conceptually a list of `Content`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
+ * 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.
@@ -1260,7 +1264,7 @@ abstract private class AccessPath extends TAccessPath {
private class AccessPathNil extends AccessPath, TNil {
override string toString() {
- exists(DataFlowType t | this = TNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -1276,7 +1280,7 @@ private class AccessPathConsNil extends AccessPathCons, TConsNil {
override string toString() {
exists(Content f, DataFlowType t | this = TConsNil(f, t) |
// The `concat` becomes "" if `ppReprType` has no result.
- result = f.toString() + concat(" : " + ppReprType(t))
+ result = "[" + f.toString() + "]" + concat(" : " + ppReprType(t))
)
}
@@ -1293,8 +1297,8 @@ private class AccessPathConsCons extends AccessPathCons, TConsCons {
override string toString() {
exists(Content f1, Content f2, int len | this = TConsCons(f1, f2, len) |
if len = 2
- then result = f1.toString() + ", " + f2.toString()
- else result = f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f1.toString() + ", " + f2.toString() + "]"
+ else result = "[" + f1.toString() + ", " + f2.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -1362,12 +1366,12 @@ private predicate flowFwd0(
(
exists(Node mid |
flowFwd(mid, fromArg, apf, ap, config) and
- localFlowBigStep(mid, node, true, config)
+ localFlowBigStep(mid, node, true, config, _)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, _, nil, config) and
- localFlowBigStep(mid, node, false, config) and
+ localFlowBigStep(mid, node, false, config, _) and
ap = node.(AccessPathNilNode).getAp() and
apf = ap.(AccessPathNil).getFront()
)
@@ -1471,13 +1475,13 @@ private predicate flow0(Node node, boolean toReturn, AccessPath ap, Configuratio
ap instanceof AccessPathNil
or
exists(Node mid |
- localFlowBigStep(node, mid, true, config) and
+ localFlowBigStep(node, mid, true, config, _) and
flow(mid, toReturn, ap, config)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(node, _, _, ap, config) and
- localFlowBigStep(node, mid, false, config) and
+ localFlowBigStep(node, mid, false, config, _) and
flow(mid, toReturn, nil, config) and
ap instanceof AccessPathNil
)
@@ -1618,11 +1622,14 @@ abstract class PathNode extends TPathNode {
/** Gets a successor of this node, if any. */
abstract PathNode getASuccessor();
+ /** Holds if this node is a source. */
+ abstract predicate isSource();
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
exists(string s | s = this.(PathNodeMid).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -1683,12 +1690,6 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
// an intermediate step to another intermediate node
result = getSuccMid()
or
- // a final step to a sink via one or more local steps
- localFlowStepPlus(node, result.getNode(), _, config) and
- ap instanceof AccessPathNil and
- result instanceof PathNodeSink and
- result.getConfiguration() = unbind(this.getConfiguration())
- or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
mid = getSuccMid() and
@@ -1697,23 +1698,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
)
- or
- // a direct step from a source to a sink if a node is both
- this instanceof PathNodeSource and
- result instanceof PathNodeSink and
- this.getNode() = result.getNode() and
- result.getConfiguration() = unbind(this.getConfiguration())
}
-}
-/**
- * A flow graph node corresponding to a source.
- */
-private class PathNodeSource extends PathNodeMid {
- PathNodeSource() {
- getConfiguration().isSource(getNode()) and
- getCallContext() instanceof CallContextAny and
- getAp() instanceof AccessPathNil
+ override predicate isSource() {
+ config.isSource(node) and
+ cc instanceof CallContextAny and
+ ap instanceof AccessPathNil
}
}
@@ -1733,6 +1723,8 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
override Configuration getConfiguration() { result = config }
override PathNode getASuccessor() { none() }
+
+ override predicate isSource() { config.isSource(node) }
}
/**
@@ -1740,14 +1732,20 @@ private class PathNodeSink extends PathNode, TPathNodeSink {
* a callable is recorded by `cc`.
*/
private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPath ap) {
- localFlowBigStep(mid.getNode(), node, true, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- ap = mid.getAp()
- or
- localFlowBigStep(mid.getNode(), node, false, mid.getConfiguration()) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof AccessPathNil and
- ap = node.(AccessPathNilNode).getAp()
+ exists(LocalCallContext localCC, AccessPath ap0, Node midnode, Configuration conf |
+ midnode = mid.getNode() and
+ conf = mid.getConfiguration() and
+ cc = mid.getCallContext() and
+ localCC = getLocalCallContext(cc, midnode.getEnclosingCallable()) and
+ ap0 = mid.getAp()
+ |
+ localFlowBigStep(midnode, node, true, conf, localCC) and
+ ap = ap0
+ or
+ localFlowBigStep(midnode, node, false, conf, localCC) and
+ ap0 instanceof AccessPathNil and
+ ap = node.(AccessPathNilNode).getAp()
+ )
or
jumpStep(mid.getNode(), node, mid.getConfiguration()) and
cc instanceof CallContextAny and
@@ -1762,8 +1760,6 @@ private predicate pathStep(PathNodeMid mid, Node node, CallContext cc, AccessPat
or
exists(Content f, AccessPath ap0 | contentStoreStep(mid, node, ap0, f, cc) and push(ap0, f, ap))
or
- pathOutOfArgument(mid, node, cc) and ap = mid.getAp()
- or
pathIntoCallable(mid, node, _, cc, _) and ap = mid.getAp()
or
pathOutOfCallable(mid, node, cc) and ap = mid.getAp()
@@ -1797,9 +1793,9 @@ private predicate pathOutOfCallable0(PathNodeMid mid, ReturnPosition pos, CallCo
not innercc instanceof CallContextCall
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathOutOfCallable1(
- PathNodeMid mid, DataFlowCall call, ReturnKind kind, CallContext cc
+ PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, CallContext cc
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
pathOutOfCallable0(mid, pos, innercc) and
@@ -1816,29 +1812,9 @@ private predicate pathOutOfCallable1(
* is a return from a callable and is recorded by `cc`, if needed.
*/
pragma[noinline]
-private predicate pathOutOfCallable(PathNodeMid mid, OutNode out, CallContext cc) {
- exists(ReturnKind kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
- out = getAnOutNode(call, kind)
- )
-}
-
-private predicate pathOutOfArgument(PathNodeMid mid, PostUpdateNode node, CallContext cc) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- flow(node, unbind(mid.getConfiguration()))
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+private predicate pathOutOfCallable(PathNodeMid mid, Node out, CallContext cc) {
+ exists(ReturnKindExt kind, DataFlowCall call | pathOutOfCallable1(mid, call, kind, cc) |
+ out = kind.getAnOutNode(call)
)
}
@@ -1891,7 +1867,7 @@ private predicate pathIntoCallable(
pathIntoCallable0(mid, callable, i, outercc, call, emptyAp) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -1900,9 +1876,9 @@ private predicate pathIntoCallable(
/** Holds if data may flow from `p` to a return of kind `kind`. */
pragma[nomagic]
private predicate paramFlowsThrough(
- ParameterNode p, ReturnKind kind, CallContextCall cc, AccessPathNil apnil, Configuration config
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, AccessPathNil apnil, Configuration config
) {
- exists(PathNodeMid mid, ReturnNode ret |
+ exists(PathNodeMid mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -1917,14 +1893,14 @@ private predicate paramFlowsThrough(
)
}
-pragma[noinline]
+pragma[nomagic]
private predicate pathThroughCallable0(
- DataFlowCall call, PathNodeMid mid, ReturnKind kind, CallContext cc, AccessPathNil apnil
+ DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPathNil apnil
) {
exists(ParameterNode p, CallContext innercc |
pathIntoCallable(mid, p, cc, innercc, call) and
paramFlowsThrough(p, kind, innercc, apnil, unbind(mid.getConfiguration())) and
- not parameterValueFlowsThrough(p, kind, innercc) and
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc) and
mid.getAp() instanceof AccessPathNil
)
}
@@ -1934,12 +1910,10 @@ private predicate pathThroughCallable0(
* The context `cc` is restored to its value prior to entering the callable.
*/
pragma[noinline]
-private predicate pathThroughCallable(
- PathNodeMid mid, OutNode out, CallContext cc, AccessPathNil apnil
-) {
- exists(DataFlowCall call, ReturnKind kind |
+private predicate pathThroughCallable(PathNodeMid mid, Node out, CallContext cc, AccessPathNil apnil) {
+ exists(DataFlowCall call, ReturnKindExt kind |
pathThroughCallable0(call, mid, kind, cc, apnil) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
@@ -1967,12 +1941,12 @@ private predicate valuePathThroughCallable(PathNodeMid mid, OutNode out, CallCon
* sinks.
*/
private predicate flowsTo(
- PathNodeSource flowsource, PathNodeSink flowsink, Node source, Node sink,
- Configuration configuration
+ PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
) {
+ flowsource.isSource() and
flowsource.getConfiguration() = configuration and
flowsource.getNode() = source and
- pathSuccPlus(flowsource, flowsink) and
+ (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNode() = sink
}
@@ -1996,16 +1970,10 @@ private module FlowExploration {
// flow into callable
viableParamArg(_, node2, node1)
or
- // flow out of an argument
- exists(ParameterNode p |
- parameterValueFlowsToUpdate(p, node1) and
- viableParamArg(_, p, node2.(PostUpdateNode).getPreUpdateNode())
- )
- or
// flow out of a callable
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
getReturnPosition(node1) = viableReturnPos(call, kind) and
- node2 = getAnOutNode(call, kind)
+ node2 = kind.getAnOutNode(call)
)
|
c1 = node1.getEnclosingCallable() and
@@ -2082,7 +2050,7 @@ private module FlowExploration {
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
- exists(DataFlowType t | this = TPartialNil(t) | result = concat(" : " + ppReprType(t)))
+ exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
@@ -2094,8 +2062,8 @@ private module FlowExploration {
override string toString() {
exists(Content f, int len | this = TPartialCons(f, len) |
if len = 1
- then result = f.toString()
- else result = f.toString() + ", ... (" + len.toString() + ")"
+ then result = "[" + f.toString() + "]"
+ else result = "[" + f.toString() + ", ... (" + len.toString() + ")]"
)
}
@@ -2172,7 +2140,7 @@ private module FlowExploration {
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
- if s = "" then result = "" else result = " [" + s + "]"
+ if s = "" then result = "" else result = " " + s
)
}
@@ -2216,16 +2184,19 @@ private module FlowExploration {
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, PartialAccessPath ap, Configuration config
) {
- localFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- or
- additionalLocalFlowStep(mid.getNode(), node, config) and
- cc = mid.getCallContext() and
- mid.getAp() instanceof PartialAccessPathNil and
- ap = TPartialNil(getErasedRepr(node.getType())) and
- config = mid.getConfiguration()
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
+ (
+ localFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ ap = mid.getAp() and
+ config = mid.getConfiguration()
+ or
+ additionalLocalFlowStep(mid.getNode(), node, config) and
+ cc = mid.getCallContext() and
+ mid.getAp() instanceof PartialAccessPathNil and
+ ap = TPartialNil(getErasedRepr(node.getType())) and
+ config = mid.getConfiguration()
+ )
or
jumpStep(mid.getNode(), node, config) and
cc instanceof CallContextAny and
@@ -2247,8 +2218,6 @@ private module FlowExploration {
apConsFwd(ap, f, ap0, config)
)
or
- partialPathOutOfArgument(mid, node, cc, ap, config)
- or
partialPathIntoCallable(mid, node, _, cc, _, ap, config)
or
partialPathOutOfCallable(mid, node, cc, ap, config)
@@ -2307,7 +2276,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathOutOfCallable1(
- PartialPathNodePriv mid, DataFlowCall call, ReturnKind kind, CallContext cc,
+ PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -2321,36 +2290,12 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPath ap, Configuration config
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
- exists(ReturnKind kind, DataFlowCall call |
+ exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
|
- out = getAnOutNode(call, kind)
- )
- }
-
- private predicate partialPathOutOfArgument(
- PartialPathNodePriv mid, PostUpdateNode node, CallContext cc, PartialAccessPath ap,
- Configuration config
- ) {
- exists(
- PostUpdateNode n, ParameterNode p, DataFlowCallable callable, CallContext innercc, int i,
- DataFlowCall call, ArgumentNode arg
- |
- mid.getNode() = n and
- parameterValueFlowsToUpdate(p, n) and
- innercc = mid.getCallContext() and
- p.isParameterOf(callable, i) and
- resolveReturn(innercc, callable, call) and
- node.getPreUpdateNode() = arg and
- arg.argumentOf(call, i) and
- ap = mid.getAp() and
- config = mid.getConfiguration()
- |
- if reducedViableImplInReturn(callable, call)
- then cc = TReturn(callable, call)
- else cc = TAnyCallContext()
+ out = kind.getAnOutNode(call)
)
}
@@ -2389,7 +2334,7 @@ private module FlowExploration {
partialPathIntoCallable0(mid, callable, i, outercc, call, emptyAp, ap, config) and
p.isParameterOf(callable, i)
|
- if reducedViableImplInCallContext(_, callable, call)
+ if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, emptyAp)
else innercc = TSomeCall(p, emptyAp)
)
@@ -2397,10 +2342,10 @@ private module FlowExploration {
pragma[nomagic]
private predicate paramFlowsThroughInPartialPath(
- ParameterNode p, ReturnKind kind, CallContextCall cc, PartialAccessPathNil apnil,
+ ParameterNode p, ReturnKindExt kind, CallContextCall cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(PartialPathNodePriv mid, ReturnNode ret |
+ exists(PartialPathNodePriv mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -2417,23 +2362,23 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
- DataFlowCall call, PartialPathNodePriv mid, ReturnKind kind, CallContext cc,
+ DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
PartialAccessPathNil apnil, Configuration config
) {
exists(ParameterNode p, CallContext innercc, PartialAccessPathNil midapnil |
partialPathIntoCallable(mid, p, cc, innercc, call, midapnil, config) and
paramFlowsThroughInPartialPath(p, kind, innercc, apnil, config) and
- not parameterValueFlowsThrough(p, kind, innercc)
+ not parameterValueFlowsThrough(p, kind.(ValueReturnKind).getKind(), innercc)
)
}
private predicate partialPathThroughCallable(
- PartialPathNodePriv mid, OutNode out, CallContext cc, PartialAccessPathNil apnil,
+ PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPathNil apnil,
Configuration config
) {
- exists(DataFlowCall call, ReturnKind kind |
+ exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, apnil, config) and
- out = getAnOutNode(call, kind)
+ out = kind.getAnOutNode(call)
)
}
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 97052e80004..8b9c8f5abd3 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
@@ -3,470 +3,861 @@ import DataFlowImplSpecific::Public
private ReturnNode getAReturnNodeOfKind(ReturnKind kind) { result.getKind() = kind }
-cached
+module Public {
+ import ImplCommon
+ import FlowThrough_v2
+}
+
private module ImplCommon {
- /**
- * Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
- * The instance parameter is considered to have index `-1`.
- */
- pragma[nomagic]
- private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
- p.isParameterOf(viableCallable(call), i)
- }
+ import Cached
- /**
- * Holds if `arg` is a possible argument to `p` in `call`, taking virtual
- * dispatch into account.
- */
cached
- predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
- exists(int i |
- viableParam(call, i, p) and
- arg.argumentOf(call, i)
- )
- }
+ private module Cached {
+ /**
+ * Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
+ * The instance parameter is considered to have index `-1`.
+ */
+ pragma[nomagic]
+ private predicate viableParam(DataFlowCall call, int i, ParameterNode p) {
+ p.isParameterOf(viableCallable(call), i)
+ }
- /**
- * Holds if `p` can flow to `node` in the same callable using only
- * value-preserving steps, not taking call contexts into account.
- */
- private predicate parameterValueFlowNoCtx(ParameterNode p, Node node) {
- p = node
- or
- exists(Node mid |
- parameterValueFlowNoCtx(p, mid) and
- simpleLocalFlowStep(mid, node) and
- compatibleTypes(p.getType(), node.getType())
- )
- or
- // flow through a callable
- exists(Node arg |
- parameterValueFlowNoCtx(p, arg) and
- argumentValueFlowsThroughNoCtx(arg, node) and
- compatibleTypes(p.getType(), node.getType())
- )
- }
-
- /**
- * Holds if `p` can flow to a return node of kind `kind` in the same
- * callable using only value-preserving steps, not taking call contexts
- * into account.
- */
- private predicate parameterValueFlowsThroughNoCtx(ParameterNode p, ReturnKind kind) {
- parameterValueFlowNoCtx(p, getAReturnNodeOfKind(kind))
- }
-
- pragma[nomagic]
- private predicate argumentValueFlowsThroughNoCtx0(
- DataFlowCall call, ArgumentNode arg, ReturnKind kind
- ) {
- exists(ParameterNode param | viableParamArg(call, param, arg) |
- parameterValueFlowsThroughNoCtx(param, kind)
- )
- }
-
- /**
- * Holds if `arg` flows to `out` through a call using only value-preserving steps,
- * not taking call contexts into account.
- */
- private predicate argumentValueFlowsThroughNoCtx(ArgumentNode arg, OutNode out) {
- exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThroughNoCtx0(call, arg, kind) |
- out = getAnOutNode(call, kind) and
- compatibleTypes(arg.getType(), out.getType())
- )
- }
-
- /**
- * Holds if `arg` is the `i`th argument of `call` inside the callable
- * `enclosing`, and `arg` may flow through `call`.
- */
- pragma[noinline]
- private predicate argumentOf(
- DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
- ) {
- arg.argumentOf(call, i) and
- argumentValueFlowsThroughNoCtx(arg, _) and
- enclosing = arg.getEnclosingCallable()
- }
-
- pragma[noinline]
- private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
-
- pragma[noinline]
- private predicate viableParamArg0(int i, ArgumentNode arg, CallContext outercc, DataFlowCall call) {
- exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
- outercc = TAnyCallContext()
- or
- outercc = TSomeCall(getAParameter(c), _)
- or
- exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
- reducedViableImplInCallContext(_, c, other)
+ /**
+ * Holds if `arg` is a possible argument to `p` in `call`, taking virtual
+ * dispatch into account.
+ */
+ cached
+ predicate viableParamArg(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
+ exists(int i |
+ viableParam(call, i, p) and
+ arg.argumentOf(call, i)
)
- )
- }
+ }
- pragma[noinline]
- private predicate viableParamArg1(
- ParameterNode p, DataFlowCallable callable, int i, ArgumentNode arg, CallContext outercc,
- DataFlowCall call
- ) {
- viableParamArg0(i, arg, outercc, call) and
- callable = resolveCall(call, outercc) and
- p.isParameterOf(callable, any(int j | j <= i and j >= i))
- }
+ /*
+ * The `FlowThrough_*` modules take a `step` relation as input and provide
+ * an `argumentValueFlowsThrough` relation as output.
+ *
+ * `FlowThrough_v1` includes just `simpleLocalFlowStep`, which is then used
+ * to detect getters and setters.
+ * `FlowThrough_v2` then includes a little bit of local field flow on top
+ * of `simpleLocalFlowStep`.
+ */
- /**
- * Holds if `arg` is a possible argument to `p`, in the call `call`, and
- * `arg` may flow through `call`. The possible contexts before and after
- * entering the callable are `outercc` and `innercc`, respectively.
- */
- private predicate viableParamArg(
- DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
- CallContextCall innercc
- ) {
- exists(int i, DataFlowCallable callable | viableParamArg1(p, callable, i, arg, outercc, call) |
- if reducedViableImplInCallContext(_, callable, call)
- then innercc = TSpecificCall(call, i, true)
- else innercc = TSomeCall(p, true)
- )
- }
+ private module FlowThrough_v1 {
+ private predicate step = simpleLocalFlowStep/2;
- private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
- result = TSomeCall(p, _)
- or
- exists(DataFlowCall call, int i, DataFlowCallable callable |
- result = TSpecificCall(call, i, _) and
- p.isParameterOf(callable, i) and
- reducedViableImplInCallContext(_, callable, call)
- )
- }
-
- /**
- * Holds if `p` can flow to `node` in the same callable using only
- * value-preserving steps, in call context `cc`.
- */
- private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
- p = node and
- parameterValueFlowsThroughNoCtx(p, _) and
- cc = getAValidCallContextForParameter(p)
- or
- exists(Node mid |
- parameterValueFlow(p, mid, cc) and
- simpleLocalFlowStep(mid, node) and
- compatibleTypes(p.getType(), node.getType())
- )
- or
- // flow through a callable
- exists(Node arg |
- parameterValueFlow(p, arg, cc) and
- argumentValueFlowsThrough(arg, node, cc) and
- compatibleTypes(p.getType(), node.getType())
- )
- }
-
- /**
- * Holds if `p` can flow to a return node of kind `kind` in the same
- * callable using only value-preserving steps, in call context `cc`.
- */
- cached
- predicate parameterValueFlowsThrough(ParameterNode p, ReturnKind kind, CallContextCall cc) {
- parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
- }
-
- pragma[nomagic]
- private predicate argumentValueFlowsThrough0(
- DataFlowCall call, ArgumentNode arg, ReturnKind kind, CallContext cc
- ) {
- exists(ParameterNode param, CallContext innercc |
- viableParamArg(call, param, arg, cc, innercc) and
- parameterValueFlowsThrough(param, kind, innercc)
- )
- }
-
- /**
- * Holds if `arg` flows to `out` through a call using only value-preserving steps,
- * in call context cc.
- */
- cached
- predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
- exists(DataFlowCall call, ReturnKind kind | argumentValueFlowsThrough0(call, arg, kind, cc) |
- out = getAnOutNode(call, kind) and
- compatibleTypes(arg.getType(), out.getType())
- )
- }
-
- /**
- * Holds if `p` can flow to the pre-update node of `n` in the same callable
- * using only value-preserving steps.
- */
- cached
- predicate parameterValueFlowsToUpdate(ParameterNode p, PostUpdateNode n) {
- parameterValueFlowNoCtx(p, n.getPreUpdateNode())
- }
-
- /**
- * Holds if data can flow from `node1` to `node2` in one local step or a step
- * through a value-preserving method.
- */
- private predicate localValueStep(Node node1, Node node2) {
- simpleLocalFlowStep(node1, node2) or
- argumentValueFlowsThrough(node1, node2, _)
- }
-
- /*
- * Calculation of `predicate store(Node node1, Content f, Node node2)`:
- * There are four cases:
- * - The base case: A direct local assignment given by `storeStep`.
- * - A call to a method or constructor with two arguments, `arg1` and `arg2`,
- * such that the call has the side-effect `arg2.f = arg1`.
- * - A call to a method that returns an object in which an argument has been
- * stored.
- * - A reverse step through a read when the result of the read has been
- * stored into. This handles cases like `x.f1.f2 = y`.
- * `storeViaSideEffect` covers the first two cases, and `storeReturn` covers
- * the third case.
- */
-
- /**
- * Holds if data can flow from `node1` to `node2` via a direct assignment to
- * `f` or via a call that acts as a setter.
- */
- cached
- predicate store(Node node1, Content f, Node node2) {
- storeViaSideEffect(node1, f, node2) or
- storeReturn(node1, f, node2) or
- read(node2.(PostUpdateNode).getPreUpdateNode(), f, node1.(PostUpdateNode).getPreUpdateNode())
- }
-
- private predicate storeViaSideEffect(Node node1, Content f, PostUpdateNode node2) {
- storeStep(node1, f, node2) and readStep(_, f, _)
- or
- exists(DataFlowCall call, int i1, int i2 |
- setterCall(call, i1, i2, f) and
- node1.(ArgumentNode).argumentOf(call, i1) and
- node2.getPreUpdateNode().(ArgumentNode).argumentOf(call, i2) and
- compatibleTypes(node1.getTypeBound(), f.getType()) and
- compatibleTypes(node2.getTypeBound(), f.getContainerType())
- )
- }
-
- pragma[nomagic]
- private predicate setterInParam(ParameterNode p1, Content f, ParameterNode p2) {
- exists(Node n1, PostUpdateNode n2 |
- parameterValueFlowNoCtx(p1, n1) and
- storeViaSideEffect(n1, f, n2) and
- parameterValueFlowNoCtx(p2, n2.getPreUpdateNode()) and
- p1 != p2
- )
- }
-
- pragma[nomagic]
- private predicate setterCall(DataFlowCall call, int i1, int i2, Content f) {
- exists(DataFlowCallable callable, ParameterNode p1, ParameterNode p2 |
- setterInParam(p1, f, p2) and
- callable = viableCallable(call) and
- p1.isParameterOf(callable, i1) and
- p2.isParameterOf(callable, i2)
- )
- }
-
- pragma[noinline]
- private predicate storeReturn0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
- exists(ParameterNode p |
- viableParamArg(call, p, arg) and
- setterReturn(p, f, kind)
- )
- }
-
- private predicate storeReturn(Node node1, Content f, Node node2) {
- exists(DataFlowCall call, ReturnKind kind |
- storeReturn0(call, kind, node1, f) and
- node2 = getAnOutNode(call, kind) and
- compatibleTypes(node1.getTypeBound(), f.getType()) and
- compatibleTypes(node2.getTypeBound(), f.getContainerType())
- )
- }
-
- private predicate setterReturn(ParameterNode p, Content f, ReturnKind kind) {
- exists(Node n1, Node n2 |
- parameterValueFlowNoCtx(p, n1) and
- store(n1, f, n2) and
- localValueStep*(n2, getAReturnNodeOfKind(kind))
- )
- }
-
- pragma[noinline]
- private predicate read0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
- exists(ParameterNode p |
- viableParamArg(call, p, arg) and
- getter(p, f, kind)
- )
- }
-
- /**
- * Holds if data can flow from `node1` to `node2` via a direct read of `f` or
- * via a getter.
- */
- cached
- predicate read(Node node1, Content f, Node node2) {
- readStep(node1, f, node2) and storeStep(_, f, _)
- or
- exists(DataFlowCall call, ReturnKind kind |
- read0(call, kind, node1, f) and
- node2 = getAnOutNode(call, kind) and
- compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
- compatibleTypes(node2.getTypeBound(), f.getType())
- )
- }
-
- private predicate getter(ParameterNode p, Content f, ReturnKind kind) {
- exists(Node n1, Node n2 |
- parameterValueFlowNoCtx(p, n1) and
- read(n1, f, n2) and
- localValueStep*(n2, getAReturnNodeOfKind(kind))
- )
- }
-
- cached
- predicate localStoreReadStep(Node node1, Node node2) {
- exists(Node mid1, Node mid2, Content f |
- store(node1, f, mid1) and
- localValueStep*(mid1, mid2) and
- read(mid2, f, node2)
- )
- }
-
- /**
- * Holds if `call` passes an implicit or explicit instance argument, i.e., an
- * expression that reaches a `this` parameter.
- */
- private predicate callHasInstanceArgument(DataFlowCall call) {
- exists(ArgumentNode arg | arg.argumentOf(call, -1))
- }
-
- cached
- newtype TCallContext =
- TAnyCallContext() or
- TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
- reducedViableImplInCallContext(_, _, call) and
- (emptyAp = true or emptyAp = false) and
- (
- exists(call.getArgument(i))
+ /**
+ * Holds if `p` can flow to `node` in the same callable using only
+ * value-preserving steps, not taking call contexts into account.
+ */
+ private predicate parameterValueFlowCand(ParameterNode p, Node node) {
+ p = node
or
- i = -1 and callHasInstanceArgument(call)
+ exists(Node mid |
+ parameterValueFlowCand(p, mid) and
+ step(mid, node) and
+ compatibleTypes(p.getType(), node.getType())
+ )
+ or
+ // flow through a callable
+ exists(Node arg |
+ parameterValueFlowCand(p, arg) and
+ argumentValueFlowsThroughCand(arg, node) and
+ compatibleTypes(p.getType(), node.getType())
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to a return node of kind `kind` in the same
+ * callable using only value-preserving steps, not taking call contexts
+ * into account.
+ */
+ private predicate parameterValueFlowsThroughCand(ParameterNode p, ReturnKind kind) {
+ parameterValueFlowCand(p, getAReturnNodeOfKind(kind))
+ }
+
+ pragma[nomagic]
+ private predicate argumentValueFlowsThroughCand0(
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind
+ ) {
+ exists(ParameterNode param | viableParamArg(call, param, arg) |
+ parameterValueFlowsThroughCand(param, kind)
+ )
+ }
+
+ /**
+ * Holds if `arg` flows to `out` through a call using only value-preserving steps,
+ * not taking call contexts into account.
+ */
+ private predicate argumentValueFlowsThroughCand(ArgumentNode arg, OutNode out) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThroughCand0(call, arg, kind)
+ |
+ out = getAnOutNode(call, kind) and
+ compatibleTypes(arg.getType(), out.getType())
+ )
+ }
+
+ /**
+ * Holds if `arg` is the `i`th argument of `call` inside the callable
+ * `enclosing`, and `arg` may flow through `call`.
+ */
+ pragma[noinline]
+ private predicate argumentOf(
+ DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
+ ) {
+ arg.argumentOf(call, i) and
+ argumentValueFlowsThroughCand(arg, _) and
+ enclosing = arg.getEnclosingCallable()
+ }
+
+ pragma[noinline]
+ private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
+
+ pragma[noinline]
+ private predicate viableParamArg0(
+ int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
+ ) {
+ exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
+ (
+ outercc = TAnyCallContext()
+ or
+ outercc = TSomeCall(getAParameter(c), _)
+ or
+ exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
+ recordDataFlowCallSite(other, c)
+ )
+ ) and
+ not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
+ )
+ }
+
+ pragma[noinline]
+ private predicate viableParamArg1(
+ ParameterNode p, DataFlowCallable callable, int i, ArgumentNode arg, CallContext outercc,
+ DataFlowCall call
+ ) {
+ viableParamArg0(i, arg, outercc, call) and
+ callable = resolveCall(call, outercc) and
+ p.isParameterOf(callable, any(int j | j <= i and j >= i))
+ }
+
+ /**
+ * Holds if `arg` is a possible argument to `p`, in the call `call`, and
+ * `arg` may flow through `call`. The possible contexts before and after
+ * entering the callable are `outercc` and `innercc`, respectively.
+ */
+ private predicate viableParamArg(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
+ CallContextCall innercc
+ ) {
+ exists(int i, DataFlowCallable callable |
+ viableParamArg1(p, callable, i, arg, outercc, call)
+ |
+ if recordDataFlowCallSite(call, callable)
+ then innercc = TSpecificCall(call, i, true)
+ else innercc = TSomeCall(p, true)
+ )
+ }
+
+ private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
+ result = TSomeCall(p, _)
+ or
+ exists(DataFlowCall call, int i, DataFlowCallable callable |
+ result = TSpecificCall(call, i, _) and
+ p.isParameterOf(callable, i) and
+ recordDataFlowCallSite(call, callable)
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to `node` in the same callable using only
+ * value-preserving steps, in call context `cc`.
+ */
+ private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
+ p = node and
+ parameterValueFlowsThroughCand(p, _) and
+ cc = getAValidCallContextForParameter(p)
+ or
+ exists(Node mid |
+ parameterValueFlow(p, mid, cc) and
+ step(mid, node) and
+ compatibleTypes(p.getType(), node.getType()) and
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
+ )
+ or
+ // flow through a callable
+ exists(Node arg |
+ parameterValueFlow(p, arg, cc) and
+ argumentValueFlowsThrough(arg, node, cc) and
+ compatibleTypes(p.getType(), node.getType()) and
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to a return node of kind `kind` in the same
+ * callable using only value-preserving steps, in call context `cc`.
+ */
+ private predicate parameterValueFlowsThrough(
+ ParameterNode p, ReturnKind kind, CallContextCall cc
+ ) {
+ parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
+ }
+
+ pragma[nomagic]
+ private predicate argumentValueFlowsThrough0(
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind, CallContext cc
+ ) {
+ exists(ParameterNode param, CallContext innercc |
+ viableParamArg(call, param, arg, cc, innercc) and
+ parameterValueFlowsThrough(param, kind, innercc)
+ )
+ }
+
+ /**
+ * Holds if `arg` flows to `out` through a call using only value-preserving steps,
+ * in call context cc.
+ */
+ predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThrough0(call, arg, kind, cc)
+ |
+ out = getAnOutNode(call, kind) and
+ not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
+ compatibleTypes(arg.getType(), out.getType())
+ )
+ }
+ }
+
+ /**
+ * Holds if `p` can flow to the pre-update node of `n` in the same callable
+ * using only value-preserving steps.
+ */
+ cached
+ predicate parameterValueFlowsToUpdate(ParameterNode p, PostUpdateNode n) {
+ parameterValueFlowNoCtx(p, n.getPreUpdateNode())
+ }
+
+ /**
+ * Holds if data can flow from `node1` to `node2` in one local step or a step
+ * through a value-preserving method.
+ */
+ private predicate localValueStep(Node node1, Node node2) {
+ simpleLocalFlowStep(node1, node2) or
+ FlowThrough_v1::argumentValueFlowsThrough(node1, node2, _)
+ }
+
+ /**
+ * Holds if `p` can flow to `node` in the same callable allowing local flow
+ * steps and value flow through methods. Call contexts are only accounted
+ * for in the nested calls.
+ */
+ private predicate parameterValueFlowNoCtx(ParameterNode p, Node node) {
+ p = node
+ or
+ exists(Node mid |
+ parameterValueFlowNoCtx(p, mid) and
+ localValueStep(mid, node) and
+ compatibleTypes(p.getType(), node.getType())
)
- } or
- TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
- TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
+ }
- cached
- newtype TReturnPosition =
- TReturnPosition0(DataFlowCallable c, ReturnKind kind) { returnPosition(_, c, kind) }
-}
+ /*
+ * Calculation of `predicate store(Node node1, Content f, Node node2)`:
+ * There are four cases:
+ * - The base case: A direct local assignment given by `storeStep`.
+ * - A call to a method or constructor with two arguments, `arg1` and `arg2`,
+ * such that the call has the side-effect `arg2.f = arg1`.
+ * - A call to a method that returns an object in which an argument has been
+ * stored.
+ * - A reverse step through a read when the result of the read has been
+ * stored into. This handles cases like `x.f1.f2 = y`.
+ * `storeViaSideEffect` covers the first two cases, and `storeReturn` covers
+ * the third case.
+ */
-import ImplCommon
+ /**
+ * Holds if data can flow from `node1` to `node2` via a direct assignment to
+ * `f` or via a call that acts as a setter.
+ */
+ cached
+ predicate store(Node node1, Content f, Node node2) {
+ storeViaSideEffect(node1, f, node2) or
+ storeReturn(node1, f, node2) or
+ read(node2.(PostUpdateNode).getPreUpdateNode(), f, node1.(PostUpdateNode).getPreUpdateNode())
+ }
-pragma[noinline]
-private predicate returnPosition(ReturnNode ret, DataFlowCallable c, ReturnKind kind) {
- c = returnNodeGetEnclosingCallable(ret) and
- kind = ret.getKind()
-}
+ private predicate storeViaSideEffect(Node node1, Content f, PostUpdateNode node2) {
+ storeStep(node1, f, node2) and readStep(_, f, _)
+ or
+ exists(DataFlowCall call, int i1, int i2 |
+ setterCall(call, i1, i2, f) and
+ node1.(ArgumentNode).argumentOf(call, i1) and
+ node2.getPreUpdateNode().(ArgumentNode).argumentOf(call, i2) and
+ compatibleTypes(node1.getTypeBound(), f.getType()) and
+ compatibleTypes(node2.getTypeBound(), f.getContainerType())
+ )
+ }
-/**
- * A call context to restrict the targets of virtual dispatch and match the
- * call sites of flow into a method with flow out of a method.
- *
- * There are four cases:
- * - `TAnyCallContext()` : No restrictions on method flow.
- * - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
- * parameter at the given `call`. This call improves the set of viable
- * dispatch targets for at least one method call in the current callable.
- * - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
- * originating call does not improve the set of dispatch targets for any
- * method call in the current callable and was therefore not recorded.
- * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
- * this dispatch target of `call` implies a reduced set of dispatch origins
- * to which data may flow if it should reach a `return` statement.
- */
-abstract class CallContext extends TCallContext {
- abstract string toString();
-}
+ pragma[nomagic]
+ private predicate setterInParam(ParameterNode p1, Content f, ParameterNode p2) {
+ exists(Node n1, PostUpdateNode n2 |
+ parameterValueFlowNoCtx(p1, n1) and
+ storeViaSideEffect(n1, f, n2) and
+ parameterValueFlowNoCtx(p2, n2.getPreUpdateNode()) and
+ p1 != p2
+ )
+ }
-class CallContextAny extends CallContext, TAnyCallContext {
- override string toString() { result = "CcAny" }
-}
+ pragma[nomagic]
+ private predicate setterCall(DataFlowCall call, int i1, int i2, Content f) {
+ exists(DataFlowCallable callable, ParameterNode p1, ParameterNode p2 |
+ setterInParam(p1, f, p2) and
+ callable = viableCallable(call) and
+ p1.isParameterOf(callable, i1) and
+ p2.isParameterOf(callable, i2)
+ )
+ }
-abstract class CallContextCall extends CallContext { }
+ pragma[noinline]
+ private predicate storeReturn0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
+ exists(ParameterNode p |
+ viableParamArg(call, p, arg) and
+ setterReturn(p, f, kind)
+ )
+ }
-class CallContextSpecificCall extends CallContextCall, TSpecificCall {
- override string toString() {
- exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
- result = "CcCall(" + call + ", " + i + ")"
+ private predicate storeReturn(Node node1, Content f, Node node2) {
+ exists(DataFlowCall call, ReturnKind kind |
+ storeReturn0(call, kind, node1, f) and
+ node2 = getAnOutNode(call, kind) and
+ compatibleTypes(node1.getTypeBound(), f.getType()) and
+ compatibleTypes(node2.getTypeBound(), f.getContainerType())
+ )
+ }
+
+ private predicate setterReturn(ParameterNode p, Content f, ReturnKind kind) {
+ exists(Node n1, Node n2 |
+ parameterValueFlowNoCtx(p, n1) and
+ store(n1, f, n2) and
+ localValueStep*(n2, getAReturnNodeOfKind(kind))
+ )
+ }
+
+ pragma[noinline]
+ private predicate read0(DataFlowCall call, ReturnKind kind, ArgumentNode arg, Content f) {
+ exists(ParameterNode p |
+ viableParamArg(call, p, arg) and
+ getter(p, f, kind)
+ )
+ }
+
+ /**
+ * Holds if data can flow from `node1` to `node2` via a direct read of `f` or
+ * via a getter.
+ */
+ cached
+ predicate read(Node node1, Content f, Node node2) {
+ readStep(node1, f, node2) and storeStep(_, f, _)
+ or
+ exists(DataFlowCall call, ReturnKind kind |
+ read0(call, kind, node1, f) and
+ node2 = getAnOutNode(call, kind) and
+ compatibleTypes(node1.getTypeBound(), f.getContainerType()) and
+ compatibleTypes(node2.getTypeBound(), f.getType())
+ )
+ }
+
+ private predicate getter(ParameterNode p, Content f, ReturnKind kind) {
+ exists(Node n1, Node n2 |
+ parameterValueFlowNoCtx(p, n1) and
+ read(n1, f, n2) and
+ localValueStep*(n2, getAReturnNodeOfKind(kind))
+ )
+ }
+
+ cached
+ predicate localStoreReadStep(Node node1, Node node2) {
+ exists(Node mid1, Node mid2, Content f |
+ store(node1, f, mid1) and
+ localValueStep*(mid1, mid2) and
+ read(mid2, f, node2) and
+ compatibleTypes(node1.getTypeBound(), node2.getTypeBound())
+ )
+ }
+
+ cached
+ module FlowThrough_v2 {
+ private predicate step(Node node1, Node node2) {
+ simpleLocalFlowStep(node1, node2) or
+ localStoreReadStep(node1, node2)
+ }
+
+ /**
+ * Holds if `p` can flow to `node` in the same callable using only
+ * value-preserving steps, not taking call contexts into account.
+ */
+ private predicate parameterValueFlowCand(ParameterNode p, Node node) {
+ p = node
+ or
+ exists(Node mid |
+ parameterValueFlowCand(p, mid) and
+ step(mid, node) and
+ compatibleTypes(p.getType(), node.getType())
+ )
+ or
+ // flow through a callable
+ exists(Node arg |
+ parameterValueFlowCand(p, arg) and
+ argumentValueFlowsThroughCand(arg, node) and
+ compatibleTypes(p.getType(), node.getType())
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to a return node of kind `kind` in the same
+ * callable using only value-preserving steps, not taking call contexts
+ * into account.
+ */
+ private predicate parameterValueFlowsThroughCand(ParameterNode p, ReturnKind kind) {
+ parameterValueFlowCand(p, getAReturnNodeOfKind(kind))
+ }
+
+ pragma[nomagic]
+ private predicate argumentValueFlowsThroughCand0(
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind
+ ) {
+ exists(ParameterNode param | viableParamArg(call, param, arg) |
+ parameterValueFlowsThroughCand(param, kind)
+ )
+ }
+
+ /**
+ * Holds if `arg` flows to `out` through a call using only value-preserving steps,
+ * not taking call contexts into account.
+ */
+ private predicate argumentValueFlowsThroughCand(ArgumentNode arg, OutNode out) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThroughCand0(call, arg, kind)
+ |
+ out = getAnOutNode(call, kind) and
+ compatibleTypes(arg.getType(), out.getType())
+ )
+ }
+
+ /**
+ * Holds if `arg` is the `i`th argument of `call` inside the callable
+ * `enclosing`, and `arg` may flow through `call`.
+ */
+ pragma[noinline]
+ private predicate argumentOf(
+ DataFlowCall call, int i, ArgumentNode arg, DataFlowCallable enclosing
+ ) {
+ arg.argumentOf(call, i) and
+ argumentValueFlowsThroughCand(arg, _) and
+ enclosing = arg.getEnclosingCallable()
+ }
+
+ pragma[noinline]
+ private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
+
+ pragma[noinline]
+ private predicate viableParamArg0(
+ int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
+ ) {
+ exists(DataFlowCallable c | argumentOf(call, i, arg, c) |
+ (
+ outercc = TAnyCallContext()
+ or
+ outercc = TSomeCall(getAParameter(c), _)
+ or
+ exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
+ recordDataFlowCallSite(other, c)
+ )
+ ) and
+ not isUnreachableInCall(arg, outercc.(CallContextSpecificCall).getCall())
+ )
+ }
+
+ pragma[noinline]
+ private predicate viableParamArg1(
+ ParameterNode p, DataFlowCallable callable, int i, ArgumentNode arg, CallContext outercc,
+ DataFlowCall call
+ ) {
+ viableParamArg0(i, arg, outercc, call) and
+ callable = resolveCall(call, outercc) and
+ p.isParameterOf(callable, any(int j | j <= i and j >= i))
+ }
+
+ /**
+ * Holds if `arg` is a possible argument to `p`, in the call `call`, and
+ * `arg` may flow through `call`. The possible contexts before and after
+ * entering the callable are `outercc` and `innercc`, respectively.
+ */
+ private predicate viableParamArg(
+ DataFlowCall call, ParameterNode p, ArgumentNode arg, CallContext outercc,
+ CallContextCall innercc
+ ) {
+ exists(int i, DataFlowCallable callable |
+ viableParamArg1(p, callable, i, arg, outercc, call)
+ |
+ if recordDataFlowCallSite(call, callable)
+ then innercc = TSpecificCall(call, i, true)
+ else innercc = TSomeCall(p, true)
+ )
+ }
+
+ private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
+ result = TSomeCall(p, _)
+ or
+ exists(DataFlowCall call, int i, DataFlowCallable callable |
+ result = TSpecificCall(call, i, _) and
+ p.isParameterOf(callable, i) and
+ recordDataFlowCallSite(call, callable)
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to `node` in the same callable using only
+ * value-preserving steps, in call context `cc`.
+ */
+ private predicate parameterValueFlow(ParameterNode p, Node node, CallContextCall cc) {
+ p = node and
+ parameterValueFlowsThroughCand(p, _) and
+ cc = getAValidCallContextForParameter(p)
+ or
+ exists(Node mid |
+ parameterValueFlow(p, mid, cc) and
+ step(mid, node) and
+ compatibleTypes(p.getType(), node.getType()) and
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
+ )
+ or
+ // flow through a callable
+ exists(Node arg |
+ parameterValueFlow(p, arg, cc) and
+ argumentValueFlowsThrough(arg, node, cc) and
+ compatibleTypes(p.getType(), node.getType()) and
+ not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall())
+ )
+ }
+
+ /**
+ * Holds if `p` can flow to a return node of kind `kind` in the same
+ * callable using only value-preserving steps, in call context `cc`.
+ */
+ cached
+ predicate parameterValueFlowsThrough(ParameterNode p, ReturnKind kind, CallContextCall cc) {
+ parameterValueFlow(p, getAReturnNodeOfKind(kind), cc)
+ }
+
+ pragma[nomagic]
+ private predicate argumentValueFlowsThrough0(
+ DataFlowCall call, ArgumentNode arg, ReturnKind kind, CallContext cc
+ ) {
+ exists(ParameterNode param, CallContext innercc |
+ viableParamArg(call, param, arg, cc, innercc) and
+ parameterValueFlowsThrough(param, kind, innercc)
+ )
+ }
+
+ /**
+ * Holds if `arg` flows to `out` through a call using only value-preserving steps,
+ * in call context cc.
+ */
+ cached
+ predicate argumentValueFlowsThrough(ArgumentNode arg, OutNode out, CallContext cc) {
+ exists(DataFlowCall call, ReturnKind kind |
+ argumentValueFlowsThrough0(call, arg, kind, cc)
+ |
+ out = getAnOutNode(call, kind) and
+ not isUnreachableInCall(out, cc.(CallContextSpecificCall).getCall()) and
+ compatibleTypes(arg.getType(), out.getType())
+ )
+ }
+ }
+
+ /**
+ * Holds if `call` passes an implicit or explicit instance argument, i.e., an
+ * expression that reaches a `this` parameter.
+ */
+ private predicate callHasInstanceArgument(DataFlowCall call) {
+ exists(ArgumentNode arg | arg.argumentOf(call, -1))
+ }
+
+ /**
+ * Holds if the call context `call` either improves virtual dispatch in
+ * `callable` or if it allows us to prune unreachable nodes in `callable`.
+ */
+ cached
+ predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
+ reducedViableImplInCallContext(_, callable, call)
+ or
+ exists(Node n | n.getEnclosingCallable() = callable | isUnreachableInCall(n, call))
+ }
+
+ cached
+ newtype TCallContext =
+ TAnyCallContext() or
+ TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
+ recordDataFlowCallSite(call, _) and
+ (emptyAp = true or emptyAp = false) and
+ (
+ exists(call.getArgument(i))
+ or
+ i = -1 and callHasInstanceArgument(call)
+ )
+ } or
+ TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
+ TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
+
+ cached
+ newtype TReturnPosition =
+ TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { returnPosition(_, c, kind) }
+
+ cached
+ newtype TLocalFlowCallContext =
+ TAnyLocalCall() or
+ TSpecificLocalCall(DataFlowCall call) { isUnreachableInCall(_, call) }
+ }
+
+ pragma[noinline]
+ private predicate returnPosition(ReturnNodeExt ret, DataFlowCallable c, ReturnKindExt kind) {
+ c = returnNodeGetEnclosingCallable(ret) and
+ kind = ret.getKind()
+ }
+
+ /**
+ * A call context to restrict the targets of virtual dispatch, prune local flow,
+ * and match the call sites of flow into a method with flow out of a method.
+ *
+ * There are four cases:
+ * - `TAnyCallContext()` : No restrictions on method flow.
+ * - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
+ * parameter at the given `call`. This call improves the set of viable
+ * dispatch targets for at least one method call in the current callable
+ * or helps prune unreachable nodes in the current callable.
+ * - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
+ * originating call does not improve the set of dispatch targets for any
+ * method call in the current callable and was therefore not recorded.
+ * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
+ * this dispatch target of `call` implies a reduced set of dispatch origins
+ * to which data may flow if it should reach a `return` statement.
+ */
+ abstract class CallContext extends TCallContext {
+ abstract string toString();
+
+ /** Holds if this call context is relevant for `callable`. */
+ abstract predicate relevantFor(DataFlowCallable callable);
+ }
+
+ class CallContextAny extends CallContext, TAnyCallContext {
+ override string toString() { result = "CcAny" }
+
+ override predicate relevantFor(DataFlowCallable callable) { any() }
+ }
+
+ abstract class CallContextCall extends CallContext { }
+
+ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
+ override string toString() {
+ exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
+ result = "CcCall(" + call + ", " + i + ")"
+ )
+ }
+
+ override predicate relevantFor(DataFlowCallable callable) {
+ recordDataFlowCallSite(getCall(), callable)
+ }
+
+ DataFlowCall getCall() { this = TSpecificCall(result, _, _) }
+ }
+
+ class CallContextSomeCall extends CallContextCall, TSomeCall {
+ override string toString() { result = "CcSomeCall" }
+
+ override predicate relevantFor(DataFlowCallable callable) {
+ exists(ParameterNode p | this = TSomeCall(p, _) and p.getEnclosingCallable() = callable)
+ }
+ }
+
+ class CallContextReturn extends CallContext, TReturn {
+ override string toString() {
+ exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
+ }
+
+ override predicate relevantFor(DataFlowCallable callable) {
+ exists(DataFlowCall call | this = TReturn(_, call) and call.getEnclosingCallable() = callable)
+ }
+ }
+
+ /**
+ * A call context that is relevant for pruning local flow.
+ */
+ abstract class LocalCallContext extends TLocalFlowCallContext {
+ abstract string toString();
+
+ /** Holds if this call context is relevant for `callable`. */
+ abstract predicate relevantFor(DataFlowCallable callable);
+ }
+
+ class LocalCallContextAny extends LocalCallContext, TAnyLocalCall {
+ override string toString() { result = "LocalCcAny" }
+
+ override predicate relevantFor(DataFlowCallable callable) { any() }
+ }
+
+ class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall {
+ LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) }
+
+ DataFlowCall call;
+
+ DataFlowCall getCall() { result = call }
+
+ override string toString() { result = "LocalCcCall(" + call + ")" }
+
+ override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) }
+ }
+
+ private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
+ exists(Node n | n.getEnclosingCallable() = callable and isUnreachableInCall(n, call))
+ }
+
+ /**
+ * Gets the local call context given the call context and the callable that
+ * the contexts apply to.
+ */
+ LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) {
+ ctx.relevantFor(callable) and
+ if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable)
+ then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall()
+ else result instanceof LocalCallContextAny
+ }
+
+ /**
+ * A node from which flow can return to the caller. This is either a regular
+ * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter.
+ */
+ class ReturnNodeExt extends Node {
+ ReturnNodeExt() {
+ this instanceof ReturnNode or
+ parameterValueFlowsToUpdate(_, this)
+ }
+
+ /** Gets the kind of this returned value. */
+ ReturnKindExt getKind() {
+ result = TValueReturn(this.(ReturnNode).getKind())
+ or
+ exists(ParameterNode p, int pos |
+ parameterValueFlowsToUpdate(p, this) and
+ p.isParameterOf(_, pos) and
+ result = TParamUpdate(pos)
+ )
+ }
+ }
+
+ private newtype TReturnKindExt =
+ TValueReturn(ReturnKind kind) or
+ TParamUpdate(int pos) {
+ exists(ParameterNode p | parameterValueFlowsToUpdate(p, _) and p.isParameterOf(_, pos))
+ }
+
+ /**
+ * An extended return kind. A return kind describes how data can be returned
+ * from a callable. This can either be through a returned value or an updated
+ * parameter.
+ */
+ abstract class ReturnKindExt extends TReturnKindExt {
+ /** Gets a textual representation of this return kind. */
+ abstract string toString();
+
+ /** Gets a node corresponding to data flow out of `call`. */
+ abstract Node getAnOutNode(DataFlowCall call);
+ }
+
+ class ValueReturnKind extends ReturnKindExt, TValueReturn {
+ private ReturnKind kind;
+
+ ValueReturnKind() { this = TValueReturn(kind) }
+
+ ReturnKind getKind() { result = kind }
+
+ override string toString() { result = kind.toString() }
+
+ override Node getAnOutNode(DataFlowCall call) { result = getAnOutNode(call, this.getKind()) }
+ }
+
+ class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
+ private int pos;
+
+ ParamUpdateReturnKind() { this = TParamUpdate(pos) }
+
+ int getPosition() { result = pos }
+
+ override string toString() { result = "param update " + pos }
+
+ override Node getAnOutNode(DataFlowCall call) {
+ exists(ArgumentNode arg |
+ result.(PostUpdateNode).getPreUpdateNode() = arg and
+ arg.argumentOf(call, this.getPosition())
+ )
+ }
+ }
+
+ /** A callable tagged with a relevant return kind. */
+ class ReturnPosition extends TReturnPosition0 {
+ private DataFlowCallable c;
+ private ReturnKindExt kind;
+
+ ReturnPosition() { this = TReturnPosition0(c, kind) }
+
+ /** Gets the callable. */
+ DataFlowCallable getCallable() { result = c }
+
+ /** Gets the return kind. */
+ ReturnKindExt getKind() { result = kind }
+
+ /** Gets a textual representation of this return position. */
+ string toString() { result = "[" + kind + "] " + c }
+ }
+
+ pragma[noinline]
+ DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) {
+ result = ret.getEnclosingCallable()
+ }
+
+ pragma[noinline]
+ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
+ exists(DataFlowCallable c, ReturnKindExt k | returnPosition(ret, c, k) |
+ result = TReturnPosition0(c, k)
)
}
-}
-class CallContextSomeCall extends CallContextCall, TSomeCall {
- override string toString() { result = "CcSomeCall" }
-}
+ bindingset[cc, callable]
+ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
+ cc instanceof CallContextAny and callable = viableCallable(call)
+ or
+ exists(DataFlowCallable c0, DataFlowCall call0 |
+ call0.getEnclosingCallable() = callable and
+ cc = TReturn(c0, call0) and
+ c0 = prunedViableImplInCallContextReverse(call0, call)
+ )
+ }
-class CallContextReturn extends CallContext, TReturn {
- override string toString() {
- exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
+ bindingset[call, cc]
+ DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
+ exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
+ if reducedViableImplInCallContext(call, _, ctx)
+ then result = prunedViableImplInCallContext(call, ctx)
+ else result = viableCallable(call)
+ )
+ or
+ result = viableCallable(call) and cc instanceof CallContextSomeCall
+ or
+ result = viableCallable(call) and cc instanceof CallContextAny
+ or
+ result = viableCallable(call) and cc instanceof CallContextReturn
}
}
-
-/** A callable tagged with a relevant return kind. */
-class ReturnPosition extends TReturnPosition0 {
- private DataFlowCallable c;
- private ReturnKind kind;
-
- ReturnPosition() { this = TReturnPosition0(c, kind) }
-
- /** Gets the callable. */
- DataFlowCallable getCallable() { result = c }
-
- /** Gets the return kind. */
- ReturnKind getKind() { result = kind }
-
- /** Gets a textual representation of this return position. */
- string toString() { result = "[" + kind + "] " + c }
-}
-
-pragma[noinline]
-DataFlowCallable returnNodeGetEnclosingCallable(ReturnNode ret) {
- result = ret.getEnclosingCallable()
-}
-
-pragma[noinline]
-ReturnPosition getReturnPosition(ReturnNode ret) {
- exists(DataFlowCallable c, ReturnKind k | returnPosition(ret, c, k) |
- result = TReturnPosition0(c, k)
- )
-}
-
-bindingset[cc, callable]
-predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
- cc instanceof CallContextAny and callable = viableCallable(call)
- or
- exists(DataFlowCallable c0, DataFlowCall call0 |
- call0.getEnclosingCallable() = callable and
- cc = TReturn(c0, call0) and
- c0 = prunedViableImplInCallContextReverse(call0, call)
- )
-}
-
-bindingset[call, cc]
-DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
- exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
- if reducedViableImplInCallContext(call, _, ctx)
- then result = prunedViableImplInCallContext(call, ctx)
- else result = viableCallable(call)
- )
- or
- result = viableCallable(call) and cc instanceof CallContextSomeCall
- or
- result = viableCallable(call) and cc instanceof CallContextAny
- or
- result = viableCallable(call) and cc instanceof CallContextReturn
-}
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 c31dc27151a..f6306759771 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
@@ -204,3 +204,5 @@ class DataFlowCall extends CallInstruction {
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
}
+
+predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
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 b5415f2bc65..24cfed86ba4 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
@@ -5,6 +5,7 @@
private import cpp
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.controlflow.IRGuards
+private import semmle.code.cpp.ir.ValueNumbering
/**
* A newtype wrapper to prevent accidental casts between `Node` and
@@ -220,7 +221,8 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
iTo.(CopyInstruction).getSourceValue() = iFrom or
iTo.(PhiInstruction).getAnOperand().getDef() = iFrom or
// Treat all conversions as flow, even conversions between different numeric types.
- iTo.(ConvertInstruction).getUnary() = iFrom
+ iTo.(ConvertInstruction).getUnary() = iFrom or
+ iTo.(InheritanceConversionInstruction).getUnary() = iFrom
}
/**
@@ -229,6 +231,14 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
*/
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
+/**
+ * Holds if data can flow from `i1` to `i2` in zero or more
+ * local (intra-procedural) steps.
+ */
+predicate localInstructionFlow(Instruction e1, Instruction e2) {
+ localFlow(instructionNode(e1), instructionNode(e2))
+}
+
/**
* Holds if data can flow from `e1` to `e2` in zero or more
* local (intra-procedural) steps.
@@ -236,7 +246,7 @@ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
/**
- * A guard that validates some expression.
+ * A guard that validates some instruction.
*
* To use this in a configuration, extend the class and provide a
* characteristic predicate precisely specifying the guard, and override
@@ -245,11 +255,15 @@ predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)
* It is important that all extending classes in scope are disjoint.
*/
class BarrierGuard extends IRGuardCondition {
- /** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `b`. */
- abstract deprecated predicate checks(Instruction e, boolean b);
+ /** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */
+ abstract predicate checks(Instruction instr, boolean b);
/** Gets a node guarded by this guard. */
final Node getAGuardedNode() {
- none() // stub
+ exists(ValueNumber value, boolean edge |
+ 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/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
index e34709e94ec..8d7c9194f4f 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
@@ -53,6 +53,14 @@ private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction no
*/
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
+/**
+ * Holds if taint can flow from `i1` to `i2` in zero or more
+ * local (intra-procedural) steps.
+ */
+predicate localInstructionTaint(Instruction i1, Instruction i2) {
+ localTaint(DataFlow::instructionNode(i1), DataFlow::instructionNode(i2))
+}
+
/**
* Holds if taint can flow from `e1` to `e2` in zero or more
* local (intra-procedural) steps.
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
new file mode 100644
index 00000000000..73cdbd20989
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll
@@ -0,0 +1,271 @@
+/**
+ * Minimal, language-neutral type system for the IR.
+ */
+
+private import internal.IRTypeInternal
+
+cached
+private newtype TIRType =
+ TIRVoidType() or
+ TIRUnknownType() or
+ TIRErrorType() { Language::hasErrorType() } or
+ TIRBooleanType(int byteSize) { Language::hasBooleanType(byteSize) } or
+ TIRSignedIntegerType(int byteSize) { Language::hasSignedIntegerType(byteSize) } or
+ TIRUnsignedIntegerType(int byteSize) { Language::hasUnsignedIntegerType(byteSize) } or
+ TIRFloatingPointType(int byteSize) { Language::hasFloatingPointType(byteSize) } or
+ TIRAddressType(int byteSize) { Language::hasAddressType(byteSize) } or
+ TIRFunctionAddressType(int byteSize) { Language::hasFunctionAddressType(byteSize) } or
+ TIROpaqueType(Language::OpaqueTypeTag tag, int byteSize) {
+ Language::hasOpaqueType(tag, byteSize)
+ }
+
+/**
+ * The language-neutral type of an IR `Instruction`, `Operand`, or `IRVariable`.
+ * The interface to `IRType` and its subclasses is the same across all languages for which the IR
+ * is supported, so analyses that expect to be used for multiple languages should generally use
+ * `IRType` rather than a language-specific type.
+ *
+ * Many types from the language-specific type system will map to a single canonical `IRType`. Two
+ * types that map to the same `IRType` are considered equivalent by the IR. As an example, in C++,
+ * all pointer types map to the same instance of `IRAddressType`.
+ */
+class IRType extends TIRType {
+ string toString() { none() }
+
+ /**
+ * Gets a string that uniquely identifies this `IRType`. This string is often the same as the
+ * result of `IRType.toString()`, but for some types it may be more verbose to ensure uniqueness.
+ */
+ string getIdentityString() { result = toString() }
+
+ /**
+ * Gets the size of the type, in bytes, if known.
+ *
+ * This will hold for all `IRType` objects except `IRUnknownType`.
+ */
+ // This predicate is overridden with `pragma[noinline]` in every leaf subclass.
+ // This allows callers to ask for things like _the_ floating-point type of
+ // size 4 without getting a join that first finds all types of size 4 and
+ // _then_ restricts them to floating-point types.
+ int getByteSize() { none() }
+
+ /**
+ * Gets a single instance of `LanguageType` that maps to this `IRType`.
+ */
+ Language::LanguageType getCanonicalLanguageType() { none() }
+}
+
+/**
+ * An unknown type. Generally used to represent results and operands that access an unknown set of
+ * memory locations, such as the side effects of a function call.
+ */
+class IRUnknownType extends IRType, TIRUnknownType {
+ final override string toString() { result = "unknown" }
+
+ final override int getByteSize() { none() }
+
+ final override Language::LanguageType getCanonicalLanguageType() {
+ result = Language::getCanonicalUnknownType()
+ }
+}
+
+/**
+ * A void type, which has no values. Used to represent the result type of an instruction that does
+ * not produce a result.
+ */
+class IRVoidType extends IRType, TIRVoidType {
+ final override string toString() { result = "void" }
+
+ final override int getByteSize() { result = 0 }
+
+ final override Language::LanguageType getCanonicalLanguageType() {
+ result = Language::getCanonicalVoidType()
+ }
+}
+
+/**
+ * An error type. Used when an error in the source code prevents the extractor from determining the
+ * proper type.
+ */
+class IRErrorType extends IRType, TIRErrorType {
+ final override string toString() { result = "error" }
+
+ final override int getByteSize() { result = 0 }
+
+ final override Language::LanguageType getCanonicalLanguageType() {
+ result = Language::getCanonicalErrorType()
+ }
+}
+
+private class IRSizedType extends IRType {
+ int byteSize;
+
+ IRSizedType() {
+ this = TIRBooleanType(byteSize) or
+ this = TIRSignedIntegerType(byteSize) or
+ this = TIRUnsignedIntegerType(byteSize) or
+ this = TIRFloatingPointType(byteSize) or
+ this = TIRAddressType(byteSize) or
+ this = TIRFunctionAddressType(byteSize) or
+ this = TIROpaqueType(_, byteSize)
+ }
+}
+
+/**
+ * A Boolean type, which can hold the values `true` (non-zero) or `false` (zero).
+ */
+class IRBooleanType extends IRSizedType, TIRBooleanType {
+ final override string toString() { result = "bool" + byteSize.toString() }
+
+ final override Language::LanguageType getCanonicalLanguageType() {
+ result = Language::getCanonicalBooleanType(byteSize)
+ }
+
+ pragma[noinline]
+ final override int getByteSize() { result = byteSize }
+}
+
+/**
+ * A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and
+ * `IRFloatingPointType`.
+ */
+class IRNumericType extends IRSizedType {
+ IRNumericType() {
+ this = TIRSignedIntegerType(byteSize) or
+ this = TIRUnsignedIntegerType(byteSize) or
+ this = TIRFloatingPointType(byteSize)
+ }
+}
+
+/**
+ * A signed two's-complement integer. Also used to represent enums whose underlying type is a signed
+ * integer, as well as character types whose representation is signed.
+ */
+class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType {
+ final override string toString() { result = "int" + byteSize.toString() }
+
+ final override Language::LanguageType getCanonicalLanguageType() {
+ result = Language::getCanonicalSignedIntegerType(byteSize)
+ }
+
+ pragma[noinline]
+ final override int getByteSize() { result = byteSize }
+}
+
+/**
+ * An unsigned two's-complement integer. Also used to represent enums whose underlying type is an
+ * unsigned integer, as well as character types whose representation is unsigned.
+ */
+class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType {
+ final override string toString() { result = "uint" + byteSize.toString() }
+
+ final override Language::LanguageType getCanonicalLanguageType() {
+ result = Language::getCanonicalUnsignedIntegerType(byteSize)
+ }
+
+ pragma[noinline]
+ final override int getByteSize() { result = byteSize }
+}
+
+/**
+ * A floating-point type.
+ */
+class IRFloatingPointType extends IRNumericType, TIRFloatingPointType {
+ final override string toString() { result = "float" + byteSize.toString() }
+
+ final override Language::LanguageType getCanonicalLanguageType() {
+ result = Language::getCanonicalFloatingPointType(byteSize)
+ }
+
+ pragma[noinline]
+ final override int getByteSize() { result = byteSize }
+}
+
+/**
+ * An address type, representing the memory address of data. Used to represent pointers, references,
+ * and lvalues, include those that are garbage collected.
+ *
+ * The address of a function is represented by the separate `IRFunctionAddressType`.
+ */
+class IRAddressType extends IRSizedType, TIRAddressType {
+ final override string toString() { result = "addr" + byteSize.toString() }
+
+ final override Language::LanguageType getCanonicalLanguageType() {
+ result = Language::getCanonicalAddressType(byteSize)
+ }
+
+ pragma[noinline]
+ final override int getByteSize() { result = byteSize }
+}
+
+/**
+ * An address type, representing the memory address of code. Used to represent function pointers,
+ * function references, and the target of a direct function call.
+ */
+class IRFunctionAddressType extends IRSizedType, TIRFunctionAddressType {
+ final override string toString() { result = "func" + byteSize.toString() }
+
+ final override Language::LanguageType getCanonicalLanguageType() {
+ result = Language::getCanonicalFunctionAddressType(byteSize)
+ }
+
+ pragma[noinline]
+ final override int getByteSize() { result = byteSize }
+}
+
+/**
+ * A type with known size that does not fit any of the other kinds of type. Used to represent
+ * classes, structs, unions, fixed-size arrays, pointers-to-member, and more.
+ */
+class IROpaqueType extends IRSizedType, TIROpaqueType {
+ Language::OpaqueTypeTag tag;
+
+ IROpaqueType() { this = TIROpaqueType(tag, byteSize) }
+
+ final override string toString() {
+ result = "opaque" + byteSize.toString() + "{" + tag.toString() + "}"
+ }
+
+ final override string getIdentityString() {
+ result = "opaque" + byteSize.toString() + "{" + Language::getOpaqueTagIdentityString(tag) + "}"
+ }
+
+ final override Language::LanguageType getCanonicalLanguageType() {
+ result = Language::getCanonicalOpaqueType(tag, byteSize)
+ }
+
+ /**
+ * Gets the "tag" that differentiates this type from other incompatible opaque types that have the
+ * same size.
+ */
+ final Language::OpaqueTypeTag getTag() { result = tag }
+
+ pragma[noinline]
+ final override int getByteSize() { result = byteSize }
+}
+
+module IRTypeSanity {
+ query predicate missingCanonicalLanguageType(IRType type, string message) {
+ not exists(type.getCanonicalLanguageType()) and
+ message = "Type does not have a canonical `LanguageType`"
+ }
+
+ query predicate multipleCanonicalLanguageTypes(IRType type, string message) {
+ strictcount(type.getCanonicalLanguageType()) > 1 and
+ message = "Type has multiple canonical `LanguageType`s: " +
+ concat(type.getCanonicalLanguageType().toString(), ", ")
+ }
+
+ query predicate missingIRType(Language::LanguageType type, string message) {
+ not exists(type.getIRType()) and
+ message = "`LanguageType` does not have a corresponding `IRType`."
+ }
+
+ query predicate multipleIRTypes(Language::LanguageType type, string message) {
+ strictcount(type.getIRType()) > 1 and
+ message = "`LanguageType` " + type.getAQlClass() + " has multiple `IRType`s: " +
+ concat(type.getIRType().toString(), ", ")
+ }
+
+ import Language::LanguageTypeSanity
+}
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 d4f1599d78e..a71608ee855 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll
@@ -5,6 +5,7 @@ private newtype TMemoryAccessKind =
TBufferMayMemoryAccess() or
TEscapedMemoryAccess() or
TEscapedMayMemoryAccess() or
+ TNonLocalMayMemoryAccess() or
TPhiMemoryAccess() or
TUnmodeledMemoryAccess() or
TChiTotalMemoryAccess() or
@@ -80,6 +81,14 @@ class EscapedMayMemoryAccess extends MemoryAccessKind, TEscapedMayMemoryAccess {
override string toString() { result = "escaped(may)" }
}
+/**
+ * The operand or result may access all memory whose address has escaped, other than data on the
+ * stack frame of the current function.
+ */
+class NonLocalMayMemoryAccess extends MemoryAccessKind, TNonLocalMayMemoryAccess {
+ override string toString() { result = "nonlocal(may)" }
+}
+
/**
* The operand is a Phi operand, which accesses the same memory as its
* definition.
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 a16b8eb88a0..0e4f800bb1b 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll
@@ -34,7 +34,7 @@ private newtype TOpcode =
TPointerSub() or
TPointerDiff() or
TConvert() or
- TConvertToBase() or
+ TConvertToNonVirtualBase() or
TConvertToVirtualBase() or
TConvertToDerived() or
TCheckedConvertOrNull() or
@@ -57,6 +57,7 @@ private newtype TOpcode =
TUnmodeledDefinition() or
TUnmodeledUse() or
TAliasedDefinition() or
+ TAliasedUse() or
TPhi() or
TBuiltIn() or
TVarArgsStart() or
@@ -66,11 +67,14 @@ private newtype TOpcode =
TCallSideEffect() or
TCallReadSideEffect() or
TIndirectReadSideEffect() or
- TIndirectWriteSideEffect() or
+ TIndirectMustWriteSideEffect() or
TIndirectMayWriteSideEffect() or
TBufferReadSideEffect() or
- TBufferWriteSideEffect() or
+ TBufferMustWriteSideEffect() or
TBufferMayWriteSideEffect() or
+ TSizedBufferReadSideEffect() or
+ TSizedBufferMustWriteSideEffect() or
+ TSizedBufferMayWriteSideEffect() or
TChi() or
TInlineAsm() or
TUnreached() or
@@ -106,6 +110,8 @@ abstract class RelationalOpcode extends CompareOpcode { }
abstract class CopyOpcode extends Opcode { }
+abstract class ConvertToBaseOpcode extends UnaryOpcode { }
+
abstract class MemoryAccessOpcode extends Opcode { }
abstract class ReturnOpcode extends Opcode { }
@@ -135,17 +141,28 @@ abstract class ReadSideEffectOpcode extends SideEffectOpcode { }
*/
abstract class WriteSideEffectOpcode extends SideEffectOpcode { }
+/**
+ * An opcode that definitely writes to a set of memory locations as a side effect.
+ */
+abstract class MustWriteSideEffectOpcode extends WriteSideEffectOpcode { }
+
/**
* An opcode that may overwrite some, all, or none of an existing set of memory locations. Modeled
* as a read of the original contents, plus a "may" write of the new contents.
*/
-abstract class MayWriteSideEffectOpcode extends SideEffectOpcode { }
+abstract class MayWriteSideEffectOpcode extends WriteSideEffectOpcode { }
/**
- * An opcode that accesses a buffer via an `AddressOperand` and a `BufferSizeOperand`.
+ * An opcode that accesses a buffer via an `AddressOperand`.
*/
abstract class BufferAccessOpcode extends MemoryAccessOpcode { }
+/**
+ * An opcode that accesses a buffer via an `AddressOperand` with a `BufferSizeOperand` specifying
+ * the number of elements accessed.
+ */
+abstract class SizedBufferAccessOpcode extends BufferAccessOpcode { }
+
module Opcode {
class NoOp extends Opcode, TNoOp {
final override string toString() { result = "NoOp" }
@@ -287,11 +304,11 @@ module Opcode {
final override string toString() { result = "Convert" }
}
- class ConvertToBase extends UnaryOpcode, TConvertToBase {
- final override string toString() { result = "ConvertToBase" }
+ class ConvertToNonVirtualBase extends ConvertToBaseOpcode, TConvertToNonVirtualBase {
+ final override string toString() { result = "ConvertToNonVirtualBase" }
}
- class ConvertToVirtualBase extends UnaryOpcode, TConvertToVirtualBase {
+ class ConvertToVirtualBase extends ConvertToBaseOpcode, TConvertToVirtualBase {
final override string toString() { result = "ConvertToVirtualBase" }
}
@@ -379,6 +396,10 @@ module Opcode {
final override string toString() { result = "AliasedDefinition" }
}
+ class AliasedUse extends Opcode, TAliasedUse {
+ final override string toString() { result = "AliasedUse" }
+ }
+
class Phi extends Opcode, TPhi {
final override string toString() { result = "Phi" }
}
@@ -416,9 +437,9 @@ module Opcode {
final override string toString() { result = "IndirectReadSideEffect" }
}
- class IndirectWriteSideEffect extends WriteSideEffectOpcode, MemoryAccessOpcode,
- TIndirectWriteSideEffect {
- final override string toString() { result = "IndirectWriteSideEffect" }
+ class IndirectMustWriteSideEffect extends MustWriteSideEffectOpcode, MemoryAccessOpcode,
+ TIndirectMustWriteSideEffect {
+ final override string toString() { result = "IndirectMustWriteSideEffect" }
}
class IndirectMayWriteSideEffect extends MayWriteSideEffectOpcode, MemoryAccessOpcode,
@@ -430,9 +451,9 @@ module Opcode {
final override string toString() { result = "BufferReadSideEffect" }
}
- class BufferWriteSideEffect extends WriteSideEffectOpcode, BufferAccessOpcode,
- TBufferWriteSideEffect {
- final override string toString() { result = "BufferWriteSideEffect" }
+ class BufferMustWriteSideEffect extends MustWriteSideEffectOpcode, BufferAccessOpcode,
+ TBufferMustWriteSideEffect {
+ final override string toString() { result = "BufferMustWriteSideEffect" }
}
class BufferMayWriteSideEffect extends MayWriteSideEffectOpcode, BufferAccessOpcode,
@@ -440,6 +461,21 @@ module Opcode {
final override string toString() { result = "BufferMayWriteSideEffect" }
}
+ class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferAccessOpcode,
+ TSizedBufferReadSideEffect {
+ final override string toString() { result = "SizedBufferReadSideEffect" }
+ }
+
+ class SizedBufferMustWriteSideEffect extends MustWriteSideEffectOpcode, SizedBufferAccessOpcode,
+ TSizedBufferMustWriteSideEffect {
+ final override string toString() { result = "SizedBufferMustWriteSideEffect" }
+ }
+
+ class SizedBufferMayWriteSideEffect extends MayWriteSideEffectOpcode, SizedBufferAccessOpcode,
+ TSizedBufferMayWriteSideEffect {
+ final override string toString() { result = "SizedBufferMayWriteSideEffect" }
+ }
+
class Chi extends Opcode, TChi {
final override string toString() { result = "Chi" }
}
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 278040f8ab8..badd48552a5 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
@@ -5,6 +5,7 @@ import IRVariable
import Operand
private import internal.IRImports as Imports
import Imports::EdgeKind
+import Imports::IRType
import Imports::MemoryAccessKind
private newtype TIRPropertyProvider = MkIRPropertyProvider()
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll
index 3921472dc8e..de66a3c99dc 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll
@@ -1,2 +1,3 @@
private import IR
import InstructionSanity
+import IRTypeSanity
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 a7ca4593ac4..e10e266b2ab 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
@@ -5,6 +5,7 @@ import Imports::TempVariableTag
private import Imports::IRUtilities
private import Imports::TTempVariableTag
private import Imports::TIRVariable
+private import Imports::IRType
IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
result.getVariable() = var and
@@ -21,10 +22,26 @@ abstract class IRVariable extends TIRVariable {
abstract string toString();
+ /**
+ * Holds if this variable's value cannot be changed within a function. Currently used for string
+ * literals, but could also apply to `const` global and static variables.
+ */
+ predicate isReadOnly() { none() }
+
/**
* Gets the type of the variable.
*/
- abstract Language::Type getType();
+ final Language::Type getType() { getLanguageType().hasType(result, false) }
+
+ /**
+ * Gets the language-neutral type of the variable.
+ */
+ final IRType getIRType() { result = getLanguageType().getIRType() }
+
+ /**
+ * Gets the type of the variable.
+ */
+ abstract Language::LanguageType getLanguageType();
/**
* Gets the AST node that declared this variable, or that introduced this
@@ -59,7 +76,7 @@ abstract class IRVariable extends TIRVariable {
*/
class IRUserVariable extends IRVariable, TIRUserVariable {
Language::Variable var;
- Language::Type type;
+ Language::LanguageType type;
IRUserVariable() { this = TIRUserVariable(var, type, func) }
@@ -71,7 +88,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
result = getVariable().toString() + " " + getVariable().getLocation().toString()
}
- final override Language::Type getType() { result = type }
+ final override Language::LanguageType getLanguageType() { result = type }
/**
* Gets the original user-declared variable.
@@ -102,34 +119,43 @@ class IRStaticUserVariable extends IRUserVariable {
final override Language::StaticVariable getVariable() { result = var }
}
+abstract class IRGeneratedVariable extends IRVariable {
+ Language::AST ast;
+ Language::LanguageType type;
+
+ final override Language::LanguageType getLanguageType() { result = type }
+
+ final override Language::AST getAST() { result = ast }
+
+ override string toString() { result = getBaseString() + getLocationString() }
+
+ override string getUniqueId() { none() }
+
+ final string getLocationString() {
+ result = ast.getLocation().getStartLine().toString() + ":" +
+ ast.getLocation().getStartColumn().toString()
+ }
+
+ string getBaseString() { none() }
+}
+
IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
result.getAST() = ast and
result.getTag() = tag
}
-class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable {
- Language::AST ast;
+class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
TempVariableTag tag;
- Language::Type type;
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
- final override Language::Type getType() { result = type }
-
- final override Language::AST getAST() { result = ast }
-
final override string getUniqueId() {
result = "Temp: " + Construction::getTempVariableUniqueId(this)
}
final TempVariableTag getTag() { result = tag }
- override string toString() {
- result = getBaseString() + ast.getLocation().getStartLine().toString() + ":" +
- ast.getLocation().getStartColumn().toString()
- }
-
- string getBaseString() { result = "#temp" }
+ override string getBaseString() { result = "#temp" }
}
class IRReturnVariable extends IRTempVariable {
@@ -143,3 +169,19 @@ class IRThrowVariable extends IRTempVariable {
override string getBaseString() { result = "#throw" }
}
+
+class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
+ Language::StringLiteral literal;
+
+ IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
+
+ final override predicate isReadOnly() { any() }
+
+ final override string getUniqueId() {
+ result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
+ }
+
+ override string getBaseString() { result = "#string" }
+
+ final Language::StringLiteral getLiteral() { result = literal }
+}
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 43f9663d2cd..e3c78f476e9 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
@@ -5,6 +5,7 @@ import IRVariable
import Operand
private import internal.InstructionImports as Imports
import Imports::EdgeKind
+import Imports::IRType
import Imports::MemoryAccessKind
import Imports::Opcode
private import Imports::OperandTag
@@ -30,7 +31,7 @@ module InstructionSanity {
or
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
or
- opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand
+ opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
or
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
or
@@ -48,8 +49,9 @@ module InstructionSanity {
or
(
opcode instanceof ReadSideEffectOpcode or
- opcode instanceof MayWriteSideEffectOpcode or
- opcode instanceof Opcode::InlineAsm
+ opcode instanceof Opcode::InlineAsm or
+ opcode instanceof Opcode::CallSideEffect or
+ opcode instanceof Opcode::AliasedUse
) and
tag instanceof SideEffectOperandTag
)
@@ -113,13 +115,34 @@ module InstructionSanity {
}
query predicate missingOperandType(Operand operand, string message) {
- exists(Language::Function func |
+ exists(Language::Function func, Instruction use |
not exists(operand.getType()) and
- func = operand.getUse().getEnclosingFunction() and
- message = "Operand missing type in function '" + Language::getIdentityString(func) + "'."
+ use = operand.getUse() and
+ func = use.getEnclosingFunction() and
+ message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString()
+ + "' missing type in function '" + Language::getIdentityString(func) + "'."
)
}
+ query predicate duplicateChiOperand(
+ ChiInstruction chi, string message, IRFunction func, string funcText
+ ) {
+ 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())
+ }
+
+ query predicate sideEffectWithoutPrimary(
+ SideEffectInstruction instr, string message, IRFunction func, string funcText
+ ) {
+ not exists(instr.getPrimaryInstruction()) and
+ message = "Side effect instruction missing primary instruction in function $@" and
+ func = instr.getEnclosingIRFunction() and
+ funcText = Language::getIdentityString(func.getFunction())
+ }
+
/**
* Holds if an instruction, other than `ExitFunction`, has no successors.
*/
@@ -251,6 +274,7 @@ module InstructionSanity {
) {
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
not useOperand.getUse() instanceof UnmodeledUseInstruction and
+ not defInstr instanceof UnmodeledDefinitionInstruction and
pointOfEvaluation(useOperand, useBlock, useIndex) and
defInstr = useOperand.getAnyDef() and
(
@@ -312,7 +336,7 @@ class Instruction extends Construction::TInstruction {
}
private string getResultPrefix() {
- if getResultType() instanceof Language::VoidType
+ if getResultIRType() instanceof IRVoidType
then result = "v"
else
if hasMemoryResult()
@@ -344,23 +368,6 @@ class Instruction extends Construction::TInstruction {
)
}
- bindingset[type]
- private string getValueCategoryString(string type) {
- if isGLValue() then result = "glval<" + type + ">" else result = type
- }
-
- string getResultTypeString() {
- exists(string valcat |
- valcat = getValueCategoryString(getResultType().toString()) and
- if
- getResultType() instanceof Language::UnknownType and
- not isGLValue() and
- exists(getResultSize())
- then result = valcat + "[" + getResultSize().toString() + "]"
- else result = valcat
- )
- }
-
/**
* Gets a human-readable string that uniquely identifies this instruction
* within the function. This string is used to refer to this instruction when
@@ -380,7 +387,9 @@ class Instruction extends Construction::TInstruction {
*
* Example: `r1_1(int*)`
*/
- final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" }
+ final string getResultString() {
+ result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
+ }
/**
* Gets a string describing the operands of this instruction, suitable for
@@ -448,6 +457,16 @@ class Instruction extends Construction::TInstruction {
result = Construction::getInstructionUnconvertedResultExpression(this)
}
+ final Language::LanguageType getResultLanguageType() {
+ result = Construction::getInstructionResultType(this)
+ }
+
+ /**
+ * Gets the type of the result produced by this instruction. If the instruction does not produce
+ * a result, its result type will be `IRVoidType`.
+ */
+ final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
+
/**
* Gets the type of the result produced by this instruction. If the
* instruction does not produce a result, its result type will be `VoidType`.
@@ -455,7 +474,16 @@ class Instruction extends Construction::TInstruction {
* If `isGLValue()` holds, then the result type of this instruction should be
* thought of as "pointer to `getResultType()`".
*/
- final Language::Type getResultType() { Construction::instructionHasType(this, result, _) }
+ final Language::Type getResultType() {
+ exists(Language::LanguageType resultType |
+ resultType = getResultLanguageType() and
+ (
+ resultType.hasUnspecifiedType(result, _)
+ or
+ not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType
+ )
+ )
+ }
/**
* Holds if the result produced by this instruction is a glvalue. If this
@@ -475,7 +503,7 @@ class Instruction extends Construction::TInstruction {
* result of the `Load` instruction is a prvalue of type `int`, representing
* the integer value loaded from variable `x`.
*/
- final predicate isGLValue() { Construction::instructionHasType(this, _, true) }
+ final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
/**
* Gets the size of the result produced by this instruction, in bytes. If the
@@ -484,16 +512,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() {
- if isGLValue()
- then
- // a glvalue is always pointer-sized.
- result = Language::getPointerSize()
- else
- if getResultType() instanceof Language::UnknownType
- then result = Construction::getInstructionResultSize(this)
- else result = Language::getTypeSize(getResultType())
- }
+ final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
/**
* Gets the opcode that specifies the operation performed by this instruction.
@@ -609,9 +628,14 @@ class VariableInstruction extends Instruction {
VariableInstruction() { var = Construction::getInstructionVariable(this) }
- final override string getImmediateString() { result = var.toString() }
+ override string getImmediateString() { result = var.toString() }
- final IRVariable getVariable() { result = var }
+ final IRVariable getIRVariable() { result = var }
+
+ /**
+ * Gets the AST variable that this instruction's IR variable refers to, if one exists.
+ */
+ final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
}
class FieldInstruction extends Instruction {
@@ -644,6 +668,16 @@ class ConstantValueInstruction extends Instruction {
final string getValue() { result = value }
}
+class IndexedInstruction extends Instruction {
+ int index;
+
+ IndexedInstruction() { index = Construction::getInstructionIndex(this) }
+
+ final override string getImmediateString() { result = index.toString() }
+
+ final int getIndex() { result = index }
+}
+
class EnterFunctionInstruction extends Instruction {
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
}
@@ -784,14 +818,12 @@ class FloatConstantInstruction extends ConstantInstruction {
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
}
-class StringConstantInstruction extends Instruction {
- Language::StringLiteral value;
+class StringConstantInstruction extends VariableInstruction {
+ override IRStringLiteral var;
- StringConstantInstruction() { value = Construction::getInstructionStringLiteral(this) }
+ final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) }
- final override string getImmediateString() { result = Language::getStringLiteralText(value) }
-
- final Language::StringLiteral getValue() { result = value }
+ final Language::StringLiteral getValue() { result = var.getLiteral() }
}
class BinaryInstruction extends Instruction {
@@ -959,14 +991,22 @@ class InheritanceConversionInstruction extends UnaryInstruction {
* to the address of a direct non-virtual base class.
*/
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
- ConvertToBaseInstruction() { getOpcode() instanceof Opcode::ConvertToBase }
+ 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.
+ */
+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.
*/
-class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction {
+class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
}
@@ -1175,6 +1215,8 @@ class CallReadSideEffectInstruction extends SideEffectInstruction {
*/
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
}
/**
@@ -1182,13 +1224,39 @@ class IndirectReadSideEffectInstruction extends SideEffectInstruction {
*/
class BufferReadSideEffectInstruction extends SideEffectInstruction {
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
+}
+
+/**
+ * An instruction representing the read of an indirect buffer parameter within a function call.
+ */
+class SizedBufferReadSideEffectInstruction extends SideEffectInstruction {
+ SizedBufferReadSideEffectInstruction() {
+ getOpcode() instanceof Opcode::SizedBufferReadSideEffect
+ }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
+
+ Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+}
+
+/**
+ * An instruction representing a side effect of a function call.
+ */
+class WriteSideEffectInstruction extends SideEffectInstruction {
+ WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
}
/**
* An instruction representing the write of an indirect parameter within a function call.
*/
-class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
- IndirectWriteSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectWriteSideEffect }
+class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
+ IndirectMustWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
+ }
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
}
@@ -1197,18 +1265,34 @@ class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
* An instruction representing the write of an indirect buffer parameter within a function call. The
* entire buffer is overwritten.
*/
-class BufferWriteSideEffectInstruction extends SideEffectInstruction {
- BufferWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferWriteSideEffect }
+class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
+ BufferMustWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::BufferMustWriteSideEffect
+ }
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
}
+/**
+ * An instruction representing the write of an indirect buffer parameter within a function call. The
+ * entire buffer is overwritten.
+ */
+class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
+ SizedBufferMustWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
+ }
+
+ final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
+
+ Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).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.
*/
-class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
+class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
IndirectMayWriteSideEffectInstruction() {
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
}
@@ -1222,7 +1306,7 @@ class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
* 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 SideEffectInstruction {
+class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
final override MemoryAccessKind getResultMemoryAccess() {
@@ -1230,6 +1314,22 @@ class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
}
}
+/**
+ * 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 {
+ SizedBufferMayWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
+ }
+
+ final override MemoryAccessKind getResultMemoryAccess() {
+ result instanceof BufferMayMemoryAccess
+ }
+
+ Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+}
+
/**
* An instruction representing a GNU or MSVC inline assembly statement.
*/
@@ -1300,7 +1400,7 @@ class CatchInstruction extends Instruction {
* An instruction that catches an exception of a specific type.
*/
class CatchByTypeInstruction extends CatchInstruction {
- Language::Type exceptionType;
+ Language::LanguageType exceptionType;
CatchByTypeInstruction() {
getOpcode() instanceof Opcode::CatchByType and
@@ -1312,7 +1412,7 @@ class CatchByTypeInstruction extends CatchInstruction {
/**
* Gets the type of exception to be caught.
*/
- final Language::Type getExceptionType() { result = exceptionType }
+ final Language::LanguageType getExceptionType() { result = exceptionType }
}
/**
@@ -1339,6 +1439,13 @@ class AliasedDefinitionInstruction extends Instruction {
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
}
+/**
+ * An instruction that consumes all escaped memory on exit from the function.
+ */
+class AliasedUseInstruction extends Instruction {
+ AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
+}
+
class UnmodeledUseInstruction extends Instruction {
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
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 1ced8ef3282..07dd107bf12 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,9 +1,10 @@
private import internal.IRInternal
-import Instruction
-import IRBlock
+private import Instruction
+private import IRBlock
private import internal.OperandImports as Imports
-import Imports::MemoryAccessKind
-import Imports::Overlap
+private import Imports::MemoryAccessKind
+private import Imports::IRType
+private import Imports::Overlap
private import Imports::OperandTag
cached
@@ -143,22 +144,40 @@ class Operand extends TOperand {
* the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
- Language::Type getType() { result = getAnyDef().getResultType() }
+ Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() }
+
+ /**
+ * Gets the language-neutral type of the value consumed by this operand. This is usually the same
+ * as the result type of the definition instruction consumed by this operand. For register
+ * operands, this is always the case. For some memory operands, the operand type may be different
+ * from the definition type, such as in the case of a partial read or a read from a pointer that
+ * has been cast to a different type.
+ */
+ final IRType getIRType() { result = getLanguageType().getIRType() }
+
+ /**
+ * Gets the type of the value consumed by this operand. This is usually the same as the
+ * result type of the definition instruction consumed by this operand. For register operands,
+ * this is always the case. For some memory operands, the operand type may be different from
+ * the definition type, such as in the case of a partial read or a read from a pointer that
+ * has been cast to a different type.
+ */
+ final Language::Type getType() { getLanguageType().hasType(result, _) }
/**
* Holds if the value consumed by this operand is a glvalue. If this
* holds, the value of the operand represents the address of a location,
* and the type of the location is given by `getType()`. If this does
* not hold, the value of the operand represents a value whose type is
- * given by `getResultType()`.
+ * given by `getType()`.
*/
- predicate isGLValue() { getAnyDef().isGLValue() }
+ final predicate isGLValue() { getLanguageType().hasType(_, true) }
/**
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
* a known constant size, this predicate does not hold.
*/
- int getSize() { result = Language::getTypeSize(getType()) }
+ final int getSize() { result = getLanguageType().getByteSize() }
}
/**
@@ -170,11 +189,6 @@ class MemoryOperand extends Operand {
this = TPhiOperand(_, _, _, _)
}
- override predicate isGLValue() {
- // A `MemoryOperand` can never be a glvalue
- none()
- }
-
/**
* Gets the kind of memory access performed by the operand.
*/
@@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe
class TypedOperand extends NonPhiMemoryOperand {
override TypedOperandTag tag;
- final override Language::Type getType() {
+ final override Language::LanguageType getLanguageType() {
result = Construction::getInstructionOperandType(useInstr, tag)
}
}
@@ -254,6 +268,16 @@ class AddressOperand extends RegisterOperand {
override string toString() { result = "Address" }
}
+/**
+ * The buffer size operand of an instruction that represents a read or write of
+ * a buffer.
+ */
+class BufferSizeOperand extends RegisterOperand {
+ override BufferSizeOperandTag tag;
+
+ override string toString() { result = "BufferSize" }
+}
+
/**
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
* `ReturnValue`, `ThrowValue`).
@@ -371,13 +395,10 @@ class PositionalArgumentOperand extends ArgumentOperand {
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
- final override int getSize() {
- if getType() instanceof Language::UnknownType
- then result = Construction::getInstructionOperandSize(useInstr, tag)
- else result = Language::getTypeSize(getType())
- }
-
override MemoryAccessKind getMemoryAccess() {
+ useInstr instanceof AliasedUseInstruction and
+ result instanceof NonLocalMayMemoryAccess
+ or
useInstr instanceof CallSideEffectInstruction and
result instanceof EscapedMayMemoryAccess
or
@@ -390,10 +411,10 @@ class SideEffectOperand extends TypedOperand {
useInstr instanceof BufferReadSideEffectInstruction and
result instanceof BufferMemoryAccess
or
- useInstr instanceof IndirectWriteSideEffectInstruction and
+ useInstr instanceof IndirectMustWriteSideEffectInstruction and
result instanceof IndirectMemoryAccess
or
- useInstr instanceof BufferWriteSideEffectInstruction and
+ useInstr instanceof BufferMustWriteSideEffectInstruction and
result instanceof BufferMemoryAccess
or
useInstr instanceof IndirectMayWriteSideEffectInstruction and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll
index e3e52df8931..79e5a2b6713 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll
@@ -1,5 +1,5 @@
private import internal.ValueNumberingInternal
-private import cpp
+private import internal.ValueNumberingImports
private import IR
/**
@@ -23,31 +23,32 @@ newtype TValueNumber =
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
- TConstantValueNumber(IRFunction irFunc, Type type, string value) {
+ TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
- TStringConstantValueNumber(IRFunction irFunc, Type type, string value) {
+ TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
stringConstantValueNumber(_, irFunc, type, value)
} or
- TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) {
+ TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) {
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
- IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand
+ IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
- IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
+ IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
ValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
} or
- TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) {
+ TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, type, operand)
} or
TInheritanceConversionValueNumber(
- IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand
+ IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
+ ValueNumber operand
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
@@ -59,7 +60,7 @@ newtype TValueNumber =
class ValueNumber extends TValueNumber {
final string toString() { result = getExampleInstruction().getResultId() }
- final Location getLocation() { result = getExampleInstruction().getLocation() }
+ final Language::Location getLocation() { result = getExampleInstruction().getLocation() }
/**
* Gets the instructions that have been assigned this value number. This will always produce at
@@ -135,14 +136,14 @@ private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getVariable() = var
+ instr.getIRVariable() = var
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getVariable() = var
+ instr.getIRVariable() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
@@ -150,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
}
private predicate constantValueNumber(
- ConstantInstruction instr, IRFunction irFunc, Type type, string value
+ ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
instr.getValue() = value
}
private predicate stringConstantValueNumber(
- StringConstantInstruction instr, IRFunction irFunc, Type type, string value
+ StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
private predicate fieldAddressValueNumber(
- FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress
+ FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
@@ -174,43 +175,43 @@ private predicate fieldAddressValueNumber(
}
private predicate binaryValueNumber(
- BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand,
+ BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand,
ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
- PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize,
- ValueNumber leftOperand, ValueNumber rightOperand
+ PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
+ int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
instr.getElementSize() = elementSize and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
- UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand
+ UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
instr.getOpcode() = opcode and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
valueNumber(instr.getUnary()) = operand
}
private predicate inheritanceConversionValueNumber(
- InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass,
- Class derivedClass, ValueNumber operand
+ InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
+ Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
@@ -225,7 +226,7 @@ private predicate inheritanceConversionValueNumber(
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
- not instr.getResultType() instanceof VoidType and
+ not instr.getResultIRType() instanceof IRVoidType and
not numberableInstruction(instr)
}
@@ -269,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) {
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
- exists(Type type, string value |
+ exists(IRType type, string value |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
- exists(Type type, string value |
+ exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
- exists(Field field, ValueNumber objectAddress |
+ exists(Language::Field field, ValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
- exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
+ exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
)
or
- exists(Opcode opcode, Type type, ValueNumber operand |
+ exists(Opcode opcode, IRType type, ValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, type, operand) and
result = TUnaryValueNumber(irFunc, opcode, type, operand)
)
or
- exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand |
+ exists(
+ Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
+ |
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(
- Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
+ Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
+ ValueNumber rightOperand
|
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
rightOperand) and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll
new file mode 100644
index 00000000000..0282f6887f1
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll
@@ -0,0 +1,2 @@
+import semmle.code.cpp.ir.internal.Overlap
+import semmle.code.cpp.ir.internal.IRCppLanguage as Language
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 dc50ef01354..6ecfd1470bb 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
@@ -109,7 +109,7 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
instr = operand.getUse() and
(
// Converting to a non-virtual base class adds the offset of the base class.
- exists(ConvertToBaseInstruction convert |
+ exists(ConvertToNonVirtualBaseInstruction convert |
convert = instr and
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
)
@@ -282,7 +282,7 @@ private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
// The variable's address escapes if the result of any
// VariableAddressInstruction that computes the variable's address escapes.
exists(VariableAddressInstruction instr |
- instr.getVariable() = var and
+ instr.getIRVariable() = var and
resultEscapesNonReturn(instr)
)
}
@@ -305,7 +305,11 @@ predicate variableAddressEscapes(IRVariable var) {
*/
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
// The address of a variable points to that variable, at offset 0.
- instr.(VariableAddressInstruction).getVariable() = var and
+ instr.(VariableAddressInstruction).getIRVariable() = var and
+ bitOffset = 0
+ or
+ // A string literal is just a special read-only global variable.
+ instr.(StringConstantInstruction).getIRVariable() = var and
bitOffset = 0
or
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
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 95ddffb2274..0835ffb7da6 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
@@ -1,6 +1,6 @@
-private import cpp
import AliasAnalysis
import semmle.code.cpp.ir.internal.Overlap
+private import semmle.code.cpp.ir.internal.IRCppLanguage as Language
private import semmle.code.cpp.Print
private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
@@ -10,31 +10,42 @@ private import semmle.code.cpp.ir.implementation.internal.OperandTag
private class IntValue = Ints::IntValue;
private predicate hasResultMemoryAccess(
- Instruction instr, IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset
+ Instruction instr, IRVariable var, IRType type, Language::LanguageType languageType,
+ IntValue startBitOffset, IntValue endBitOffset
) {
resultPointsTo(instr.getResultAddress(), var, startBitOffset) and
- type = instr.getResultType() and
- if exists(instr.getResultSize())
- then endBitOffset = Ints::add(startBitOffset, Ints::mul(instr.getResultSize(), 8))
+ languageType = instr.getResultLanguageType() and
+ type = languageType.getIRType() and
+ if exists(type.getByteSize())
+ then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
else endBitOffset = Ints::unknown()
}
private predicate hasOperandMemoryAccess(
- MemoryOperand operand, IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset
+ MemoryOperand operand, IRVariable var, IRType type, Language::LanguageType languageType,
+ IntValue startBitOffset, IntValue endBitOffset
) {
resultPointsTo(operand.getAddressOperand().getAnyDef(), var, startBitOffset) and
- type = operand.getType() and
- if exists(operand.getSize())
- then endBitOffset = Ints::add(startBitOffset, Ints::mul(operand.getSize(), 8))
+ languageType = operand.getLanguageType() and
+ type = languageType.getIRType() and
+ if exists(type.getByteSize())
+ then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
else endBitOffset = Ints::unknown()
}
private newtype TMemoryLocation =
- TVariableMemoryLocation(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset) {
- hasResultMemoryAccess(_, var, type, startBitOffset, endBitOffset) or
- hasOperandMemoryAccess(_, var, type, startBitOffset, endBitOffset)
+ TVariableMemoryLocation(
+ IRVariable var, IRType type, Language::LanguageType languageType, IntValue startBitOffset,
+ IntValue endBitOffset
+ ) {
+ (
+ hasResultMemoryAccess(_, var, type, _, startBitOffset, endBitOffset) or
+ hasOperandMemoryAccess(_, var, type, _, startBitOffset, endBitOffset)
+ ) and
+ languageType = type.getCanonicalLanguageType()
} or
TUnknownMemoryLocation(IRFunction irFunc) or
+ TUnknownNonLocalMemoryLocation(IRFunction irFunc) or
TUnknownVirtualVariable(IRFunction irFunc)
/**
@@ -49,9 +60,11 @@ abstract class MemoryLocation extends TMemoryLocation {
abstract VirtualVariable getVirtualVariable();
- abstract Type getType();
+ abstract Language::LanguageType getType();
abstract string getUniqueId();
+
+ final IRType getIRType() { result = getType().getIRType() }
}
abstract class VirtualVariable extends MemoryLocation { }
@@ -62,20 +75,34 @@ abstract class VirtualVariable extends MemoryLocation { }
*/
class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
IRVariable var;
- Type type;
+ IRType type;
+ Language::LanguageType languageType;
IntValue startBitOffset;
IntValue endBitOffset;
VariableMemoryLocation() {
- this = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
+ this = TVariableMemoryLocation(var, type, languageType, startBitOffset, endBitOffset)
}
final override string toString() {
result = var.toString() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" +
- type.toString() + ">"
+ type.toString() + ", " + languageType.toString() + ">"
}
- final override Type getType() { result = type }
+ final override Language::LanguageType getType() {
+ if
+ strictcount(Language::LanguageType accessType |
+ hasResultMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset) or
+ hasOperandMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset)
+ ) = 1
+ then
+ // All of the accesses have the same `LanguageType`, so just use that.
+ hasResultMemoryAccess(_, var, type, result, startBitOffset, endBitOffset) or
+ hasOperandMemoryAccess(_, var, type, result, startBitOffset, endBitOffset)
+ else
+ // There is no single type for all accesses, so just use the canonical one for this `IRType`.
+ result = type.getCanonicalLanguageType()
+ }
final IntValue getStartBitOffset() { result = startBitOffset }
@@ -85,13 +112,14 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
final override string getUniqueId() {
result = var.getUniqueId() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" +
- getTypeIdentityString(type) + ">"
+ type.getIdentityString() + ">"
}
final override VirtualVariable getVirtualVariable() {
if variableAddressEscapes(var)
then result = TUnknownVirtualVariable(var.getEnclosingIRFunction())
- else result = TVariableMemoryLocation(var, var.getType(), 0, var.getType().getSize() * 8)
+ else
+ result = TVariableMemoryLocation(var, var.getIRType(), _, 0, var.getIRType().getByteSize() * 8)
}
/**
@@ -99,7 +127,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
*/
final predicate coversEntireVariable() {
startBitOffset = 0 and
- endBitOffset = var.getType().getSize() * 8
+ endBitOffset = var.getIRType().getByteSize() * 8
}
}
@@ -111,7 +139,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation {
class VariableVirtualVariable extends VariableMemoryLocation, VirtualVariable {
VariableVirtualVariable() {
not variableAddressEscapes(var) and
- type = var.getType() and
+ type = var.getIRType() and
coversEntireVariable()
}
}
@@ -128,11 +156,33 @@ class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation {
final override VirtualVariable getVirtualVariable() { result = TUnknownVirtualVariable(irFunc) }
- final override Type getType() { result instanceof UnknownType }
+ final override Language::LanguageType getType() {
+ result = any(IRUnknownType type).getCanonicalLanguageType()
+ }
final override string getUniqueId() { result = "{Unknown}" }
}
+/**
+ * An access to memory that is not known to be confined to a specific `IRVariable`, but is known to
+ * not access memory on the current function's stack frame.
+ */
+class UnknownNonLocalMemoryLocation extends TUnknownNonLocalMemoryLocation, MemoryLocation {
+ IRFunction irFunc;
+
+ UnknownNonLocalMemoryLocation() { this = TUnknownNonLocalMemoryLocation(irFunc) }
+
+ final override string toString() { result = "{UnknownNonLocal}" }
+
+ final override VirtualVariable getVirtualVariable() { result = TUnknownVirtualVariable(irFunc) }
+
+ final override Language::LanguageType getType() {
+ result = any(IRUnknownType type).getCanonicalLanguageType()
+ }
+
+ final override string getUniqueId() { result = "{UnknownNonLocal}" }
+}
+
/**
* An access to all aliased memory.
*/
@@ -143,7 +193,9 @@ class UnknownVirtualVariable extends TUnknownVirtualVariable, VirtualVariable {
final override string toString() { result = "{AllAliased}" }
- final override Type getType() { result instanceof UnknownType }
+ final override Language::LanguageType getType() {
+ result = any(IRUnknownType type).getCanonicalLanguageType()
+ }
final override string getUniqueId() { result = " " + toString() }
@@ -158,10 +210,21 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
def instanceof UnknownVirtualVariable and
result instanceof MustTotallyOverlap
or
- // An UnknownMemoryLocation may partially overlap any Location within the same virtual variable.
+ // An UnknownMemoryLocation may partially overlap any Location within the same virtual variable,
+ // unless the location is read-only.
def.getVirtualVariable() = use.getVirtualVariable() and
def instanceof UnknownMemoryLocation and
- result instanceof MayPartiallyOverlap
+ result instanceof MayPartiallyOverlap and
+ not use.(VariableMemoryLocation).getVariable().isReadOnly()
+ or
+ // An UnknownNonLocalMemoryLocation may partially overlap any location within the same virtual
+ // variable, except a local variable or read-only variable.
+ def.getVirtualVariable() = use.getVirtualVariable() and
+ def instanceof UnknownNonLocalMemoryLocation and
+ result instanceof MayPartiallyOverlap and
+ not exists(IRVariable var | var = use.(VariableMemoryLocation).getVariable() |
+ var instanceof IRAutomaticVariable or var.isReadOnly()
+ )
or
exists(VariableMemoryLocation defVariableLocation |
defVariableLocation = def and
@@ -171,13 +234,20 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
(use instanceof UnknownMemoryLocation or use instanceof UnknownVirtualVariable) and
result instanceof MayPartiallyOverlap
or
+ // A VariableMemoryLocation that is not a local variable may partially overlap an unknown
+ // non-local location within the same virtual variable.
+ def.getVirtualVariable() = use.getVirtualVariable() and
+ use instanceof UnknownNonLocalMemoryLocation and
+ result instanceof MayPartiallyOverlap and
+ not defVariableLocation.getVariable() instanceof IRAutomaticVariable
+ or
// A VariableMemoryLocation overlaps another location within the same variable based on the relationship
// of the two offset intervals.
exists(Overlap intervalOverlap |
intervalOverlap = getVariableMemoryLocationOverlap(def, use) and
if intervalOverlap instanceof MustExactlyOverlap
then
- if def.getType() = use.getType()
+ if def.getIRType() = use.getIRType()
then
// The def and use types match, so it's an exact overlap.
result instanceof MustExactlyOverlap
@@ -282,11 +352,11 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
(
(
kind.usesAddressOperand() and
- if hasResultMemoryAccess(instr, _, _, _, _)
+ if hasResultMemoryAccess(instr, _, _, _, _, _)
then
- exists(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset |
- hasResultMemoryAccess(instr, var, type, startBitOffset, endBitOffset) and
- result = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
+ exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
+ hasResultMemoryAccess(instr, var, type, _, startBitOffset, endBitOffset) and
+ result = TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset)
)
else result = TUnknownMemoryLocation(instr.getEnclosingIRFunction())
)
@@ -296,6 +366,9 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
or
kind instanceof EscapedMayMemoryAccess and
result = TUnknownMemoryLocation(instr.getEnclosingIRFunction())
+ or
+ kind instanceof NonLocalMayMemoryAccess and
+ result = TUnknownNonLocalMemoryLocation(instr.getEnclosingIRFunction())
)
)
}
@@ -306,11 +379,11 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
(
(
kind.usesAddressOperand() and
- if hasOperandMemoryAccess(operand, _, _, _, _)
+ if hasOperandMemoryAccess(operand, _, _, _, _, _)
then
- exists(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset |
- hasOperandMemoryAccess(operand, var, type, startBitOffset, endBitOffset) and
- result = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset)
+ exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
+ hasOperandMemoryAccess(operand, var, type, _, startBitOffset, endBitOffset) and
+ result = TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset)
)
else result = TUnknownMemoryLocation(operand.getEnclosingIRFunction())
)
@@ -320,6 +393,9 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) {
or
kind instanceof EscapedMayMemoryAccess and
result = TUnknownMemoryLocation(operand.getEnclosingIRFunction())
+ or
+ kind instanceof NonLocalMayMemoryAccess and
+ result = TUnknownNonLocalMemoryLocation(operand.getEnclosingIRFunction())
)
)
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll
index 74f7b4a2b64..42d6e7db693 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll
@@ -1,2 +1,3 @@
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll
index 1f0f5eaccde..8c60565defc 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll
@@ -1,3 +1,4 @@
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag
import semmle.code.cpp.ir.internal.IRUtilities as IRUtilities
import semmle.code.cpp.ir.internal.TempVariableTag as TTempVariableTag
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll
index 0138af075dc..a75f70dbe2f 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll
@@ -1,4 +1,5 @@
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
import semmle.code.cpp.ir.implementation.Opcode as Opcode
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll
index e626662ccf2..3c781579cee 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll
@@ -1,3 +1,4 @@
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.internal.Overlap as Overlap
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll
index a5c09c6401b..23121523a6b 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll
@@ -4,34 +4,52 @@ private import Alias
private import SSAConstruction
private import DebugSSA
+bindingset[offset]
+private string getKeySuffixForOffset(int offset) {
+ if offset % 2 = 0 then result = "" else result = "_Chi"
+}
+
+bindingset[offset]
+private int getIndexForOffset(int offset) { result = offset / 2 }
+
/**
* Property provide that dumps the memory access of each result. Useful for debugging SSA
* construction.
*/
class PropertyProvider extends IRPropertyProvider {
override string getInstructionProperty(Instruction instruction, string key) {
- exists(MemoryLocation location |
- location = getResultMemoryLocation(instruction) and
- (
- key = "ResultMemoryLocation" and result = location.toString()
- or
- key = "ResultVirtualVariable" and result = location.getVirtualVariable().toString()
+ key = "ResultMemoryLocation" and
+ result = strictconcat(MemoryLocation loc |
+ loc = getResultMemoryLocation(instruction)
+ |
+ loc.toString(), ","
)
- )
or
- exists(MemoryLocation location |
- location = getOperandMemoryLocation(instruction.getAnOperand()) and
- (
- key = "OperandMemoryAccess" and result = location.toString()
- or
- key = "OperandVirtualVariable" and result = location.getVirtualVariable().toString()
+ key = "ResultVirtualVariable" and
+ result = strictconcat(MemoryLocation loc |
+ loc = getResultMemoryLocation(instruction)
+ |
+ loc.getVirtualVariable().toString(), ","
)
- )
or
- exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
- hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
- defBlock.getInstruction(defIndex) = instruction and
- key = "DefinitionRank[" + useLocation.toString() + "]" and
+ key = "OperandMemoryLocation" and
+ result = strictconcat(MemoryLocation loc |
+ loc = getOperandMemoryLocation(instruction.getAnOperand())
+ |
+ loc.toString(), ","
+ )
+ or
+ key = "OperandVirtualVariable" and
+ result = strictconcat(MemoryLocation loc |
+ loc = getOperandMemoryLocation(instruction.getAnOperand())
+ |
+ loc.getVirtualVariable().toString(), ","
+ )
+ or
+ exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset |
+ hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and
+ defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and
+ key = "DefinitionRank" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString() + "]" and
result = defRank.toString()
)
or
@@ -41,10 +59,11 @@ class PropertyProvider extends IRPropertyProvider {
result = useRank.toString()
)
or
- exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
- hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
- defBlock.getInstruction(defIndex) = instruction and
- key = "DefinitionReachesUse[" + useLocation.toString() + "]" and
+ exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset |
+ hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and
+ defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and
+ key = "DefinitionReachesUse" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString()
+ + "]" and
result = strictconcat(IRBlock useBlock, int useRank, int useIndex |
exists(Instruction useInstruction |
hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and
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 1ca6e099795..a789edc7590 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,8 +1,5 @@
import SSAConstructionInternal
-private import cpp
-private import semmle.code.cpp.ir.implementation.Opcode
-private import semmle.code.cpp.ir.implementation.internal.OperandTag
-private import semmle.code.cpp.ir.internal.Overlap
+private import SSAConstructionImports
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -18,7 +15,7 @@ private module Cached {
}
cached
- predicate functionHasIR(Function func) {
+ predicate functionHasIR(Language::Function func) {
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
}
@@ -42,7 +39,7 @@ private module Cached {
not oldInstruction instanceof OldIR::PhiInstruction and
hasChiNode(_, oldInstruction)
} or
- Unreached(Function function) {
+ Unreached(Language::Function function) {
exists(OldInstruction oldInstruction |
function = oldInstruction.getEnclosingFunction() and
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
@@ -50,12 +47,14 @@ private module Cached {
}
cached
- predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, Type type) {
+ 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.getType() = type
+ var.getLanguageType() = type
)
}
@@ -135,24 +134,12 @@ private module Cached {
}
cached
- Type getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
+ Language::LanguageType getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
exists(OldInstruction oldInstruction, OldIR::TypedOperand oldOperand |
oldInstruction = getOldInstruction(instr) and
oldOperand = oldInstruction.getAnOperand() and
tag = oldOperand.getOperandTag() and
- result = oldOperand.getType()
- )
- }
-
- cached
- int getInstructionOperandSize(Instruction instr, SideEffectOperandTag tag) {
- exists(OldInstruction oldInstruction, OldIR::SideEffectOperand oldOperand |
- oldInstruction = getOldInstruction(instr) and
- oldOperand = oldInstruction.getAnOperand() and
- tag = oldOperand.getOperandTag() and
- // Only return a result for operands that need an explicit result size.
- oldOperand.getType() instanceof UnknownType and
- result = oldOperand.getSize()
+ result = oldOperand.getLanguageType()
)
}
@@ -196,20 +183,21 @@ private module Cached {
}
cached
- Expr getInstructionConvertedResultExpression(Instruction instruction) {
+ Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
result = getOldInstruction(instruction).getConvertedResultExpression()
}
cached
- Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
+ 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
* the new instructions generated from the successors of the old instruction
*/
+
cached
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
if hasChiNode(_, getOldInstruction(instruction))
@@ -252,7 +240,7 @@ private module Cached {
}
cached
- Locatable getInstructionAST(Instruction instruction) {
+ Language::AST getInstructionAST(Instruction instruction) {
exists(OldInstruction oldInstruction |
instruction = WrappedInstruction(oldInstruction)
or
@@ -270,29 +258,25 @@ private module Cached {
}
cached
- predicate instructionHasType(Instruction instruction, Type type, boolean isGLValue) {
+ Language::LanguageType getInstructionResultType(Instruction instruction) {
exists(OldInstruction oldInstruction |
instruction = WrappedInstruction(oldInstruction) and
- type = oldInstruction.getResultType() and
- if oldInstruction.isGLValue() then isGLValue = true else isGLValue = false
+ result = oldInstruction.getResultLanguageType()
)
or
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
instruction = Chi(oldInstruction) and
hasChiNode(vvar, oldInstruction) and
- type = vvar.getType() and
- isGLValue = false
+ result = vvar.getType()
)
or
exists(Alias::MemoryLocation location |
instruction = Phi(_, location) and
- type = location.getType() and
- isGLValue = false
+ result = location.getType()
)
or
instruction = Unreached(_) and
- type instanceof VoidType and
- isGLValue = false
+ result = Language::getVoidType()
}
cached
@@ -334,16 +318,21 @@ private module Cached {
IRVariable getInstructionVariable(Instruction instruction) {
result = getNewIRVariable(getOldInstruction(instruction)
.(OldIR::VariableInstruction)
- .getVariable())
+ .getIRVariable())
}
cached
- Field getInstructionField(Instruction instruction) {
+ Language::Field getInstructionField(Instruction instruction) {
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
}
cached
- Function getInstructionFunction(Instruction instruction) {
+ int getInstructionIndex(Instruction instruction) {
+ result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
+ }
+
+ cached
+ Language::Function getInstructionFunction(Instruction instruction) {
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
}
@@ -353,19 +342,14 @@ private module Cached {
}
cached
- StringLiteral getInstructionStringLiteral(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue()
- }
-
- cached
- BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
+ Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
result = getOldInstruction(instruction)
.(OldIR::BuiltInOperationInstruction)
.getBuiltInOperation()
}
cached
- Type getInstructionExceptionType(Instruction instruction) {
+ Language::LanguageType getInstructionExceptionType(Instruction instruction) {
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
}
@@ -375,14 +359,9 @@ private module Cached {
}
cached
- int getInstructionResultSize(Instruction instruction) {
- // Only return a result for instructions that needed an explicit result size.
- instruction.getResultType() instanceof UnknownType and
- result = getOldInstruction(instruction).getResultSize()
- }
-
- cached
- predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) {
+ predicate getInstructionInheritance(
+ Instruction instruction, Language::Class baseClass, Language::Class derivedClass
+ ) {
exists(OldIR::InheritanceConversionInstruction oldInstr |
oldInstr = getOldInstruction(instruction) and
baseClass = oldInstr.getBaseClass() and
@@ -417,7 +396,11 @@ private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
defLocation.getVirtualVariable() = vvar and
// If the definition totally (or exactly) overlaps the virtual variable, then there's no need for a `Chi`
// instruction.
- Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap
+ (
+ Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap or
+ def.getResultMemoryAccess() instanceof IndirectMayMemoryAccess or
+ def.getResultMemoryAccess() instanceof BufferMayMemoryAccess
+ )
)
}
@@ -730,7 +713,10 @@ module DefUse {
defLocation = Alias::getResultMemoryLocation(def) and
block.getInstruction(index) = def and
overlap = Alias::getOverlap(defLocation, useLocation) and
- if overlap instanceof MayPartiallyOverlap
+ if
+ overlap instanceof MayPartiallyOverlap or
+ def.getResultMemoryAccess() instanceof IndirectMayMemoryAccess or
+ def.getResultMemoryAccess() instanceof BufferMayMemoryAccess
then offset = (index * 2) + 1 // The use will be connected to the definition on the `Chi` instruction.
else offset = index * 2 // The use will be connected to the definition on the original instruction.
)
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
new file mode 100644
index 00000000000..00f12020a29
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll
@@ -0,0 +1,3 @@
+import semmle.code.cpp.ir.implementation.Opcode
+import semmle.code.cpp.ir.implementation.internal.OperandTag
+import semmle.code.cpp.ir.internal.Overlap
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 5eebc0b9516..c0922aff891 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,4 +2,5 @@ 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.internal.IRCppLanguage as Language
import AliasedSSA as Alias
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRTypeInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRTypeInternal.qll
new file mode 100644
index 00000000000..bd6c2f4c151
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRTypeInternal.qll
@@ -0,0 +1 @@
+import semmle.code.cpp.ir.internal.IRCppLanguage as Language
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 d1172670a79..227b1a34041 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
@@ -66,12 +66,14 @@ AddressOperandTag addressOperand() { result = TAddressOperand() }
* The buffer size operand of an instruction that represents a read or write of
* a buffer.
*/
-class BufferSizeOperand extends RegisterOperandTag, TBufferSizeOperand {
+class BufferSizeOperandTag extends RegisterOperandTag, TBufferSizeOperand {
final override string toString() { result = "BufferSize" }
final override int getSortOrder() { result = 1 }
}
+BufferSizeOperandTag bufferSizeOperand() { result = TBufferSizeOperand() }
+
/**
* The operand representing the read side effect of a `SideEffectInstruction`.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll
index 908a208b83a..01c135abf13 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll
@@ -2,11 +2,17 @@ private import TIRVariableInternal
private import Imports::TempVariableTag
newtype TIRVariable =
- TIRUserVariable(Language::Variable var, Language::Type type, Language::Function func) {
+ TIRUserVariable(Language::Variable var, Language::LanguageType type, Language::Function func) {
Construction::hasUserVariable(func, var, type)
} or
TIRTempVariable(
- Language::Function func, Language::AST ast, TempVariableTag tag, Language::Type type
+ Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
) {
Construction::hasTempVariable(func, ast, tag, type)
+ } or
+ TIRStringLiteral(
+ Language::Function func, Language::AST ast, Language::LanguageType type,
+ Language::StringLiteral literal
+ ) {
+ Construction::hasStringLiteral(func, ast, type, literal)
}
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 278040f8ab8..badd48552a5 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
@@ -5,6 +5,7 @@ import IRVariable
import Operand
private import internal.IRImports as Imports
import Imports::EdgeKind
+import Imports::IRType
import Imports::MemoryAccessKind
private newtype TIRPropertyProvider = MkIRPropertyProvider()
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll
index 3921472dc8e..de66a3c99dc 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll
@@ -1,2 +1,3 @@
private import IR
import InstructionSanity
+import IRTypeSanity
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 a7ca4593ac4..e10e266b2ab 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
@@ -5,6 +5,7 @@ import Imports::TempVariableTag
private import Imports::IRUtilities
private import Imports::TTempVariableTag
private import Imports::TIRVariable
+private import Imports::IRType
IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
result.getVariable() = var and
@@ -21,10 +22,26 @@ abstract class IRVariable extends TIRVariable {
abstract string toString();
+ /**
+ * Holds if this variable's value cannot be changed within a function. Currently used for string
+ * literals, but could also apply to `const` global and static variables.
+ */
+ predicate isReadOnly() { none() }
+
/**
* Gets the type of the variable.
*/
- abstract Language::Type getType();
+ final Language::Type getType() { getLanguageType().hasType(result, false) }
+
+ /**
+ * Gets the language-neutral type of the variable.
+ */
+ final IRType getIRType() { result = getLanguageType().getIRType() }
+
+ /**
+ * Gets the type of the variable.
+ */
+ abstract Language::LanguageType getLanguageType();
/**
* Gets the AST node that declared this variable, or that introduced this
@@ -59,7 +76,7 @@ abstract class IRVariable extends TIRVariable {
*/
class IRUserVariable extends IRVariable, TIRUserVariable {
Language::Variable var;
- Language::Type type;
+ Language::LanguageType type;
IRUserVariable() { this = TIRUserVariable(var, type, func) }
@@ -71,7 +88,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
result = getVariable().toString() + " " + getVariable().getLocation().toString()
}
- final override Language::Type getType() { result = type }
+ final override Language::LanguageType getLanguageType() { result = type }
/**
* Gets the original user-declared variable.
@@ -102,34 +119,43 @@ class IRStaticUserVariable extends IRUserVariable {
final override Language::StaticVariable getVariable() { result = var }
}
+abstract class IRGeneratedVariable extends IRVariable {
+ Language::AST ast;
+ Language::LanguageType type;
+
+ final override Language::LanguageType getLanguageType() { result = type }
+
+ final override Language::AST getAST() { result = ast }
+
+ override string toString() { result = getBaseString() + getLocationString() }
+
+ override string getUniqueId() { none() }
+
+ final string getLocationString() {
+ result = ast.getLocation().getStartLine().toString() + ":" +
+ ast.getLocation().getStartColumn().toString()
+ }
+
+ string getBaseString() { none() }
+}
+
IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
result.getAST() = ast and
result.getTag() = tag
}
-class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable {
- Language::AST ast;
+class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
TempVariableTag tag;
- Language::Type type;
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
- final override Language::Type getType() { result = type }
-
- final override Language::AST getAST() { result = ast }
-
final override string getUniqueId() {
result = "Temp: " + Construction::getTempVariableUniqueId(this)
}
final TempVariableTag getTag() { result = tag }
- override string toString() {
- result = getBaseString() + ast.getLocation().getStartLine().toString() + ":" +
- ast.getLocation().getStartColumn().toString()
- }
-
- string getBaseString() { result = "#temp" }
+ override string getBaseString() { result = "#temp" }
}
class IRReturnVariable extends IRTempVariable {
@@ -143,3 +169,19 @@ class IRThrowVariable extends IRTempVariable {
override string getBaseString() { result = "#throw" }
}
+
+class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
+ Language::StringLiteral literal;
+
+ IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
+
+ final override predicate isReadOnly() { any() }
+
+ final override string getUniqueId() {
+ result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
+ }
+
+ override string getBaseString() { result = "#string" }
+
+ final Language::StringLiteral getLiteral() { result = literal }
+}
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 43f9663d2cd..e3c78f476e9 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
@@ -5,6 +5,7 @@ import IRVariable
import Operand
private import internal.InstructionImports as Imports
import Imports::EdgeKind
+import Imports::IRType
import Imports::MemoryAccessKind
import Imports::Opcode
private import Imports::OperandTag
@@ -30,7 +31,7 @@ module InstructionSanity {
or
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
or
- opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand
+ opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
or
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
or
@@ -48,8 +49,9 @@ module InstructionSanity {
or
(
opcode instanceof ReadSideEffectOpcode or
- opcode instanceof MayWriteSideEffectOpcode or
- opcode instanceof Opcode::InlineAsm
+ opcode instanceof Opcode::InlineAsm or
+ opcode instanceof Opcode::CallSideEffect or
+ opcode instanceof Opcode::AliasedUse
) and
tag instanceof SideEffectOperandTag
)
@@ -113,13 +115,34 @@ module InstructionSanity {
}
query predicate missingOperandType(Operand operand, string message) {
- exists(Language::Function func |
+ exists(Language::Function func, Instruction use |
not exists(operand.getType()) and
- func = operand.getUse().getEnclosingFunction() and
- message = "Operand missing type in function '" + Language::getIdentityString(func) + "'."
+ use = operand.getUse() and
+ func = use.getEnclosingFunction() and
+ message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString()
+ + "' missing type in function '" + Language::getIdentityString(func) + "'."
)
}
+ query predicate duplicateChiOperand(
+ ChiInstruction chi, string message, IRFunction func, string funcText
+ ) {
+ 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())
+ }
+
+ query predicate sideEffectWithoutPrimary(
+ SideEffectInstruction instr, string message, IRFunction func, string funcText
+ ) {
+ not exists(instr.getPrimaryInstruction()) and
+ message = "Side effect instruction missing primary instruction in function $@" and
+ func = instr.getEnclosingIRFunction() and
+ funcText = Language::getIdentityString(func.getFunction())
+ }
+
/**
* Holds if an instruction, other than `ExitFunction`, has no successors.
*/
@@ -251,6 +274,7 @@ module InstructionSanity {
) {
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
not useOperand.getUse() instanceof UnmodeledUseInstruction and
+ not defInstr instanceof UnmodeledDefinitionInstruction and
pointOfEvaluation(useOperand, useBlock, useIndex) and
defInstr = useOperand.getAnyDef() and
(
@@ -312,7 +336,7 @@ class Instruction extends Construction::TInstruction {
}
private string getResultPrefix() {
- if getResultType() instanceof Language::VoidType
+ if getResultIRType() instanceof IRVoidType
then result = "v"
else
if hasMemoryResult()
@@ -344,23 +368,6 @@ class Instruction extends Construction::TInstruction {
)
}
- bindingset[type]
- private string getValueCategoryString(string type) {
- if isGLValue() then result = "glval<" + type + ">" else result = type
- }
-
- string getResultTypeString() {
- exists(string valcat |
- valcat = getValueCategoryString(getResultType().toString()) and
- if
- getResultType() instanceof Language::UnknownType and
- not isGLValue() and
- exists(getResultSize())
- then result = valcat + "[" + getResultSize().toString() + "]"
- else result = valcat
- )
- }
-
/**
* Gets a human-readable string that uniquely identifies this instruction
* within the function. This string is used to refer to this instruction when
@@ -380,7 +387,9 @@ class Instruction extends Construction::TInstruction {
*
* Example: `r1_1(int*)`
*/
- final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" }
+ final string getResultString() {
+ result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
+ }
/**
* Gets a string describing the operands of this instruction, suitable for
@@ -448,6 +457,16 @@ class Instruction extends Construction::TInstruction {
result = Construction::getInstructionUnconvertedResultExpression(this)
}
+ final Language::LanguageType getResultLanguageType() {
+ result = Construction::getInstructionResultType(this)
+ }
+
+ /**
+ * Gets the type of the result produced by this instruction. If the instruction does not produce
+ * a result, its result type will be `IRVoidType`.
+ */
+ final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
+
/**
* Gets the type of the result produced by this instruction. If the
* instruction does not produce a result, its result type will be `VoidType`.
@@ -455,7 +474,16 @@ class Instruction extends Construction::TInstruction {
* If `isGLValue()` holds, then the result type of this instruction should be
* thought of as "pointer to `getResultType()`".
*/
- final Language::Type getResultType() { Construction::instructionHasType(this, result, _) }
+ final Language::Type getResultType() {
+ exists(Language::LanguageType resultType |
+ resultType = getResultLanguageType() and
+ (
+ resultType.hasUnspecifiedType(result, _)
+ or
+ not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType
+ )
+ )
+ }
/**
* Holds if the result produced by this instruction is a glvalue. If this
@@ -475,7 +503,7 @@ class Instruction extends Construction::TInstruction {
* result of the `Load` instruction is a prvalue of type `int`, representing
* the integer value loaded from variable `x`.
*/
- final predicate isGLValue() { Construction::instructionHasType(this, _, true) }
+ final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
/**
* Gets the size of the result produced by this instruction, in bytes. If the
@@ -484,16 +512,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() {
- if isGLValue()
- then
- // a glvalue is always pointer-sized.
- result = Language::getPointerSize()
- else
- if getResultType() instanceof Language::UnknownType
- then result = Construction::getInstructionResultSize(this)
- else result = Language::getTypeSize(getResultType())
- }
+ final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
/**
* Gets the opcode that specifies the operation performed by this instruction.
@@ -609,9 +628,14 @@ class VariableInstruction extends Instruction {
VariableInstruction() { var = Construction::getInstructionVariable(this) }
- final override string getImmediateString() { result = var.toString() }
+ override string getImmediateString() { result = var.toString() }
- final IRVariable getVariable() { result = var }
+ final IRVariable getIRVariable() { result = var }
+
+ /**
+ * Gets the AST variable that this instruction's IR variable refers to, if one exists.
+ */
+ final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
}
class FieldInstruction extends Instruction {
@@ -644,6 +668,16 @@ class ConstantValueInstruction extends Instruction {
final string getValue() { result = value }
}
+class IndexedInstruction extends Instruction {
+ int index;
+
+ IndexedInstruction() { index = Construction::getInstructionIndex(this) }
+
+ final override string getImmediateString() { result = index.toString() }
+
+ final int getIndex() { result = index }
+}
+
class EnterFunctionInstruction extends Instruction {
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
}
@@ -784,14 +818,12 @@ class FloatConstantInstruction extends ConstantInstruction {
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
}
-class StringConstantInstruction extends Instruction {
- Language::StringLiteral value;
+class StringConstantInstruction extends VariableInstruction {
+ override IRStringLiteral var;
- StringConstantInstruction() { value = Construction::getInstructionStringLiteral(this) }
+ final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) }
- final override string getImmediateString() { result = Language::getStringLiteralText(value) }
-
- final Language::StringLiteral getValue() { result = value }
+ final Language::StringLiteral getValue() { result = var.getLiteral() }
}
class BinaryInstruction extends Instruction {
@@ -959,14 +991,22 @@ class InheritanceConversionInstruction extends UnaryInstruction {
* to the address of a direct non-virtual base class.
*/
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
- ConvertToBaseInstruction() { getOpcode() instanceof Opcode::ConvertToBase }
+ 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.
+ */
+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.
*/
-class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction {
+class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
}
@@ -1175,6 +1215,8 @@ class CallReadSideEffectInstruction extends SideEffectInstruction {
*/
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
}
/**
@@ -1182,13 +1224,39 @@ class IndirectReadSideEffectInstruction extends SideEffectInstruction {
*/
class BufferReadSideEffectInstruction extends SideEffectInstruction {
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
+}
+
+/**
+ * An instruction representing the read of an indirect buffer parameter within a function call.
+ */
+class SizedBufferReadSideEffectInstruction extends SideEffectInstruction {
+ SizedBufferReadSideEffectInstruction() {
+ getOpcode() instanceof Opcode::SizedBufferReadSideEffect
+ }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
+
+ Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+}
+
+/**
+ * An instruction representing a side effect of a function call.
+ */
+class WriteSideEffectInstruction extends SideEffectInstruction {
+ WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
}
/**
* An instruction representing the write of an indirect parameter within a function call.
*/
-class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
- IndirectWriteSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectWriteSideEffect }
+class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
+ IndirectMustWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
+ }
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
}
@@ -1197,18 +1265,34 @@ class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
* An instruction representing the write of an indirect buffer parameter within a function call. The
* entire buffer is overwritten.
*/
-class BufferWriteSideEffectInstruction extends SideEffectInstruction {
- BufferWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferWriteSideEffect }
+class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
+ BufferMustWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::BufferMustWriteSideEffect
+ }
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
}
+/**
+ * An instruction representing the write of an indirect buffer parameter within a function call. The
+ * entire buffer is overwritten.
+ */
+class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
+ SizedBufferMustWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
+ }
+
+ final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
+
+ Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).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.
*/
-class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
+class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
IndirectMayWriteSideEffectInstruction() {
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
}
@@ -1222,7 +1306,7 @@ class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
* 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 SideEffectInstruction {
+class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
final override MemoryAccessKind getResultMemoryAccess() {
@@ -1230,6 +1314,22 @@ class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
}
}
+/**
+ * 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 {
+ SizedBufferMayWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
+ }
+
+ final override MemoryAccessKind getResultMemoryAccess() {
+ result instanceof BufferMayMemoryAccess
+ }
+
+ Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+}
+
/**
* An instruction representing a GNU or MSVC inline assembly statement.
*/
@@ -1300,7 +1400,7 @@ class CatchInstruction extends Instruction {
* An instruction that catches an exception of a specific type.
*/
class CatchByTypeInstruction extends CatchInstruction {
- Language::Type exceptionType;
+ Language::LanguageType exceptionType;
CatchByTypeInstruction() {
getOpcode() instanceof Opcode::CatchByType and
@@ -1312,7 +1412,7 @@ class CatchByTypeInstruction extends CatchInstruction {
/**
* Gets the type of exception to be caught.
*/
- final Language::Type getExceptionType() { result = exceptionType }
+ final Language::LanguageType getExceptionType() { result = exceptionType }
}
/**
@@ -1339,6 +1439,13 @@ class AliasedDefinitionInstruction extends Instruction {
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
}
+/**
+ * An instruction that consumes all escaped memory on exit from the function.
+ */
+class AliasedUseInstruction extends Instruction {
+ AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
+}
+
class UnmodeledUseInstruction extends Instruction {
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
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 1ced8ef3282..07dd107bf12 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,9 +1,10 @@
private import internal.IRInternal
-import Instruction
-import IRBlock
+private import Instruction
+private import IRBlock
private import internal.OperandImports as Imports
-import Imports::MemoryAccessKind
-import Imports::Overlap
+private import Imports::MemoryAccessKind
+private import Imports::IRType
+private import Imports::Overlap
private import Imports::OperandTag
cached
@@ -143,22 +144,40 @@ class Operand extends TOperand {
* the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
- Language::Type getType() { result = getAnyDef().getResultType() }
+ Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() }
+
+ /**
+ * Gets the language-neutral type of the value consumed by this operand. This is usually the same
+ * as the result type of the definition instruction consumed by this operand. For register
+ * operands, this is always the case. For some memory operands, the operand type may be different
+ * from the definition type, such as in the case of a partial read or a read from a pointer that
+ * has been cast to a different type.
+ */
+ final IRType getIRType() { result = getLanguageType().getIRType() }
+
+ /**
+ * Gets the type of the value consumed by this operand. This is usually the same as the
+ * result type of the definition instruction consumed by this operand. For register operands,
+ * this is always the case. For some memory operands, the operand type may be different from
+ * the definition type, such as in the case of a partial read or a read from a pointer that
+ * has been cast to a different type.
+ */
+ final Language::Type getType() { getLanguageType().hasType(result, _) }
/**
* Holds if the value consumed by this operand is a glvalue. If this
* holds, the value of the operand represents the address of a location,
* and the type of the location is given by `getType()`. If this does
* not hold, the value of the operand represents a value whose type is
- * given by `getResultType()`.
+ * given by `getType()`.
*/
- predicate isGLValue() { getAnyDef().isGLValue() }
+ final predicate isGLValue() { getLanguageType().hasType(_, true) }
/**
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
* a known constant size, this predicate does not hold.
*/
- int getSize() { result = Language::getTypeSize(getType()) }
+ final int getSize() { result = getLanguageType().getByteSize() }
}
/**
@@ -170,11 +189,6 @@ class MemoryOperand extends Operand {
this = TPhiOperand(_, _, _, _)
}
- override predicate isGLValue() {
- // A `MemoryOperand` can never be a glvalue
- none()
- }
-
/**
* Gets the kind of memory access performed by the operand.
*/
@@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe
class TypedOperand extends NonPhiMemoryOperand {
override TypedOperandTag tag;
- final override Language::Type getType() {
+ final override Language::LanguageType getLanguageType() {
result = Construction::getInstructionOperandType(useInstr, tag)
}
}
@@ -254,6 +268,16 @@ class AddressOperand extends RegisterOperand {
override string toString() { result = "Address" }
}
+/**
+ * The buffer size operand of an instruction that represents a read or write of
+ * a buffer.
+ */
+class BufferSizeOperand extends RegisterOperand {
+ override BufferSizeOperandTag tag;
+
+ override string toString() { result = "BufferSize" }
+}
+
/**
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
* `ReturnValue`, `ThrowValue`).
@@ -371,13 +395,10 @@ class PositionalArgumentOperand extends ArgumentOperand {
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
- final override int getSize() {
- if getType() instanceof Language::UnknownType
- then result = Construction::getInstructionOperandSize(useInstr, tag)
- else result = Language::getTypeSize(getType())
- }
-
override MemoryAccessKind getMemoryAccess() {
+ useInstr instanceof AliasedUseInstruction and
+ result instanceof NonLocalMayMemoryAccess
+ or
useInstr instanceof CallSideEffectInstruction and
result instanceof EscapedMayMemoryAccess
or
@@ -390,10 +411,10 @@ class SideEffectOperand extends TypedOperand {
useInstr instanceof BufferReadSideEffectInstruction and
result instanceof BufferMemoryAccess
or
- useInstr instanceof IndirectWriteSideEffectInstruction and
+ useInstr instanceof IndirectMustWriteSideEffectInstruction and
result instanceof IndirectMemoryAccess
or
- useInstr instanceof BufferWriteSideEffectInstruction and
+ useInstr instanceof BufferMustWriteSideEffectInstruction and
result instanceof BufferMemoryAccess
or
useInstr instanceof IndirectMayWriteSideEffectInstruction and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll
index e3e52df8931..79e5a2b6713 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll
@@ -1,5 +1,5 @@
private import internal.ValueNumberingInternal
-private import cpp
+private import internal.ValueNumberingImports
private import IR
/**
@@ -23,31 +23,32 @@ newtype TValueNumber =
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
- TConstantValueNumber(IRFunction irFunc, Type type, string value) {
+ TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
- TStringConstantValueNumber(IRFunction irFunc, Type type, string value) {
+ TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
stringConstantValueNumber(_, irFunc, type, value)
} or
- TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) {
+ TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) {
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
- IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand
+ IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
- IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
+ IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
ValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
} or
- TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) {
+ TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, type, operand)
} or
TInheritanceConversionValueNumber(
- IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand
+ IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
+ ValueNumber operand
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
@@ -59,7 +60,7 @@ newtype TValueNumber =
class ValueNumber extends TValueNumber {
final string toString() { result = getExampleInstruction().getResultId() }
- final Location getLocation() { result = getExampleInstruction().getLocation() }
+ final Language::Location getLocation() { result = getExampleInstruction().getLocation() }
/**
* Gets the instructions that have been assigned this value number. This will always produce at
@@ -135,14 +136,14 @@ private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getVariable() = var
+ instr.getIRVariable() = var
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getVariable() = var
+ instr.getIRVariable() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
@@ -150,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
}
private predicate constantValueNumber(
- ConstantInstruction instr, IRFunction irFunc, Type type, string value
+ ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
instr.getValue() = value
}
private predicate stringConstantValueNumber(
- StringConstantInstruction instr, IRFunction irFunc, Type type, string value
+ StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
private predicate fieldAddressValueNumber(
- FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress
+ FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
@@ -174,43 +175,43 @@ private predicate fieldAddressValueNumber(
}
private predicate binaryValueNumber(
- BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand,
+ BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand,
ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
- PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize,
- ValueNumber leftOperand, ValueNumber rightOperand
+ PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
+ int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
instr.getElementSize() = elementSize and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
- UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand
+ UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
instr.getOpcode() = opcode and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
valueNumber(instr.getUnary()) = operand
}
private predicate inheritanceConversionValueNumber(
- InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass,
- Class derivedClass, ValueNumber operand
+ InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
+ Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
@@ -225,7 +226,7 @@ private predicate inheritanceConversionValueNumber(
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
- not instr.getResultType() instanceof VoidType and
+ not instr.getResultIRType() instanceof IRVoidType and
not numberableInstruction(instr)
}
@@ -269,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) {
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
- exists(Type type, string value |
+ exists(IRType type, string value |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
- exists(Type type, string value |
+ exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
- exists(Field field, ValueNumber objectAddress |
+ exists(Language::Field field, ValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
- exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
+ exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
)
or
- exists(Opcode opcode, Type type, ValueNumber operand |
+ exists(Opcode opcode, IRType type, ValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, type, operand) and
result = TUnaryValueNumber(irFunc, opcode, type, operand)
)
or
- exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand |
+ exists(
+ Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
+ |
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(
- Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
+ Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
+ ValueNumber rightOperand
|
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
rightOperand) and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll
new file mode 100644
index 00000000000..0282f6887f1
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll
@@ -0,0 +1,2 @@
+import semmle.code.cpp.ir.internal.Overlap
+import semmle.code.cpp.ir.internal.IRCppLanguage as Language
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 43283db2bc1..628aae26995 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
@@ -1,6 +1,8 @@
private import cpp
import semmle.code.cpp.ir.implementation.raw.IR
private import semmle.code.cpp.ir.implementation.internal.OperandTag
+private import semmle.code.cpp.ir.internal.CppType
+private import semmle.code.cpp.ir.internal.Overlap
private import semmle.code.cpp.ir.internal.TempVariableTag
private import InstructionTag
private import TranslatedCondition
@@ -25,16 +27,16 @@ private module Cached {
cached
newtype TInstruction =
MkInstruction(TranslatedElement element, InstructionTag tag) {
- element.hasInstruction(_, tag, _, _)
+ element.hasInstruction(_, tag, _)
}
cached
- predicate hasUserVariable(Function func, Variable var, Type type) {
+ predicate hasUserVariable(Function func, Variable var, CppType type) {
getTranslatedFunction(func).hasUserVariable(var, type)
}
cached
- predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, Type type) {
+ predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) {
exists(TranslatedElement element |
element.getAST() = ast and
func = element.getFunction() and
@@ -42,6 +44,13 @@ private module Cached {
)
}
+ cached
+ predicate hasStringLiteral(Function func, Locatable ast, CppType type, StringLiteral literal) {
+ literal = ast and
+ literal.getEnclosingFunction() = func and
+ getTypeForPRValue(literal.getType()) = type
+ }
+
cached
predicate hasModeledMemoryResult(Instruction instruction) { none() }
@@ -49,20 +58,23 @@ private module Cached {
Expr getInstructionConvertedResultExpression(Instruction instruction) {
exists(TranslatedExpr translatedExpr |
translatedExpr = getTranslatedExpr(result) and
- instruction = translatedExpr.getResult()
+ 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) {
- exists(Expr converted, TranslatedExpr translatedExpr |
+ exists(Expr converted |
result = converted.(Conversion).getExpr+()
or
result = converted
|
not result instanceof Conversion and
- translatedExpr = getTranslatedExpr(converted) and
- instruction = translatedExpr.getResult()
+ converted = getInstructionConvertedResultExpression(instruction)
)
}
@@ -82,22 +94,16 @@ private module Cached {
}
cached
- Type getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
+ 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.
- result = instruction.(LoadInstruction).getResultType()
+ result = instruction.(LoadInstruction).getResultLanguageType()
or
not instruction instanceof LoadInstruction and
result = getInstructionTranslatedElement(instruction)
.getInstructionOperandType(getInstructionTag(instruction), tag)
}
- cached
- int getInstructionOperandSize(Instruction instruction, SideEffectOperandTag tag) {
- result = getInstructionTranslatedElement(instruction)
- .getInstructionOperandSize(getInstructionTag(instruction), tag)
- }
-
cached
Instruction getPhiOperandDefinition(
PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap
@@ -217,15 +223,15 @@ private module Cached {
}
cached
- predicate instructionHasType(Instruction instruction, Type type, boolean isGLValue) {
+ CppType getInstructionResultType(Instruction instruction) {
getInstructionTranslatedElement(instruction)
- .hasInstruction(_, getInstructionTag(instruction), type, isGLValue)
+ .hasInstruction(_, getInstructionTag(instruction), result)
}
cached
Opcode getInstructionOpcode(Instruction instruction) {
getInstructionTranslatedElement(instruction)
- .hasInstruction(result, getInstructionTag(instruction), _, _)
+ .hasInstruction(result, getInstructionTag(instruction), _)
}
cached
@@ -235,8 +241,14 @@ private module Cached {
cached
IRVariable getInstructionVariable(Instruction instruction) {
- result = getInstructionTranslatedElement(instruction)
- .getInstructionVariable(getInstructionTag(instruction))
+ exists(TranslatedElement element, InstructionTag tag |
+ element = getInstructionTranslatedElement(instruction) and
+ tag = getInstructionTag(instruction) and
+ (
+ result = element.getInstructionVariable(tag) or
+ result.(IRStringLiteral).getAST() = element.getInstructionStringLiteral(tag)
+ )
+ )
}
cached
@@ -260,9 +272,11 @@ private module Cached {
}
cached
- StringLiteral getInstructionStringLiteral(Instruction instruction) {
- result = getInstructionTranslatedElement(instruction)
- .getInstructionStringLiteral(getInstructionTag(instruction))
+ int getInstructionIndex(Instruction instruction) {
+ exists(TranslatedElement element, InstructionTag tag |
+ instructionOrigin(instruction, element, tag) and
+ result = element.getInstructionIndex(tag)
+ )
}
cached
@@ -272,7 +286,7 @@ private module Cached {
}
cached
- Type getInstructionExceptionType(Instruction instruction) {
+ CppType getInstructionExceptionType(Instruction instruction) {
result = getInstructionTranslatedElement(instruction)
.getInstructionExceptionType(getInstructionTag(instruction))
}
@@ -299,6 +313,11 @@ private module Cached {
)
}
+ cached
+ predicate needsUnknownOpaqueType(int byteSize) {
+ exists(TranslatedElement element | element.needsUnknownOpaqueType(byteSize))
+ }
+
cached
int getInstructionResultSize(Instruction instruction) {
exists(TranslatedElement element, InstructionTag tag |
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll
index 74f7b4a2b64..42d6e7db693 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll
@@ -1,2 +1,3 @@
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll
index 1f0f5eaccde..8c60565defc 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll
@@ -1,3 +1,4 @@
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag
import semmle.code.cpp.ir.internal.IRUtilities as IRUtilities
import semmle.code.cpp.ir.internal.TempVariableTag as TTempVariableTag
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll
index 0138af075dc..a75f70dbe2f 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll
@@ -1,4 +1,5 @@
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
import semmle.code.cpp.ir.implementation.Opcode as Opcode
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll
index ddac5692cdf..7b0d33546cf 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll
@@ -26,6 +26,7 @@ newtype TInstructionTag =
UnmodeledDefinitionTag() or
UnmodeledUseTag() or
AliasedDefinitionTag() or
+ AliasedUseTag() or
SwitchBranchTag() or
CallTargetTag() or
CallTag() or
@@ -44,6 +45,7 @@ newtype TInstructionTag =
ConditionValueResultLoadTag() or
BoolConversionConstantTag() or
BoolConversionCompareTag() or
+ ResultCopyTag() or
LoadTag() or // Implicit load due to lvalue-to-rvalue conversion
CatchTag() or
ThrowTag() or
@@ -118,6 +120,8 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = AliasedDefinitionTag() and result = "AliasedDef"
or
+ tag = AliasedUseTag() and result = "AliasedUse"
+ or
tag = SwitchBranchTag() and result = "SwitchBranch"
or
tag = CallTargetTag() and result = "CallTarget"
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll
index e626662ccf2..3c781579cee 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll
@@ -1,3 +1,4 @@
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.internal.Overlap as Overlap
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll
index 14168217d9b..ae9e6f1d3bd 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll
@@ -1,10 +1,12 @@
private import cpp
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.implementation.internal.OperandTag
+private import semmle.code.cpp.ir.internal.CppType
private import semmle.code.cpp.models.interfaces.SideEffect
private import InstructionTag
private import TranslatedElement
private import TranslatedExpr
+private import TranslatedFunction
/**
* The IR translation of a call to a function. The call may be from an actual
@@ -22,6 +24,8 @@ abstract class TranslatedCall extends TranslatedExpr {
id = -1 and result = getCallTarget()
or
result = getArgument(id)
+ or
+ id = getNumberOfArguments() and result = getSideEffects()
}
final override Instruction getFirstInstruction() {
@@ -30,13 +34,10 @@ abstract class TranslatedCall extends TranslatedExpr {
else result = getFirstCallTargetInstruction()
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = CallTag() and
opcode instanceof Opcode::Call and
- resultType = getCallResultType() and
- isGLValue = false
+ resultType = getTypeForPRValue(getCallResultType())
or
hasSideEffect() and
tag = CallSideEffectTag() and
@@ -44,13 +45,12 @@ abstract class TranslatedCall extends TranslatedExpr {
if hasWriteSideEffect()
then (
opcode instanceof Opcode::CallSideEffect and
- resultType instanceof UnknownType
+ resultType = getUnknownType()
) else (
opcode instanceof Opcode::CallReadSideEffect and
- resultType instanceof VoidType
+ resultType = getVoidType()
)
- ) and
- isGLValue = false
+ )
}
override Instruction getChildSuccessor(TranslatedElement child) {
@@ -66,6 +66,9 @@ abstract class TranslatedCall extends TranslatedExpr {
then result = getArgument(argIndex + 1).getFirstInstruction()
else result = getInstruction(CallTag())
)
+ or
+ child = getSideEffects() and
+ result = getParent().getChildSuccessor(this)
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -75,12 +78,19 @@ abstract class TranslatedCall extends TranslatedExpr {
tag = CallTag() and
if hasSideEffect()
then result = getInstruction(CallSideEffectTag())
- else result = getParent().getChildSuccessor(this)
+ else
+ if hasPreciseSideEffect()
+ then result = getSideEffects().getFirstInstruction()
+ else result = getParent().getChildSuccessor(this)
)
or
- hasSideEffect() and
- tag = CallSideEffectTag() and
- result = getParent().getChildSuccessor(this)
+ (
+ hasSideEffect() and
+ tag = CallSideEffectTag() and
+ if hasPreciseSideEffect()
+ then result = getSideEffects().getFirstInstruction()
+ else result = getParent().getChildSuccessor(this)
+ )
)
}
@@ -105,11 +115,11 @@ abstract class TranslatedCall extends TranslatedExpr {
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
}
- final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
tag = CallSideEffectTag() and
hasSideEffect() and
operandTag instanceof SideEffectOperandTag and
- result instanceof UnknownType
+ result = getUnknownType()
}
final override Instruction getResult() { result = getInstruction(CallTag()) }
@@ -165,6 +175,8 @@ abstract class TranslatedCall extends TranslatedExpr {
*/
abstract TranslatedExpr getArgument(int index);
+ abstract int getNumberOfArguments();
+
/**
* If there are any arguments, gets the first instruction of the first
* argument. Otherwise, returns the call instruction.
@@ -191,6 +203,10 @@ abstract class TranslatedCall extends TranslatedExpr {
tag = CallSideEffectTag() and
result = getResult()
}
+
+ predicate hasPreciseSideEffect() { exists(getSideEffects()) }
+
+ TranslatedSideEffects getSideEffects() { result.getCall() = expr }
}
/**
@@ -205,18 +221,12 @@ abstract class TranslatedDirectCall extends TranslatedCall {
final override Instruction getCallTargetResult() { result = getInstruction(CallTargetTag()) }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
- TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue)
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ TranslatedCall.super.hasInstruction(opcode, tag, resultType)
or
tag = CallTargetTag() and
opcode instanceof Opcode::FunctionAddress and
- // The database does not contain a `FunctionType` for a function unless
- // its address was taken, so we'll just use glval instead of
- // glval.
- resultType instanceof UnknownType and
- isGLValue = true
+ resultType = getFunctionGLValueType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -234,7 +244,7 @@ abstract class TranslatedDirectCall extends TranslatedCall {
abstract class TranslatedCallExpr extends TranslatedNonConstantExpr, TranslatedCall {
override Call expr;
- final override Type getCallResultType() { result = getResultType() }
+ final override Type getCallResultType() { result = expr.getType() }
final override predicate hasArguments() { exists(expr.getArgument(0)) }
@@ -245,6 +255,8 @@ abstract class TranslatedCallExpr extends TranslatedNonConstantExpr, TranslatedC
final override TranslatedExpr getArgument(int index) {
result = getTranslatedExpr(expr.getArgument(index).getFullyConverted())
}
+
+ final override int getNumberOfArguments() { result = expr.getNumberOfArguments() }
}
/**
@@ -269,11 +281,11 @@ class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
}
override predicate hasReadSideEffect() {
- not expr.getTarget().(SideEffectFunction).neverReadsMemory()
+ not expr.getTarget().(SideEffectFunction).hasOnlySpecificReadSideEffects()
}
override predicate hasWriteSideEffect() {
- not expr.getTarget().(SideEffectFunction).neverWritesMemory()
+ not expr.getTarget().(SideEffectFunction).hasOnlySpecificWriteSideEffects()
}
}
@@ -295,3 +307,275 @@ class TranslatedStructorCall extends TranslatedFunctionCall {
override predicate hasQualifier() { any() }
}
+
+class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
+ Call expr;
+
+ TranslatedSideEffects() { this = TTranslatedSideEffects(expr) }
+
+ override string toString() { result = "(side effects for " + expr.toString() + ")" }
+
+ override Locatable getAST() { result = expr }
+
+ Call getCall() { result = expr }
+
+ override TranslatedElement getChild(int i) {
+ result = rank[i + 1](TranslatedSideEffect tse, int isWrite, int index |
+ (
+ tse.getCall() = getCall() and
+ tse.getArgumentIndex() = index and
+ if tse.isWrite() then isWrite = 1 else isWrite = 0
+ )
+ |
+ tse order by isWrite, index
+ )
+ }
+
+ override Instruction getChildSuccessor(TranslatedElement te) {
+ exists(int i |
+ getChild(i) = te and
+ if exists(getChild(i + 1))
+ then result = getChild(i + 1).getFirstInstruction()
+ else result = getParent().getChildSuccessor(this)
+ )
+ }
+
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) { none() }
+
+ override Instruction getFirstInstruction() { result = getChild(0).getFirstInstruction() }
+
+ override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
+
+ override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
+
+ override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ none()
+ }
+
+ override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
+ tag = OnlyInstructionTag() and
+ result = getTranslatedExpr(expr).getInstruction(CallTag())
+ }
+
+ /**
+ * Gets the `TranslatedFunction` containing this expression.
+ */
+ final TranslatedFunction getEnclosingFunction() {
+ result = getTranslatedFunction(expr.getEnclosingFunction())
+ }
+
+ /**
+ * Gets the `Function` containing this expression.
+ */
+ override Function getFunction() { result = expr.getEnclosingFunction() }
+}
+
+class TranslatedStructorCallSideEffects extends TranslatedSideEffects {
+ TranslatedStructorCallSideEffects() { getParent().(TranslatedStructorCall).hasQualifier() }
+
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType t) {
+ opcode instanceof Opcode::IndirectMayWriteSideEffect and
+ tag instanceof OnlyInstructionTag and
+ t = getTypeForPRValue(expr.getTarget().getDeclaringType())
+ }
+
+ override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
+ (
+ if exists(getChild(0))
+ then result = getChild(0).getFirstInstruction()
+ else result = getParent().getChildSuccessor(this)
+ ) and
+ tag = OnlyInstructionTag() and
+ kind instanceof GotoEdge
+ }
+
+ override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
+
+ override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ tag instanceof OnlyInstructionTag and
+ operandTag instanceof AddressOperandTag and
+ result = getParent().(TranslatedStructorCall).getQualifierResult()
+ }
+
+ final override int getInstructionIndex(InstructionTag tag) {
+ tag = OnlyInstructionTag() and
+ result = -1
+ }
+}
+
+class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEffect {
+ Call call;
+ Expr arg;
+ int index;
+ boolean write;
+
+ TranslatedSideEffect() { this = TTranslatedArgumentSideEffect(call, arg, index, write) }
+
+ override Locatable getAST() { result = arg }
+
+ Expr getExpr() { result = arg }
+
+ Call getCall() { result = call }
+
+ int getArgumentIndex() { result = index }
+
+ predicate isWrite() { write = true }
+
+ override string toString() {
+ write = true and
+ result = "(write side effect for " + arg.toString() + ")"
+ or
+ write = false and
+ result = "(read side effect for " + arg.toString() + ")"
+ }
+
+ override TranslatedElement getChild(int n) { none() }
+
+ override Instruction getChildSuccessor(TranslatedElement child) { none() }
+
+ override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
+
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
+ isWrite() and
+ hasSpecificWriteSideEffect(opcode) and
+ tag = OnlyInstructionTag() and
+ (
+ opcode instanceof BufferAccessOpcode and
+ type = getUnknownType()
+ or
+ not opcode instanceof BufferAccessOpcode and
+ exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() |
+ if baseType instanceof VoidType
+ then type = getUnknownType()
+ else type = getTypeForPRValueOrUnknown(baseType)
+ )
+ or
+ index = -1 and
+ not arg.getUnspecifiedType() instanceof DerivedType and
+ type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType())
+ )
+ or
+ not isWrite() and
+ hasSpecificReadSideEffect(opcode) and
+ tag = OnlyInstructionTag() and
+ type = getVoidType()
+ }
+
+ override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
+ result = getParent().getChildSuccessor(this) and
+ tag = OnlyInstructionTag() and
+ kind instanceof GotoEdge
+ }
+
+ override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ tag instanceof OnlyInstructionTag and
+ operandTag instanceof AddressOperandTag and
+ result = getTranslatedExpr(arg).getResult()
+ or
+ tag instanceof OnlyInstructionTag and
+ operandTag instanceof SideEffectOperandTag and
+ not isWrite() and
+ result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
+ or
+ tag instanceof OnlyInstructionTag and
+ operandTag instanceof BufferSizeOperandTag and
+ result = getTranslatedExpr(call
+ .getArgument(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
+ .getFullyConverted()).getResult()
+ }
+
+ override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ if hasSpecificReadSideEffect(any(Opcode::BufferReadSideEffect op))
+ then
+ result = getUnknownType() and
+ tag instanceof OnlyInstructionTag and
+ operandTag instanceof SideEffectOperandTag
+ else
+ exists(Type operandType |
+ tag instanceof OnlyInstructionTag and
+ operandType = arg.getType().getUnspecifiedType().(DerivedType).getBaseType() and
+ operandTag instanceof SideEffectOperandTag
+ or
+ tag instanceof OnlyInstructionTag and
+ operandType = arg.getType().getUnspecifiedType() and
+ not operandType instanceof DerivedType and
+ operandTag instanceof SideEffectOperandTag
+ |
+ // If the type we select is an incomplete type (e.g. a forward-declared `struct`), there will
+ // not be a `CppType` that represents that type. In that case, fall back to `UnknownCppType`.
+ result = getTypeForPRValueOrUnknown(operandType)
+ )
+ }
+
+ predicate hasSpecificWriteSideEffect(Opcode op) {
+ exists(boolean buffer, boolean mustWrite |
+ if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
+ then
+ call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, true, mustWrite) and
+ buffer = true and
+ (
+ mustWrite = false and op instanceof Opcode::SizedBufferMayWriteSideEffect
+ or
+ mustWrite = true and op instanceof Opcode::SizedBufferMustWriteSideEffect
+ )
+ else (
+ call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(index, buffer, mustWrite) and
+ (
+ buffer = true and mustWrite = false and op instanceof Opcode::BufferMayWriteSideEffect
+ or
+ buffer = false and mustWrite = false and op instanceof Opcode::IndirectMayWriteSideEffect
+ or
+ buffer = true and mustWrite = true and op instanceof Opcode::BufferMustWriteSideEffect
+ or
+ buffer = false and mustWrite = true and op instanceof Opcode::IndirectMustWriteSideEffect
+ )
+ )
+ )
+ or
+ not call.getTarget() instanceof SideEffectFunction and
+ getArgumentIndex() != -1 and
+ op instanceof Opcode::BufferMayWriteSideEffect
+ or
+ not call.getTarget() instanceof SideEffectFunction and
+ getArgumentIndex() = -1 and
+ op instanceof Opcode::IndirectMayWriteSideEffect
+ }
+
+ predicate hasSpecificReadSideEffect(Opcode op) {
+ exists(boolean buffer |
+ call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(index, buffer) and
+ if exists(call.getTarget().(SideEffectFunction).getParameterSizeIndex(index))
+ then buffer = true and op instanceof Opcode::SizedBufferReadSideEffect
+ else (
+ buffer = true and op instanceof Opcode::BufferReadSideEffect
+ or
+ buffer = false and op instanceof Opcode::IndirectReadSideEffect
+ )
+ )
+ or
+ not call.getTarget() instanceof SideEffectFunction and
+ op instanceof Opcode::BufferReadSideEffect
+ }
+
+ override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
+ tag = OnlyInstructionTag() and
+ result = getTranslatedExpr(call).getInstruction(CallTag())
+ }
+
+ final override int getInstructionIndex(InstructionTag tag) {
+ tag = OnlyInstructionTag() and
+ result = index
+ }
+
+ /**
+ * Gets the `TranslatedFunction` containing this expression.
+ */
+ final TranslatedFunction getEnclosingFunction() {
+ result = getTranslatedFunction(arg.getEnclosingFunction())
+ }
+
+ /**
+ * Gets the `Function` containing this expression.
+ */
+ override Function getFunction() { result = arg.getEnclosingFunction() }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll
index 6c501ef4284..e2fc86db7ce 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll
@@ -1,6 +1,7 @@
private import cpp
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.implementation.internal.OperandTag
+private import semmle.code.cpp.ir.internal.CppType
private import InstructionTag
private import TranslatedElement
private import TranslatedExpr
@@ -37,9 +38,7 @@ abstract class TranslatedFlexibleCondition extends TranslatedCondition, Conditio
final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -105,9 +104,7 @@ abstract class TranslatedBinaryLogicalOperation extends TranslatedNativeConditio
result = getLeftOperand().getFirstInstruction()
}
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -163,13 +160,10 @@ class TranslatedValueCondition extends TranslatedCondition, TTranslatedValueCond
override Instruction getFirstInstruction() { result = getValueExpr().getFirstInstruction() }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = ValueConditionConditionalBranchTag() and
opcode instanceof Opcode::ConditionalBranch and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
}
override Instruction getChildSuccessor(TranslatedElement child) {
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll
index 1efe8cf9f78..87fd781fd95 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll
@@ -1,7 +1,8 @@
private import cpp
private import semmle.code.cpp.ir.implementation.Opcode
-private import semmle.code.cpp.ir.internal.IRUtilities
private import semmle.code.cpp.ir.implementation.internal.OperandTag
+private import semmle.code.cpp.ir.internal.CppType
+private import semmle.code.cpp.ir.internal.IRUtilities
private import InstructionTag
private import TranslatedElement
private import TranslatedExpr
@@ -54,19 +55,15 @@ abstract class TranslatedVariableDeclaration extends TranslatedElement, Initiali
result = getInstruction(InitializerVariableAddressTag())
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerVariableAddressTag() and
opcode instanceof Opcode::VariableAddress and
- resultType = getVariableType(getVariable()) and
- isGLValue = true
+ resultType = getTypeForGLValue(getVariableType(getVariable()))
or
hasUninitializedInstruction() and
tag = InitializerStoreTag() and
opcode instanceof Opcode::Uninitialized and
- resultType = getVariableType(getVariable()) and
- isGLValue = false
+ resultType = getTypeForPRValue(getVariableType(getVariable()))
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -123,7 +120,9 @@ abstract class TranslatedVariableDeclaration extends TranslatedElement, Initiali
private predicate hasUninitializedInstruction() {
not exists(getInitialization()) or
- getInitialization() instanceof TranslatedListInitialization
+ getInitialization() instanceof TranslatedListInitialization or
+ getInitialization() instanceof TranslatedConstructorInitialization or
+ getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
}
}
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 23d3036ca15..f118561546b 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
@@ -3,17 +3,15 @@ import semmle.code.cpp.ir.implementation.raw.IR
private import semmle.code.cpp.ir.IRConfiguration
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.implementation.internal.OperandTag
+private import semmle.code.cpp.ir.internal.CppType
private import semmle.code.cpp.ir.internal.TempVariableTag
private import InstructionTag
private import TranslatedCondition
private import TranslatedFunction
private import TranslatedStmt
+private import TranslatedExpr
private import IRConstruction
-
-/**
- * Gets the built-in `int` type.
- */
-Type getIntType() { result.(IntType).isImplicitlySigned() }
+private import semmle.code.cpp.models.interfaces.SideEffect
/**
* Gets the "real" parent of `expr`. This predicate treats conversions as if
@@ -52,6 +50,9 @@ private predicate ignoreExprAndDescendants(Expr expr) {
// constant value.
isIRConstant(getRealParent(expr))
or
+ // Ignore descendants of `__assume` expressions, since we translated these to `NoOp`.
+ getRealParent(expr) instanceof AssumeExpr
+ or
// The `DestructorCall` node for a `DestructorFieldDestruction` has a `FieldAccess`
// node as its qualifier, but that `FieldAccess` does not have a child of its own.
// We'll ignore that `FieldAccess`, and supply the receiver as part of the calling
@@ -65,8 +66,8 @@ private predicate ignoreExprAndDescendants(Expr expr) {
)
or
// Do not translate input/output variables in GNU asm statements
- getRealParent(expr) instanceof AsmStmt
- or
+ // getRealParent(expr) instanceof AsmStmt
+ // or
ignoreExprAndDescendants(getRealParent(expr)) // recursive case
or
// We do not yet translate destructors properly, so for now we ignore any
@@ -235,6 +236,15 @@ newtype TTranslatedElement =
expr.hasLValueToRValueConversion() and
not ignoreLoad(expr)
} or
+ TTranslatedResultCopy(Expr expr) {
+ not ignoreExpr(expr) and
+ exprNeedsCopyIfNotLoaded(expr) and
+ // Doesn't have a TTranslatedLoad
+ not (
+ expr.hasLValueToRValueConversion() and
+ not ignoreLoad(expr)
+ )
+ } or
// An expression most naturally translated as control flow.
TTranslatedNativeCondition(Expr expr) {
not ignoreExpr(expr) and
@@ -369,7 +379,46 @@ newtype TTranslatedElement =
// An allocation size for a `new` or `new[]` expression
TTranslatedAllocationSize(NewOrNewArrayExpr newExpr) { not ignoreExpr(newExpr) } or
// The declaration/initialization part of a `ConditionDeclExpr`
- TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) }
+ TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
+ // The side effects of a `Call`
+ TTranslatedSideEffects(Call expr) {
+ exists(TTranslatedArgumentSideEffect(expr, _, _, _)) or expr instanceof ConstructorCall
+ } or // A precise side effect of an argument to a `Call`
+ TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
+ (
+ expr = call.getArgument(n).getFullyConverted()
+ or
+ expr = call.getQualifier().getFullyConverted() and
+ n = -1
+ ) and
+ (
+ call.getTarget().(SideEffectFunction).hasSpecificReadSideEffect(n, _) and
+ isWrite = false
+ or
+ call.getTarget().(SideEffectFunction).hasSpecificWriteSideEffect(n, _, _) and
+ isWrite = true
+ or
+ not call.getTarget() instanceof SideEffectFunction and
+ exists(Type t | t = expr.getUnspecifiedType() |
+ t instanceof ArrayType or
+ t instanceof PointerType or
+ t instanceof ReferenceType
+ ) and
+ (
+ isWrite = true or
+ isWrite = false
+ )
+ or
+ not call.getTarget() instanceof SideEffectFunction and
+ n = -1 and
+ (
+ isWrite = true or
+ isWrite = false
+ )
+ ) and
+ not ignoreExpr(expr) and
+ not ignoreExpr(call)
+ }
/**
* Gets the index of the first explicitly initialized element in `initList`
@@ -494,9 +543,7 @@ abstract class TranslatedElement extends TTranslatedElement {
* If the instruction does not return a result, `resultType` should be
* `VoidType`.
*/
- abstract predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- );
+ abstract predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType);
/**
* Gets the `Function` that contains this element.
@@ -536,7 +583,7 @@ abstract class TranslatedElement extends TTranslatedElement {
* `tag` must be unique for each variable generated from the same AST node
* (not just from the same `TranslatedElement`).
*/
- predicate hasTempVariable(TempVariableTag tag, Type type) { none() }
+ predicate hasTempVariable(TempVariableTag tag, CppType type) { none() }
/**
* If the instruction specified by `tag` is a `FunctionInstruction`, gets the
@@ -562,6 +609,12 @@ abstract class TranslatedElement extends TTranslatedElement {
*/
string getInstructionConstantValue(InstructionTag tag) { none() }
+ /**
+ * If the instruction specified by `tag` is an `IndexedInstruction`, gets the
+ * index for that instruction.
+ */
+ int getInstructionIndex(InstructionTag tag) { none() }
+
/**
* If the instruction specified by `tag` is a `PointerArithmeticInstruction`,
* gets the size of the type pointed to by the pointer.
@@ -575,6 +628,8 @@ abstract class TranslatedElement extends TTranslatedElement {
*/
int getInstructionResultSize(InstructionTag tag) { none() }
+ predicate needsUnknownOpaqueType(int byteSize) { none() }
+
/**
* If the instruction specified by `tag` is a `StringConstantInstruction`,
* gets the `StringLiteral` for that instruction.
@@ -590,7 +645,7 @@ abstract class TranslatedElement extends TTranslatedElement {
* If the instruction specified by `tag` is a `CatchByTypeInstruction`,
* gets the type of the exception to be caught.
*/
- Type getInstructionExceptionType(InstructionTag tag) { none() }
+ CppType getInstructionExceptionType(InstructionTag tag) { none() }
/**
* If the instruction specified by `tag` is an `InheritanceConversionInstruction`,
@@ -609,7 +664,7 @@ abstract class TranslatedElement extends TTranslatedElement {
/**
* Gets the type of the memory operand specified by `operandTag` on the the instruction specified by `tag`.
*/
- Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() }
+ CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() }
/**
* Gets the size of the memory operand specified by `operandTag` on the the instruction specified by `tag`.
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 fa8eeb36adb..9db42c742ac 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
@@ -1,6 +1,8 @@
private import cpp
+private import semmle.code.cpp.ir.implementation.IRType
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.implementation.internal.OperandTag
+private import semmle.code.cpp.ir.internal.CppType
private import semmle.code.cpp.ir.internal.TempVariableTag
private import InstructionTag
private import TranslatedCondition
@@ -51,10 +53,21 @@ abstract class TranslatedExpr extends TranslatedElement {
*/
abstract predicate producesExprResult();
+ final CppType getResultType() {
+ if isResultGLValue()
+ then result = getTypeForGLValue(expr.getType())
+ else result = getTypeForPRValue(expr.getType())
+ }
+
/**
- * Gets the type of the result produced by this expression.
+ * Holds if the result of this `TranslatedExpr` is a glvalue.
*/
- final Type getResultType() { result = expr.getUnspecifiedType() }
+ predicate isResultGLValue() {
+ // This implementation is overridden in `TranslatedCoreExpr` to mark them
+ // as glvalues if they have loads on them. It's not overridden in
+ // `TranslatedResultCopy` since result copies never have loads.
+ expr.isGLValueCategory()
+ }
final override Locatable getAST() { result = expr }
@@ -82,33 +95,28 @@ abstract class TranslatedExpr extends TranslatedElement {
abstract class TranslatedCoreExpr extends TranslatedExpr {
final override string toString() { result = expr.toString() }
+ /**
+ * Holds if the result of this `TranslatedExpr` is a glvalue.
+ */
+ override predicate isResultGLValue() {
+ super.isResultGLValue()
+ or
+ // If this TranslatedExpr doesn't produce the result, then it must represent
+ // a glvalue that is then loaded by a TranslatedLoad.
+ hasLoad()
+ }
+
+ final predicate hasLoad() {
+ expr.hasLValueToRValueConversion() and
+ not ignoreLoad(expr)
+ }
+
final override predicate producesExprResult() {
// If there's no load, then this is the only TranslatedExpr for this
// expression.
- not expr.hasLValueToRValueConversion()
- or
- // If we're supposed to ignore the load on this expression, then this
- // is the only TranslatedExpr.
- ignoreLoad(expr)
- }
-
- /**
- * Returns `true` if the result of this `TranslatedExpr` is a glvalue, or
- * `false` if the result is a prvalue.
- *
- * This predicate returns a `boolean` value instead of just a being a plain
- * predicate because all of the subclass predicates that call it require a
- * `boolean` value.
- */
- final boolean isResultGLValue() {
- if
- expr.isGLValueCategory()
- or
- // If this TranslatedExpr doesn't produce the result, then it must represent
- // a glvalue that is then loaded by a TranslatedLoad.
- not producesExprResult()
- then result = true
- else result = false
+ not hasLoad() and
+ // If there's a result copy, then this expression's result is the copy.
+ not exprNeedsCopyIfNotLoaded(expr)
}
}
@@ -120,38 +128,32 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
(
tag = ConditionValueTrueTempAddressTag() or
tag = ConditionValueFalseTempAddressTag() or
tag = ConditionValueResultTempAddressTag()
) and
opcode instanceof Opcode::VariableAddress and
- resultType = getResultType() and
- isGLValue = true
+ resultType = getTypeForGLValue(expr.getType())
or
(
tag = ConditionValueTrueConstantTag() or
tag = ConditionValueFalseConstantTag()
) and
opcode instanceof Opcode::Constant and
- resultType = getResultType() and
- isGLValue = isResultGLValue()
+ resultType = getResultType()
or
(
tag = ConditionValueTrueStoreTag() or
tag = ConditionValueFalseStoreTag()
) and
opcode instanceof Opcode::Store and
- resultType = getResultType() and
- isGLValue = isResultGLValue()
+ resultType = getResultType()
or
tag = ConditionValueResultLoadTag() and
opcode instanceof Opcode::Load and
- resultType = getResultType() and
- isGLValue = isResultGLValue()
+ resultType = getResultType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -212,9 +214,9 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
)
}
- override predicate hasTempVariable(TempVariableTag tag, Type type) {
+ override predicate hasTempVariable(TempVariableTag tag, CppType type) {
tag = ConditionValueTempVar() and
- type = getResultType()
+ type = getTypeForPRValue(expr.getType())
}
override IRVariable getInstructionVariable(InstructionTag tag) {
@@ -262,13 +264,10 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = LoadTag() and
opcode instanceof Opcode::Load and
- resultType = expr.getUnspecifiedType() and
- if expr.isGLValueCategory() then isGLValue = true else isGLValue = false
+ resultType = getResultType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -302,6 +301,48 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
private TranslatedCoreExpr getOperand() { result.getExpr() = expr }
}
+/**
+ * IR translation of an implicit lvalue-to-rvalue conversion on the result of
+ * an expression.
+ */
+class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy {
+ TranslatedResultCopy() { this = TTranslatedResultCopy(expr) }
+
+ override string toString() { result = "Result of " + expr.toString() }
+
+ override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
+
+ override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
+
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ tag = ResultCopyTag() and
+ opcode instanceof Opcode::CopyValue and
+ resultType = getOperand().getResultType()
+ }
+
+ override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
+ tag = ResultCopyTag() and
+ result = getParent().getChildSuccessor(this) and
+ kind instanceof GotoEdge
+ }
+
+ override Instruction getChildSuccessor(TranslatedElement child) {
+ child = getOperand() and result = getInstruction(ResultCopyTag())
+ }
+
+ override Instruction getResult() { result = getInstruction(ResultCopyTag()) }
+
+ override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
+ tag = ResultCopyTag() and
+ operandTag instanceof UnaryOperandTag and
+ result = getOperand().getResult()
+ }
+
+ final override predicate producesExprResult() { any() }
+
+ private TranslatedCoreExpr getOperand() { result.getExpr() = expr }
+}
+
class TranslatedCommaExpr extends TranslatedNonConstantExpr {
override CommaExpr expr;
@@ -324,9 +365,7 @@ class TranslatedCommaExpr extends TranslatedNonConstantExpr {
child = getRightOperand() and result = getParent().getChildSuccessor(this)
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -341,6 +380,10 @@ class TranslatedCommaExpr extends TranslatedNonConstantExpr {
}
}
+private int getElementSize(Type type) {
+ result = max(type.getUnspecifiedType().(PointerType).getBaseType().getSize())
+}
+
abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
override CrementOperation expr;
@@ -349,9 +392,9 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
final override string getInstructionConstantValue(InstructionTag tag) {
tag = CrementConstantTag() and
exists(Type resultType |
- resultType = getResultType() and
+ resultType = expr.getUnspecifiedType() and
(
- resultType instanceof IntegralType and result = "1"
+ resultType instanceof IntegralOrEnumType and result = "1"
or
resultType instanceof FloatingPointType and result = "1.0"
or
@@ -360,38 +403,34 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
)
}
- private Type getConstantType() {
+ private CppType getConstantType() {
exists(Type resultType |
- resultType = getResultType() and
+ resultType = expr.getUnspecifiedType() and
(
- resultType instanceof ArithmeticType and result = resultType
+ resultType instanceof ArithmeticType and
+ result = getTypeForPRValue(expr.getType())
or
resultType instanceof PointerType and result = getIntType()
)
)
}
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
- isGLValue = false and
- (
- tag = CrementLoadTag() and
- opcode instanceof Opcode::Load and
- resultType = getResultType()
- or
- tag = CrementConstantTag() and
- opcode instanceof Opcode::Constant and
- resultType = getConstantType()
- or
- tag = CrementOpTag() and
- opcode = getOpcode() and
- resultType = getResultType()
- or
- tag = CrementStoreTag() and
- opcode instanceof Opcode::Store and
- resultType = getResultType()
- )
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ tag = CrementLoadTag() and
+ opcode instanceof Opcode::Load and
+ resultType = getTypeForPRValue(expr.getType())
+ or
+ tag = CrementConstantTag() and
+ opcode instanceof Opcode::Constant and
+ resultType = getConstantType()
+ or
+ tag = CrementOpTag() and
+ opcode = getOpcode() and
+ resultType = getTypeForPRValue(expr.getType())
+ or
+ tag = CrementStoreTag() and
+ opcode instanceof Opcode::Store and
+ resultType = getTypeForPRValue(expr.getType())
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -452,7 +491,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
getOpcode() instanceof Opcode::PointerAdd or
getOpcode() instanceof Opcode::PointerSub
) and
- result = max(getResultType().(PointerType).getBaseType().getSize())
+ result = getElementSize(expr.getType())
}
final TranslatedExpr getOperand() {
@@ -461,7 +500,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
final Opcode getOpcode() {
exists(Type resultType |
- resultType = getResultType() and
+ resultType = expr.getUnspecifiedType() and
(
(
expr instanceof IncrementOperation and
@@ -542,13 +581,10 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr {
override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::PointerAdd and
- resultType = getResultType() and
- isGLValue = true
+ resultType = getTypeForGLValue(expr.getType())
}
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -564,7 +600,7 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr {
override int getInstructionElementSize(InstructionTag tag) {
tag = OnlyInstructionTag() and
- result = max(getResultType().getSize())
+ result = max(expr.getUnspecifiedType().getSize())
}
private TranslatedExpr getBaseOperand() {
@@ -587,9 +623,7 @@ abstract class TranslatedTransparentExpr extends TranslatedNonConstantExpr {
child = getOperand() and result = getParent().getChildSuccessor(this)
}
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -640,13 +674,10 @@ class TranslatedThisExpr extends TranslatedNonConstantExpr {
final override TranslatedElement getChild(int id) { none() }
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::CopyValue and
- resultType = expr.getUnspecifiedType() and
- isGLValue = false
+ resultType = getResultType()
}
final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
@@ -707,13 +738,10 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess {
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::VariableAddress and
- resultType = getResultType() and
- isGLValue = true
+ resultType = getTypeForGLValue(expr.getType())
}
override IRVariable getInstructionVariable(InstructionTag tag) {
@@ -733,13 +761,10 @@ class TranslatedFieldAccess extends TranslatedVariableAccess {
result = getQualifier().getResult()
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::FieldAddress and
- resultType = getResultType() and
- isGLValue = true
+ resultType = getTypeForGLValue(expr.getType())
}
override Field getInstructionField(InstructionTag tag) {
@@ -763,13 +788,10 @@ class TranslatedFunctionAccess extends TranslatedNonConstantExpr {
kind instanceof GotoEdge
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::FunctionAddress and
- resultType = expr.getUnspecifiedType() and
- isGLValue = true
+ resultType = getResultType()
}
override Function getInstructionFunction(InstructionTag tag) {
@@ -811,15 +833,10 @@ abstract class TranslatedConstantExpr extends TranslatedCoreExpr, TTranslatedVal
none()
}
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode = getOpcode() and
- resultType = getResultType() and
- if expr.isGLValueCategory() or expr.hasLValueToRValueConversion()
- then isGLValue = true
- else isGLValue = false
+ resultType = getResultType()
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -865,13 +882,10 @@ abstract class TranslatedSingleInstructionExpr extends TranslatedNonConstantExpr
*/
abstract Opcode getOpcode();
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
opcode = getOpcode() and
tag = OnlyInstructionTag() and
- resultType = getResultType() and
- isGLValue = isResultGLValue()
+ resultType = getResultType()
}
final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
@@ -945,13 +959,10 @@ abstract class TranslatedSingleInstructionConversion extends TranslatedConversio
child = getOperand() and result = getInstruction(OnlyInstructionTag())
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode = getOpcode() and
- resultType = getResultType() and
- isGLValue = isResultGLValue()
+ resultType = getResultType()
}
override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
@@ -996,7 +1007,7 @@ class TranslatedDynamicCast extends TranslatedSingleInstructionConversion {
override Opcode getOpcode() {
exists(Type resultType |
- resultType = getResultType() and
+ resultType = expr.getUnspecifiedType() and
if resultType instanceof PointerType
then
if resultType.(PointerType).getBaseType() instanceof VoidType
@@ -1027,7 +1038,7 @@ class TranslatedInheritanceConversion extends TranslatedSingleInstructionConvers
then
if expr.(BaseClassConversion).isVirtual()
then result instanceof Opcode::ConvertToVirtualBase
- else result instanceof Opcode::ConvertToBase
+ else result instanceof Opcode::ConvertToNonVirtualBase
else result instanceof Opcode::ConvertToDerived
}
}
@@ -1054,19 +1065,14 @@ class TranslatedBoolConversion extends TranslatedConversion {
child = getOperand() and result = getInstruction(BoolConversionConstantTag())
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
- isGLValue = false and
- (
- tag = BoolConversionConstantTag() and
- opcode instanceof Opcode::Constant and
- resultType = getOperand().getResultType()
- or
- tag = BoolConversionCompareTag() and
- opcode instanceof Opcode::CompareNE and
- resultType instanceof BoolType
- )
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ tag = BoolConversionConstantTag() and
+ opcode instanceof Opcode::Constant and
+ resultType = getOperand().getResultType()
+ or
+ tag = BoolConversionCompareTag() and
+ opcode instanceof Opcode::CompareNE and
+ resultType = getBoolType()
}
override Instruction getResult() { result = getInstruction(BoolConversionCompareTag()) }
@@ -1197,7 +1203,7 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
opcode instanceof Opcode::PointerSub or
opcode instanceof Opcode::PointerDiff
) and
- result = max(getPointerOperand().getResultType().(PointerType).getBaseType().getSize())
+ result = getElementSize(getPointerOperand().getExpr().getType())
)
}
@@ -1282,13 +1288,10 @@ class TranslatedAssignExpr extends TranslatedAssignment {
result = getInstruction(AssignmentStoreTag())
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = AssignmentStoreTag() and
opcode instanceof Opcode::Store and
- resultType = getResultType() and
- isGLValue = false
+ resultType = getTypeForPRValue(expr.getType()) // Always a prvalue
}
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -1365,14 +1368,16 @@ class TranslatedAssignOperation extends TranslatedAssignment {
// anyway. If we really want to model this case perfectly, we'll need the
// extractor to tell us what the promoted type of the left operand would
// be.
- result = getLeftOperand().getResultType()
+ result = getLeftOperand().getExpr().getType()
else
// The right operand has already been converted to the type of the op.
- result = getRightOperand().getResultType()
+ result = getRightOperand().getExpr().getType()
}
private predicate leftOperandNeedsConversion() {
- getConvertedLeftOperandType() != getLeftOperand().getResultType()
+ getConvertedLeftOperandType().getUnspecifiedType() != getLeftOperand()
+ .getExpr()
+ .getUnspecifiedType()
}
private Opcode getOpcode() {
@@ -1401,32 +1406,27 @@ class TranslatedAssignOperation extends TranslatedAssignment {
expr instanceof AssignPointerSubExpr and result instanceof Opcode::PointerSub
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
- isGLValue = false and
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ tag = AssignOperationLoadTag() and
+ opcode instanceof Opcode::Load and
+ resultType = getTypeForPRValue(getLeftOperand().getExpr().getType())
+ or
+ tag = AssignOperationOpTag() and
+ opcode = getOpcode() and
+ resultType = getTypeForPRValue(getConvertedLeftOperandType())
+ or
+ tag = AssignmentStoreTag() and
+ opcode instanceof Opcode::Store and
+ resultType = getTypeForPRValue(expr.getType()) // Always a prvalue
+ or
+ leftOperandNeedsConversion() and
+ opcode instanceof Opcode::Convert and
(
- tag = AssignOperationLoadTag() and
- opcode instanceof Opcode::Load and
- resultType = getLeftOperand().getResultType()
+ tag = AssignOperationConvertLeftTag() and
+ resultType = getTypeForPRValue(getConvertedLeftOperandType())
or
- tag = AssignOperationOpTag() and
- opcode = getOpcode() and
- resultType = getConvertedLeftOperandType()
- or
- tag = AssignmentStoreTag() and
- opcode instanceof Opcode::Store and
- resultType = getResultType()
- or
- leftOperandNeedsConversion() and
- opcode instanceof Opcode::Convert and
- (
- tag = AssignOperationConvertLeftTag() and
- resultType = getConvertedLeftOperandType()
- or
- tag = AssignOperationConvertResultTag() and
- resultType = getLeftOperand().getResultType()
- )
+ tag = AssignOperationConvertResultTag() and
+ resultType = getTypeForPRValue(getLeftOperand().getExpr().getType())
)
}
@@ -1436,7 +1436,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
opcode = getOpcode() and
(opcode instanceof Opcode::PointerAdd or opcode instanceof Opcode::PointerSub)
) and
- result = max(getResultType().(PointerType).getBaseType().getSize())
+ result = getElementSize(expr.getType())
}
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -1518,13 +1518,10 @@ class TranslatedConstantAllocationSize extends TranslatedAllocationSize {
final override Instruction getFirstInstruction() { result = getInstruction(AllocationSizeTag()) }
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = AllocationSizeTag() and
opcode instanceof Opcode::Constant and
- resultType = expr.getAllocator().getParameter(0).getUnspecifiedType() and
- isGLValue = false
+ resultType = getTypeForPRValue(expr.getAllocator().getParameter(0).getType())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -1557,11 +1554,8 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
final override Instruction getFirstInstruction() { result = getExtent().getFirstInstruction() }
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
- isGLValue = false and
- resultType = expr.getAllocator().getParameter(0).getUnspecifiedType() and
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ resultType = getTypeForPRValue(expr.getAllocator().getParameter(0).getType()) and
(
// Convert the extent to `size_t`, because the AST doesn't do this already.
tag = AllocationExtentConvertTag() and opcode instanceof Opcode::Convert
@@ -1633,7 +1627,7 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
tag = CallTargetTag() and result = expr.getAllocator()
}
- final override Type getCallResultType() { result = expr.getAllocator().getUnspecifiedType() }
+ final override Type getCallResultType() { result = expr.getAllocator().getType() }
final override TranslatedExpr getQualifier() { none() }
@@ -1642,6 +1636,10 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
any()
}
+ final override int getNumberOfArguments() {
+ result = expr.getAllocatorCall().getNumberOfArguments()
+ }
+
final override TranslatedExpr getArgument(int index) {
// If the allocator is the default operator new(void*), there will be no
// allocator call in the AST. Otherwise, there will be an allocator call
@@ -1684,13 +1682,10 @@ class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr, St
final override TranslatedElement getChild(int id) { id = 0 and result = getDestructorCall() }
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::FieldAddress and
- resultType = expr.getTarget().getUnspecifiedType() and
- isGLValue = true
+ resultType = getTypeForGLValue(expr.getTarget().getType())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -1737,9 +1732,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
not resultIsVoid() and
(
(
@@ -1750,8 +1743,11 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
tag = ConditionValueResultTempAddressTag()
) and
opcode instanceof Opcode::VariableAddress and
- resultType = getResultType() and
- isGLValue = true
+ (
+ if expr.isGLValueCategory()
+ then resultType = getTypeForGLValue(any(UnknownType t)) // glvalue to a glvalue
+ else resultType = getTypeForGLValue(expr.getType()) // glvalue to the result type
+ )
or
(
not thenIsVoid() and tag = ConditionValueTrueStoreTag()
@@ -1759,13 +1755,11 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
not elseIsVoid() and tag = ConditionValueFalseStoreTag()
) and
opcode instanceof Opcode::Store and
- resultType = getResultType() and
- isGLValue = false
+ resultType = getResultType()
or
tag = ConditionValueResultLoadTag() and
opcode instanceof Opcode::Load and
- resultType = getResultType() and
- isGLValue = isResultGLValue()
+ resultType = getResultType()
)
}
@@ -1833,7 +1827,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
)
}
- override predicate hasTempVariable(TempVariableTag tag, Type type) {
+ override predicate hasTempVariable(TempVariableTag tag, CppType type) {
not resultIsVoid() and
tag = ConditionValueTempVar() and
type = getResultType()
@@ -1893,7 +1887,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
}
private predicate thenIsVoid() {
- getThen().getResultType() instanceof VoidType
+ getThen().getResultType().getIRType() instanceof IRVoidType
or
// A `ThrowExpr.getType()` incorrectly returns the type of exception being
// thrown, rather than `void`. Handle that case here.
@@ -1901,14 +1895,14 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
}
private predicate elseIsVoid() {
- getElse().getResultType() instanceof VoidType
+ getElse().getResultType().getIRType() instanceof IRVoidType
or
// A `ThrowExpr.getType()` incorrectly returns the type of exception being
// thrown, rather than `void`. Handle that case here.
expr.getElse() instanceof ThrowExpr
}
- private predicate resultIsVoid() { getResultType() instanceof VoidType }
+ private predicate resultIsVoid() { getResultType().getIRType() instanceof IRVoidType }
}
/**
@@ -1917,13 +1911,10 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
abstract class TranslatedThrowExpr extends TranslatedNonConstantExpr {
override ThrowExpr expr;
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = ThrowTag() and
opcode = getThrowOpcode() and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -1950,15 +1941,12 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex
result = getInstruction(InitializerVariableAddressTag())
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
- TranslatedThrowExpr.super.hasInstruction(opcode, tag, resultType, isGLValue)
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ TranslatedThrowExpr.super.hasInstruction(opcode, tag, resultType)
or
tag = InitializerVariableAddressTag() and
opcode instanceof Opcode::VariableAddress and
- resultType = getExceptionType() and
- isGLValue = true
+ resultType = getTypeForGLValue(getExceptionType())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -1979,9 +1967,9 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex
result = getIRTempVariable(expr, ThrowTempVar())
}
- final override predicate hasTempVariable(TempVariableTag tag, Type type) {
+ final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
tag = ThrowTempVar() and
- type = getExceptionType()
+ type = getTypeForPRValue(getExceptionType())
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -1995,10 +1983,10 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex
)
}
- final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
tag = ThrowTag() and
operandTag instanceof LoadOperandTag and
- result = getExceptionType()
+ result = getTypeForPRValue(getExceptionType())
}
override Instruction getTargetAddress() {
@@ -2013,7 +2001,7 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex
final override Opcode getThrowOpcode() { result instanceof Opcode::ThrowValue }
- private Type getExceptionType() { result = expr.getUnspecifiedType() }
+ private Type getExceptionType() { result = expr.getType() }
}
/**
@@ -2071,13 +2059,10 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
)
}
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode = getOpcode() and
- resultType = getResultType() and
- isGLValue = isResultGLValue()
+ resultType = getResultType()
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -2144,13 +2129,10 @@ abstract class TranslatedNewOrNewArrayExpr extends TranslatedNonConstantExpr, In
id = 1 and result = getInitialization()
}
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::Convert and
- resultType = getResultType() and
- isGLValue = false
+ resultType = getResultType()
}
final override Instruction getFirstInstruction() {
@@ -2311,9 +2293,7 @@ class TranslatedConditionDeclExpr extends TranslatedNonConstantExpr {
child = getConditionExpr() and result = getParent().getChildSuccessor(this)
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -2362,23 +2342,18 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont
result = getInstruction(LoadTag())
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerVariableAddressTag() and
opcode instanceof Opcode::VariableAddress and
- resultType = getResultType() and
- isGLValue = true
+ resultType = getTypeForGLValue(expr.getType())
or
tag = InitializerStoreTag() and
opcode instanceof Opcode::Uninitialized and
- resultType = getResultType() and
- isGLValue = false
+ resultType = getTypeForPRValue(expr.getType())
or
tag = LoadTag() and
opcode instanceof Opcode::Load and
- resultType = getResultType() and
- isGLValue = false
+ resultType = getTypeForPRValue(expr.getType())
}
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -2404,16 +2379,16 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont
result = getTempVariable(LambdaTempVar())
}
- override predicate hasTempVariable(TempVariableTag tag, Type type) {
+ override predicate hasTempVariable(TempVariableTag tag, CppType type) {
tag = LambdaTempVar() and
- type = getResultType()
+ type = getTypeForPRValue(expr.getType())
}
final override Instruction getTargetAddress() {
result = getInstruction(InitializerVariableAddressTag())
}
- final override Type getTargetType() { result = getResultType() }
+ final override Type getTargetType() { result = expr.getType() }
private predicate hasInitializer() { exists(getInitialization()) }
@@ -2444,13 +2419,10 @@ class TranslatedStmtExpr extends TranslatedNonConstantExpr {
result = getInstruction(OnlyInstructionTag())
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
opcode instanceof Opcode::CopyValue and
tag instanceof OnlyInstructionTag and
- resultType = expr.getType() and
- isGLValue = false
+ resultType = getResultType()
}
override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
@@ -2485,3 +2457,78 @@ class TranslatedErrorExpr extends TranslatedSingleInstructionExpr {
final override Opcode getOpcode() { result instanceof Opcode::Error }
}
+
+/**
+ * Holds if the translation of `expr` will not directly generate any
+ * `Instruction` for use as result. For such instructions we can synthesize a
+ * `CopyValue` instruction to ensure that there is a 1-to-1 mapping between
+ * expressions and result-bearing instructions.
+ */
+// This should ideally be a dispatch predicate on TranslatedNonConstantExpr,
+// but it doesn't look monotonic to QL.
+predicate exprNeedsCopyIfNotLoaded(Expr expr) {
+ (
+ expr instanceof AssignExpr
+ or
+ expr instanceof AssignOperation and
+ not expr.isPRValueCategory() // is C++
+ or
+ expr instanceof PrefixCrementOperation and
+ not expr.isPRValueCategory() // is C++
+ or
+ expr instanceof PointerDereferenceExpr
+ or
+ expr instanceof AddressOfExpr
+ or
+ expr instanceof BuiltInOperationBuiltInAddressOf
+ or
+ // No case for ParenthesisExpr to avoid getting too many instructions
+ expr instanceof ReferenceDereferenceExpr
+ or
+ expr instanceof ReferenceToExpr
+ or
+ expr instanceof CommaExpr
+ or
+ expr instanceof ConditionDeclExpr
+ // TODO: simplify TranslatedStmtExpr too
+ ) and
+ not exprImmediatelyDiscarded(expr)
+}
+
+/**
+ * Holds if `expr` is immediately discarded. Such expressions do not need a
+ * `CopyValue` because it's unlikely that anyone is interested in their value.
+ */
+private predicate exprImmediatelyDiscarded(Expr expr) {
+ exists(ExprStmt s |
+ s = expr.getParent() and
+ not exists(StmtExpr se | s = se.getStmt().(Block).getLastStmt())
+ )
+ or
+ exists(CommaExpr c | c.getLeftOperand() = expr)
+ or
+ exists(ForStmt for | for.getUpdate() = expr)
+}
+
+/**
+ * The IR translation of an `__assume` expression. We currently translate these as `NoOp`. In the
+ * future, we will probably want to do something better. At a minimum, we can model `__assume(0)` as
+ * `Unreached`.
+ */
+class TranslatedAssumeExpr extends TranslatedSingleInstructionExpr {
+ override AssumeExpr expr;
+
+ final override Opcode getOpcode() { result instanceof Opcode::NoOp }
+
+ final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
+
+ final override TranslatedElement getChild(int id) { none() }
+
+ final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
+ tag = OnlyInstructionTag() and
+ result = getParent().getChildSuccessor(this) and
+ kind instanceof GotoEdge
+ }
+
+ final override Instruction getChildSuccessor(TranslatedElement child) { none() }
+}
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 a035f8b9b31..5b437f45c54 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
@@ -1,6 +1,7 @@
private import cpp
import semmle.code.cpp.ir.implementation.raw.IR
private import semmle.code.cpp.ir.implementation.Opcode
+private import semmle.code.cpp.ir.internal.CppType
private import semmle.code.cpp.ir.internal.IRUtilities
private import semmle.code.cpp.ir.implementation.internal.OperandTag
private import semmle.code.cpp.ir.internal.TempVariableTag
@@ -95,6 +96,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
result = getInstruction(UnmodeledUseTag())
or
tag = UnmodeledUseTag() and
+ result = getInstruction(AliasedUseTag())
+ or
+ tag = AliasedUseTag() and
result = getInstruction(ExitFunctionTag())
)
}
@@ -115,55 +119,46 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
or
(
child = getDestructorDestructionList() and
- if getReturnType() instanceof VoidType
- then result = getInstruction(ReturnTag())
- else result = getInstruction(ReturnValueAddressTag())
+ if hasReturnValue()
+ then result = getInstruction(ReturnValueAddressTag())
+ else result = getInstruction(ReturnTag())
)
}
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
(
tag = EnterFunctionTag() and
opcode instanceof Opcode::EnterFunction and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
or
tag = UnmodeledDefinitionTag() and
opcode instanceof Opcode::UnmodeledDefinition and
- resultType instanceof UnknownType and
- isGLValue = false
+ resultType = getUnknownType()
or
tag = AliasedDefinitionTag() and
opcode instanceof Opcode::AliasedDefinition and
- resultType instanceof UnknownType and
- isGLValue = false
+ resultType = getUnknownType()
or
tag = InitializeThisTag() and
opcode instanceof Opcode::InitializeThis and
- resultType = getThisType() and
- isGLValue = true
+ resultType = getTypeForGLValue(getThisType())
or
tag = ReturnValueAddressTag() and
opcode instanceof Opcode::VariableAddress and
- resultType = getReturnType() and
- not resultType instanceof VoidType and
- isGLValue = true
+ resultType = getTypeForGLValue(getReturnType()) and
+ hasReturnValue()
or
(
tag = ReturnTag() and
- resultType instanceof VoidType and
- isGLValue = false and
- if getReturnType() instanceof VoidType
- then opcode instanceof Opcode::ReturnVoid
- else opcode instanceof Opcode::ReturnValue
+ resultType = getVoidType() and
+ if hasReturnValue()
+ then opcode instanceof Opcode::ReturnValue
+ else opcode instanceof Opcode::ReturnVoid
)
or
tag = UnwindTag() and
opcode instanceof Opcode::Unwind and
- resultType instanceof VoidType and
- isGLValue = false and
+ resultType = getVoidType() and
(
// Only generate the `Unwind` instruction if there is any exception
// handling present in the function.
@@ -173,13 +168,15 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
or
tag = UnmodeledUseTag() and
opcode instanceof Opcode::UnmodeledUse and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
+ or
+ tag = AliasedUseTag() and
+ opcode instanceof Opcode::AliasedUse and
+ resultType = getVoidType()
or
tag = ExitFunctionTag() and
opcode instanceof Opcode::ExitFunction and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
)
}
@@ -197,8 +194,12 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
operandTag instanceof UnmodeledUseOperandTag and
result = getUnmodeledDefinitionInstruction()
or
+ tag = AliasedUseTag() and
+ operandTag instanceof SideEffectOperandTag and
+ result = getUnmodeledDefinitionInstruction()
+ or
tag = ReturnTag() and
- not getReturnType() instanceof VoidType and
+ hasReturnValue() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(ReturnValueAddressTag())
@@ -208,11 +209,15 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
)
}
- final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
tag = ReturnTag() and
- not getReturnType() instanceof VoidType and
+ hasReturnValue() and
operandTag instanceof LoadOperandTag and
- result = getReturnType()
+ result = getTypeForPRValue(getReturnType())
+ or
+ tag = AliasedUseTag() and
+ operandTag instanceof SideEffectOperandTag and
+ result = getUnknownType()
}
final override IRVariable getInstructionVariable(InstructionTag tag) {
@@ -220,10 +225,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
result = getReturnVariable()
}
- final override predicate hasTempVariable(TempVariableTag tag, Type type) {
+ final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
tag = ReturnValueTempVar() and
- type = getReturnType() and
- not type instanceof VoidType
+ hasReturnValue() and
+ type = getTypeForPRValue(getReturnType())
}
/**
@@ -241,6 +246,11 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
result = getIRTempVariable(func, ReturnValueTempVar())
}
+ /**
+ * Holds if the function has a non-`void` return type.
+ */
+ final predicate hasReturnValue() { not func.getUnspecifiedType() instanceof VoidType }
+
/**
* Gets the single `UnmodeledDefinition` instruction for this function.
*/
@@ -271,7 +281,7 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
* parameters and local variables, plus any global variables or static data members that are
* directly accessed by the function.
*/
- final predicate hasUserVariable(Variable var, Type type) {
+ final predicate hasUserVariable(Variable var, CppType type) {
(
(
var instanceof GlobalOrNamespaceVariable
@@ -287,10 +297,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
or
var.(Parameter).getCatchBlock().getEnclosingFunction() = func
) and
- type = getVariableType(var)
+ type = getTypeForPRValue(getVariableType(var))
}
- final private Type getReturnType() { result = func.getUnspecifiedType() }
+ final Type getReturnType() { result = func.getType() }
}
/**
@@ -335,18 +345,14 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
final override Instruction getChildSuccessor(TranslatedElement child) { none() }
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerVariableAddressTag() and
opcode instanceof Opcode::VariableAddress and
- resultType = getVariableType(param) and
- isGLValue = true
+ resultType = getTypeForGLValue(getVariableType(param))
or
tag = InitializerStoreTag() and
opcode instanceof Opcode::InitializeParameter and
- resultType = getVariableType(param) and
- isGLValue = false
+ resultType = getTypeForPRValue(getVariableType(param))
}
final override IRVariable getInstructionVariable(InstructionTag tag) {
@@ -404,9 +410,7 @@ class TranslatedConstructorInitList extends TranslatedElement, InitializationCon
else result = getParent().getChildSuccessor(this)
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -469,9 +473,7 @@ class TranslatedDestructorDestructionList extends TranslatedElement,
else result = getParent().getChildSuccessor(this)
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
index 01c29d422f4..609f8276640 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
@@ -1,6 +1,7 @@
private import cpp
private import semmle.code.cpp.ir.implementation.Opcode
private import semmle.code.cpp.ir.implementation.internal.OperandTag
+private import semmle.code.cpp.ir.internal.CppType
private import InstructionTag
private import TranslatedElement
private import TranslatedExpr
@@ -79,9 +80,7 @@ abstract class TranslatedListInitialization extends TranslatedInitialization, In
)
}
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -150,13 +149,10 @@ class TranslatedSimpleDirectInitialization extends TranslatedDirectInitializatio
not expr instanceof StringLiteral
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerStoreTag() and
opcode instanceof Opcode::Store and
- resultType = getContext().getTargetType() and
- isGLValue = false
+ resultType = getTypeForPRValue(getContext().getTargetType())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -188,20 +184,16 @@ class TranslatedSimpleDirectInitialization extends TranslatedDirectInitializatio
class TranslatedStringLiteralInitialization extends TranslatedDirectInitialization {
override StringLiteral expr;
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
// Load the string literal to make it a prvalue of type `char[len]`
tag = InitializerLoadStringTag() and
opcode instanceof Opcode::Load and
- resultType = getInitializer().getResultType() and
- isGLValue = false
+ resultType = getTypeForPRValue(expr.getType())
or
// Store the string into the target.
tag = InitializerStoreTag() and
opcode instanceof Opcode::Store and
- resultType = getInitializer().getResultType() and
- isGLValue = false
+ resultType = getTypeForPRValue(expr.getType())
or
exists(int startIndex, int elementCount |
// If the initializer string isn't large enough to fill the target, then
@@ -213,26 +205,22 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
// space in the target array.
tag = ZeroPadStringConstantTag() and
opcode instanceof Opcode::Constant and
- resultType instanceof UnknownType and
- isGLValue = false
+ resultType = getUnknownOpaqueType(elementCount * getElementType().getSize())
or
// The index of the first element to be zero initialized.
tag = ZeroPadStringElementIndexTag() and
opcode instanceof Opcode::Constant and
- resultType = getIntType() and
- isGLValue = false
+ resultType = getIntType()
or
// Compute the address of the first element to be zero initialized.
tag = ZeroPadStringElementAddressTag() and
opcode instanceof Opcode::PointerAdd and
- resultType = getElementType() and
- isGLValue = true
+ resultType = getTypeForGLValue(getElementType())
or
// Store the constant zero into the remainder of the string.
tag = ZeroPadStringStoreTag() and
opcode instanceof Opcode::Store and
- resultType instanceof UnknownType and
- isGLValue = false
+ resultType = getUnknownOpaqueType(elementCount * getElementType().getSize())
)
)
}
@@ -326,6 +314,13 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
)
}
+ override predicate needsUnknownOpaqueType(int byteSize) {
+ exists(int elementCount |
+ zeroInitRange(_, elementCount) and
+ byteSize = elementCount * getElementType().getSize()
+ )
+ }
+
override int getInstructionResultSize(InstructionTag tag) {
exists(int elementCount |
zeroInitRange(_, elementCount) and
@@ -338,17 +333,17 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
}
private Type getElementType() {
- result = getContext().getTargetType().(ArrayType).getBaseType().getUnspecifiedType()
+ result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType()
}
/**
* Holds if the `elementCount` array elements starting at `startIndex` must be
* zero initialized.
*/
- private predicate zeroInitRange(int startIndex, int elementCount) {
+ predicate zeroInitRange(int startIndex, int elementCount) {
exists(int targetCount |
startIndex = expr.getUnspecifiedType().(ArrayType).getArraySize() and
- targetCount = getContext().getTargetType().(ArrayType).getArraySize() and
+ targetCount = getContext().getTargetType().getUnspecifiedType().(ArrayType).getArraySize() and
elementCount = targetCount - startIndex and
elementCount > 0
)
@@ -359,9 +354,7 @@ class TranslatedConstructorInitialization extends TranslatedDirectInitialization
StructorCallContext {
override ConstructorCall expr;
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -412,13 +405,10 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
*/
final int getOrder() { result = field.getInitializationOrder() }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = getFieldAddressTag() and
opcode instanceof Opcode::FieldAddress and
- resultType = field.getUnspecifiedType() and
- isGLValue = true
+ resultType = getTypeForGLValue(field.getType())
}
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -481,20 +471,16 @@ class TranslatedFieldValueInitialization extends TranslatedFieldInitialization,
TTranslatedFieldValueInitialization {
TranslatedFieldValueInitialization() { this = TTranslatedFieldValueInitialization(ast, field) }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
- TranslatedFieldInitialization.super.hasInstruction(opcode, tag, resultType, isGLValue)
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ TranslatedFieldInitialization.super.hasInstruction(opcode, tag, resultType)
or
tag = getFieldDefaultValueTag() and
opcode instanceof Opcode::Constant and
- resultType = field.getUnspecifiedType() and
- isGLValue = false
+ resultType = getTypeForPRValue(field.getType())
or
tag = getFieldDefaultValueStoreTag() and
opcode instanceof Opcode::Store and
- resultType = field.getUnspecifiedType() and
- isGLValue = false
+ resultType = getTypeForPRValue(field.getUnspecifiedType())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -557,18 +543,14 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
final override Instruction getFirstInstruction() { result = getInstruction(getElementIndexTag()) }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = getElementIndexTag() and
opcode instanceof Opcode::Constant and
- resultType = getIntType() and
- isGLValue = false
+ resultType = getIntType()
or
tag = getElementAddressTag() and
opcode instanceof Opcode::PointerAdd and
- resultType = getElementType() and
- isGLValue = true
+ resultType = getTypeForGLValue(getElementType())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -606,7 +588,7 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
final ArrayOrVectorAggregateLiteral getInitList() { result = initList }
- final Type getElementType() { result = initList.getElementType().getUnspecifiedType() }
+ final Type getElementType() { result = initList.getElementType() }
}
/**
@@ -659,20 +641,16 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
this = TTranslatedElementValueInitialization(initList, elementIndex, elementCount)
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
- TranslatedElementInitialization.super.hasInstruction(opcode, tag, resultType, isGLValue)
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
+ TranslatedElementInitialization.super.hasInstruction(opcode, tag, resultType)
or
tag = getElementDefaultValueTag() and
opcode instanceof Opcode::Constant and
- resultType = getDefaultValueType() and
- isGLValue = false
+ resultType = getDefaultValueType()
or
tag = getElementDefaultValueStoreTag() and
opcode instanceof Opcode::Store and
- resultType = getDefaultValueType() and
- isGLValue = false
+ resultType = getDefaultValueType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -726,6 +704,10 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
override int getElementIndex() { result = elementIndex }
+ override predicate needsUnknownOpaqueType(int byteSize) {
+ elementCount != 0 and byteSize = elementCount * getElementType().getSize()
+ }
+
private InstructionTag getElementDefaultValueTag() {
result = InitializerElementDefaultValueTag()
}
@@ -734,8 +716,10 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
result = InitializerElementDefaultValueStoreTag()
}
- private Type getDefaultValueType() {
- if elementCount = 1 then result = getElementType() else result instanceof UnknownType
+ private CppType getDefaultValueType() {
+ if elementCount = 1
+ then result = getTypeForPRValue(getElementType())
+ else result = getUnknownOpaqueType(elementCount * getElementType().getSize())
}
}
@@ -766,13 +750,10 @@ abstract class TranslatedStructorCallFromStructor extends TranslatedElement, Str
abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStructor {
final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
- opcode instanceof Opcode::ConvertToBase and
- resultType = call.getTarget().getDeclaringType().getUnspecifiedType() and
- isGLValue = true
+ opcode instanceof Opcode::ConvertToNonVirtualBase and
+ resultType = getTypeForGLValue(call.getTarget().getDeclaringType())
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -822,9 +803,7 @@ class TranslatedConstructorDelegationInit extends TranslatedConstructorCallFromC
result = getStructorCall().getFirstInstruction()
}
- final override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
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 d84e9fc1c4e..62cbba8604b 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
@@ -1,6 +1,7 @@
private import cpp
private import semmle.code.cpp.ir.internal.IRUtilities
private import semmle.code.cpp.ir.implementation.internal.OperandTag
+private import semmle.code.cpp.ir.internal.CppType
private import semmle.code.cpp.ir.internal.TempVariableTag
private import InstructionTag
private import TranslatedCondition
@@ -35,13 +36,10 @@ class TranslatedEmptyStmt extends TranslatedStmt {
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::NoOp and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -63,9 +61,7 @@ class TranslatedDeclStmt extends TranslatedStmt {
override TranslatedElement getChild(int id) { result = getDeclarationEntry(id) }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -112,9 +108,7 @@ class TranslatedExprStmt extends TranslatedStmt {
override TranslatedElement getChild(int id) { id = 0 and result = getExpr() }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -145,13 +139,10 @@ class TranslatedReturnValueStmt extends TranslatedReturnStmt, InitializationCont
result = getInstruction(InitializerVariableAddressTag())
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerVariableAddressTag() and
opcode instanceof Opcode::VariableAddress and
- resultType = getEnclosingFunction().getReturnVariable().getType() and
- isGLValue = true
+ resultType = getTypeForGLValue(getEnclosingFunction().getReturnType())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -174,7 +165,7 @@ class TranslatedReturnValueStmt extends TranslatedReturnStmt, InitializationCont
result = getInstruction(InitializerVariableAddressTag())
}
- override Type getTargetType() { result = getEnclosingFunction().getReturnVariable().getType() }
+ override Type getTargetType() { result = getEnclosingFunction().getReturnType() }
TranslatedInitialization getInitialization() {
result = getTranslatedInitialization(stmt.getExpr().getFullyConverted())
@@ -188,13 +179,10 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::NoOp and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -218,9 +206,7 @@ class TranslatedTryStmt extends TranslatedStmt {
result = getHandler(id - 1)
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -262,14 +248,11 @@ class TranslatedBlock extends TranslatedStmt {
override TranslatedElement getChild(int id) { result = getStmt(id) }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
isEmpty() and
opcode instanceof Opcode::NoOp and
tag = OnlyInstructionTag() and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
}
override Instruction getFirstInstruction() {
@@ -330,13 +313,10 @@ abstract class TranslatedHandler extends TranslatedStmt {
class TranslatedCatchByTypeHandler extends TranslatedHandler {
TranslatedCatchByTypeHandler() { exists(stmt.getParameter()) }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = CatchTag() and
opcode instanceof Opcode::CatchByType and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
}
override TranslatedElement getChild(int id) {
@@ -362,9 +342,9 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
)
}
- override Type getInstructionExceptionType(InstructionTag tag) {
+ override CppType getInstructionExceptionType(InstructionTag tag) {
tag = CatchTag() and
- result = stmt.getParameter().getType()
+ result = getTypeForPRValue(stmt.getParameter().getType())
}
private TranslatedParameter getParameter() {
@@ -378,13 +358,10 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
class TranslatedCatchAnyHandler extends TranslatedHandler {
TranslatedCatchAnyHandler() { not exists(stmt.getParameter()) }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = CatchTag() and
opcode instanceof Opcode::CatchAny and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -436,9 +413,7 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
result = getParent().getChildSuccessor(this)
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
}
@@ -466,9 +441,7 @@ abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
id = 1 and result = getBody()
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -597,9 +570,7 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
result = getCondition().getFirstInstruction()
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
none()
}
@@ -649,13 +620,10 @@ class TranslatedJumpStmt extends TranslatedStmt {
override TranslatedElement getChild(int id) { none() }
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = OnlyInstructionTag() and
opcode instanceof Opcode::NoOp and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
@@ -693,13 +661,10 @@ class TranslatedSwitchStmt extends TranslatedStmt {
id = 1 and result = getBody()
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = SwitchBranchTag() and
opcode instanceof Opcode::Switch and
- resultType instanceof VoidType and
- isGLValue = false
+ resultType = getVoidType()
}
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -727,37 +692,20 @@ class TranslatedSwitchStmt extends TranslatedStmt {
class TranslatedAsmStmt extends TranslatedStmt {
override AsmStmt stmt;
- override TranslatedElement getChild(int id) { none() }
+ override TranslatedExpr getChild(int id) {
+ result = getTranslatedExpr(stmt.getChild(id).(Expr).getFullyConverted())
+ }
override Instruction getFirstInstruction() {
- if exists(stmt.getChild(0))
- then result = getInstruction(AsmInputTag(0))
+ if exists(getChild(0))
+ then result = getChild(0).getFirstInstruction()
else result = getInstruction(AsmTag())
}
- override predicate hasInstruction(
- Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue
- ) {
+ override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = AsmTag() and
opcode instanceof Opcode::InlineAsm and
- resultType instanceof UnknownType and
- isGLValue = false
- or
- exists(int index, VariableAccess va |
- tag = AsmInputTag(index) and
- stmt.getChild(index) = va and
- opcode instanceof Opcode::VariableAddress and
- resultType = va.getType().getUnspecifiedType() and
- isGLValue = true
- )
- }
-
- override IRVariable getInstructionVariable(InstructionTag tag) {
- exists(int index |
- tag = AsmInputTag(index) and
- result = getIRUserVariable(stmt.getEnclosingFunction(),
- stmt.getChild(index).(VariableAccess).getTarget())
- )
+ resultType = getUnknownType()
}
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
@@ -768,29 +716,28 @@ class TranslatedAsmStmt extends TranslatedStmt {
exists(int index |
tag = AsmTag() and
operandTag = asmOperand(index) and
- result = getInstruction(AsmInputTag(index))
+ result = getChild(index).getResult()
)
}
- final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
+ final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
tag = AsmTag() and
operandTag instanceof SideEffectOperandTag and
- result instanceof UnknownType
+ result = getUnknownType()
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = AsmTag() and
result = getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
- or
+ }
+
+ override Instruction getChildSuccessor(TranslatedElement child) {
exists(int index |
- tag = AsmInputTag(index) and
- kind instanceof GotoEdge and
- if exists(stmt.getChild(index + 1))
- then result = getInstruction(AsmInputTag(index + 1))
+ child = getChild(index) and
+ if exists(getChild(index + 1))
+ then result = getChild(index + 1).getFirstInstruction()
else result = getInstruction(AsmTag())
)
}
-
- override Instruction getChildSuccessor(TranslatedElement child) { none() }
}
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 278040f8ab8..badd48552a5 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
@@ -5,6 +5,7 @@ import IRVariable
import Operand
private import internal.IRImports as Imports
import Imports::EdgeKind
+import Imports::IRType
import Imports::MemoryAccessKind
private newtype TIRPropertyProvider = MkIRPropertyProvider()
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll
index 3921472dc8e..de66a3c99dc 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll
@@ -1,2 +1,3 @@
private import IR
import InstructionSanity
+import IRTypeSanity
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 a7ca4593ac4..e10e266b2ab 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
@@ -5,6 +5,7 @@ import Imports::TempVariableTag
private import Imports::IRUtilities
private import Imports::TTempVariableTag
private import Imports::TIRVariable
+private import Imports::IRType
IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) {
result.getVariable() = var and
@@ -21,10 +22,26 @@ abstract class IRVariable extends TIRVariable {
abstract string toString();
+ /**
+ * Holds if this variable's value cannot be changed within a function. Currently used for string
+ * literals, but could also apply to `const` global and static variables.
+ */
+ predicate isReadOnly() { none() }
+
/**
* Gets the type of the variable.
*/
- abstract Language::Type getType();
+ final Language::Type getType() { getLanguageType().hasType(result, false) }
+
+ /**
+ * Gets the language-neutral type of the variable.
+ */
+ final IRType getIRType() { result = getLanguageType().getIRType() }
+
+ /**
+ * Gets the type of the variable.
+ */
+ abstract Language::LanguageType getLanguageType();
/**
* Gets the AST node that declared this variable, or that introduced this
@@ -59,7 +76,7 @@ abstract class IRVariable extends TIRVariable {
*/
class IRUserVariable extends IRVariable, TIRUserVariable {
Language::Variable var;
- Language::Type type;
+ Language::LanguageType type;
IRUserVariable() { this = TIRUserVariable(var, type, func) }
@@ -71,7 +88,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
result = getVariable().toString() + " " + getVariable().getLocation().toString()
}
- final override Language::Type getType() { result = type }
+ final override Language::LanguageType getLanguageType() { result = type }
/**
* Gets the original user-declared variable.
@@ -102,34 +119,43 @@ class IRStaticUserVariable extends IRUserVariable {
final override Language::StaticVariable getVariable() { result = var }
}
+abstract class IRGeneratedVariable extends IRVariable {
+ Language::AST ast;
+ Language::LanguageType type;
+
+ final override Language::LanguageType getLanguageType() { result = type }
+
+ final override Language::AST getAST() { result = ast }
+
+ override string toString() { result = getBaseString() + getLocationString() }
+
+ override string getUniqueId() { none() }
+
+ final string getLocationString() {
+ result = ast.getLocation().getStartLine().toString() + ":" +
+ ast.getLocation().getStartColumn().toString()
+ }
+
+ string getBaseString() { none() }
+}
+
IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
result.getAST() = ast and
result.getTag() = tag
}
-class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable {
- Language::AST ast;
+class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
TempVariableTag tag;
- Language::Type type;
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
- final override Language::Type getType() { result = type }
-
- final override Language::AST getAST() { result = ast }
-
final override string getUniqueId() {
result = "Temp: " + Construction::getTempVariableUniqueId(this)
}
final TempVariableTag getTag() { result = tag }
- override string toString() {
- result = getBaseString() + ast.getLocation().getStartLine().toString() + ":" +
- ast.getLocation().getStartColumn().toString()
- }
-
- string getBaseString() { result = "#temp" }
+ override string getBaseString() { result = "#temp" }
}
class IRReturnVariable extends IRTempVariable {
@@ -143,3 +169,19 @@ class IRThrowVariable extends IRTempVariable {
override string getBaseString() { result = "#throw" }
}
+
+class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
+ Language::StringLiteral literal;
+
+ IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
+
+ final override predicate isReadOnly() { any() }
+
+ final override string getUniqueId() {
+ result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
+ }
+
+ override string getBaseString() { result = "#string" }
+
+ final Language::StringLiteral getLiteral() { result = literal }
+}
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 43f9663d2cd..e3c78f476e9 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
@@ -5,6 +5,7 @@ import IRVariable
import Operand
private import internal.InstructionImports as Imports
import Imports::EdgeKind
+import Imports::IRType
import Imports::MemoryAccessKind
import Imports::Opcode
private import Imports::OperandTag
@@ -30,7 +31,7 @@ module InstructionSanity {
or
opcode instanceof MemoryAccessOpcode and tag instanceof AddressOperandTag
or
- opcode instanceof BufferAccessOpcode and tag instanceof BufferSizeOperand
+ opcode instanceof SizedBufferAccessOpcode and tag instanceof BufferSizeOperandTag
or
opcode instanceof OpcodeWithCondition and tag instanceof ConditionOperandTag
or
@@ -48,8 +49,9 @@ module InstructionSanity {
or
(
opcode instanceof ReadSideEffectOpcode or
- opcode instanceof MayWriteSideEffectOpcode or
- opcode instanceof Opcode::InlineAsm
+ opcode instanceof Opcode::InlineAsm or
+ opcode instanceof Opcode::CallSideEffect or
+ opcode instanceof Opcode::AliasedUse
) and
tag instanceof SideEffectOperandTag
)
@@ -113,13 +115,34 @@ module InstructionSanity {
}
query predicate missingOperandType(Operand operand, string message) {
- exists(Language::Function func |
+ exists(Language::Function func, Instruction use |
not exists(operand.getType()) and
- func = operand.getUse().getEnclosingFunction() and
- message = "Operand missing type in function '" + Language::getIdentityString(func) + "'."
+ use = operand.getUse() and
+ func = use.getEnclosingFunction() and
+ message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString()
+ + "' missing type in function '" + Language::getIdentityString(func) + "'."
)
}
+ query predicate duplicateChiOperand(
+ ChiInstruction chi, string message, IRFunction func, string funcText
+ ) {
+ 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())
+ }
+
+ query predicate sideEffectWithoutPrimary(
+ SideEffectInstruction instr, string message, IRFunction func, string funcText
+ ) {
+ not exists(instr.getPrimaryInstruction()) and
+ message = "Side effect instruction missing primary instruction in function $@" and
+ func = instr.getEnclosingIRFunction() and
+ funcText = Language::getIdentityString(func.getFunction())
+ }
+
/**
* Holds if an instruction, other than `ExitFunction`, has no successors.
*/
@@ -251,6 +274,7 @@ module InstructionSanity {
) {
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
not useOperand.getUse() instanceof UnmodeledUseInstruction and
+ not defInstr instanceof UnmodeledDefinitionInstruction and
pointOfEvaluation(useOperand, useBlock, useIndex) and
defInstr = useOperand.getAnyDef() and
(
@@ -312,7 +336,7 @@ class Instruction extends Construction::TInstruction {
}
private string getResultPrefix() {
- if getResultType() instanceof Language::VoidType
+ if getResultIRType() instanceof IRVoidType
then result = "v"
else
if hasMemoryResult()
@@ -344,23 +368,6 @@ class Instruction extends Construction::TInstruction {
)
}
- bindingset[type]
- private string getValueCategoryString(string type) {
- if isGLValue() then result = "glval<" + type + ">" else result = type
- }
-
- string getResultTypeString() {
- exists(string valcat |
- valcat = getValueCategoryString(getResultType().toString()) and
- if
- getResultType() instanceof Language::UnknownType and
- not isGLValue() and
- exists(getResultSize())
- then result = valcat + "[" + getResultSize().toString() + "]"
- else result = valcat
- )
- }
-
/**
* Gets a human-readable string that uniquely identifies this instruction
* within the function. This string is used to refer to this instruction when
@@ -380,7 +387,9 @@ class Instruction extends Construction::TInstruction {
*
* Example: `r1_1(int*)`
*/
- final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" }
+ final string getResultString() {
+ result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
+ }
/**
* Gets a string describing the operands of this instruction, suitable for
@@ -448,6 +457,16 @@ class Instruction extends Construction::TInstruction {
result = Construction::getInstructionUnconvertedResultExpression(this)
}
+ final Language::LanguageType getResultLanguageType() {
+ result = Construction::getInstructionResultType(this)
+ }
+
+ /**
+ * Gets the type of the result produced by this instruction. If the instruction does not produce
+ * a result, its result type will be `IRVoidType`.
+ */
+ final IRType getResultIRType() { result = getResultLanguageType().getIRType() }
+
/**
* Gets the type of the result produced by this instruction. If the
* instruction does not produce a result, its result type will be `VoidType`.
@@ -455,7 +474,16 @@ class Instruction extends Construction::TInstruction {
* If `isGLValue()` holds, then the result type of this instruction should be
* thought of as "pointer to `getResultType()`".
*/
- final Language::Type getResultType() { Construction::instructionHasType(this, result, _) }
+ final Language::Type getResultType() {
+ exists(Language::LanguageType resultType |
+ resultType = getResultLanguageType() and
+ (
+ resultType.hasUnspecifiedType(result, _)
+ or
+ not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType
+ )
+ )
+ }
/**
* Holds if the result produced by this instruction is a glvalue. If this
@@ -475,7 +503,7 @@ class Instruction extends Construction::TInstruction {
* result of the `Load` instruction is a prvalue of type `int`, representing
* the integer value loaded from variable `x`.
*/
- final predicate isGLValue() { Construction::instructionHasType(this, _, true) }
+ final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) }
/**
* Gets the size of the result produced by this instruction, in bytes. If the
@@ -484,16 +512,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() {
- if isGLValue()
- then
- // a glvalue is always pointer-sized.
- result = Language::getPointerSize()
- else
- if getResultType() instanceof Language::UnknownType
- then result = Construction::getInstructionResultSize(this)
- else result = Language::getTypeSize(getResultType())
- }
+ final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() }
/**
* Gets the opcode that specifies the operation performed by this instruction.
@@ -609,9 +628,14 @@ class VariableInstruction extends Instruction {
VariableInstruction() { var = Construction::getInstructionVariable(this) }
- final override string getImmediateString() { result = var.toString() }
+ override string getImmediateString() { result = var.toString() }
- final IRVariable getVariable() { result = var }
+ final IRVariable getIRVariable() { result = var }
+
+ /**
+ * Gets the AST variable that this instruction's IR variable refers to, if one exists.
+ */
+ final Language::Variable getASTVariable() { result = var.(IRUserVariable).getVariable() }
}
class FieldInstruction extends Instruction {
@@ -644,6 +668,16 @@ class ConstantValueInstruction extends Instruction {
final string getValue() { result = value }
}
+class IndexedInstruction extends Instruction {
+ int index;
+
+ IndexedInstruction() { index = Construction::getInstructionIndex(this) }
+
+ final override string getImmediateString() { result = index.toString() }
+
+ final int getIndex() { result = index }
+}
+
class EnterFunctionInstruction extends Instruction {
EnterFunctionInstruction() { getOpcode() instanceof Opcode::EnterFunction }
}
@@ -784,14 +818,12 @@ class FloatConstantInstruction extends ConstantInstruction {
FloatConstantInstruction() { getResultType() instanceof Language::FloatingPointType }
}
-class StringConstantInstruction extends Instruction {
- Language::StringLiteral value;
+class StringConstantInstruction extends VariableInstruction {
+ override IRStringLiteral var;
- StringConstantInstruction() { value = Construction::getInstructionStringLiteral(this) }
+ final override string getImmediateString() { result = Language::getStringLiteralText(getValue()) }
- final override string getImmediateString() { result = Language::getStringLiteralText(value) }
-
- final Language::StringLiteral getValue() { result = value }
+ final Language::StringLiteral getValue() { result = var.getLiteral() }
}
class BinaryInstruction extends Instruction {
@@ -959,14 +991,22 @@ class InheritanceConversionInstruction extends UnaryInstruction {
* to the address of a direct non-virtual base class.
*/
class ConvertToBaseInstruction extends InheritanceConversionInstruction {
- ConvertToBaseInstruction() { getOpcode() instanceof Opcode::ConvertToBase }
+ 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.
+ */
+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.
*/
-class ConvertToVirtualBaseInstruction extends InheritanceConversionInstruction {
+class ConvertToVirtualBaseInstruction extends ConvertToBaseInstruction {
ConvertToVirtualBaseInstruction() { getOpcode() instanceof Opcode::ConvertToVirtualBase }
}
@@ -1175,6 +1215,8 @@ class CallReadSideEffectInstruction extends SideEffectInstruction {
*/
class IndirectReadSideEffectInstruction extends SideEffectInstruction {
IndirectReadSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectReadSideEffect }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
}
/**
@@ -1182,13 +1224,39 @@ class IndirectReadSideEffectInstruction extends SideEffectInstruction {
*/
class BufferReadSideEffectInstruction extends SideEffectInstruction {
BufferReadSideEffectInstruction() { getOpcode() instanceof Opcode::BufferReadSideEffect }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
+}
+
+/**
+ * An instruction representing the read of an indirect buffer parameter within a function call.
+ */
+class SizedBufferReadSideEffectInstruction extends SideEffectInstruction {
+ SizedBufferReadSideEffectInstruction() {
+ getOpcode() instanceof Opcode::SizedBufferReadSideEffect
+ }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
+
+ Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+}
+
+/**
+ * An instruction representing a side effect of a function call.
+ */
+class WriteSideEffectInstruction extends SideEffectInstruction {
+ WriteSideEffectInstruction() { getOpcode() instanceof WriteSideEffectOpcode }
+
+ Instruction getArgumentDef() { result = getAnOperand().(AddressOperand).getDef() }
}
/**
* An instruction representing the write of an indirect parameter within a function call.
*/
-class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
- IndirectWriteSideEffectInstruction() { getOpcode() instanceof Opcode::IndirectWriteSideEffect }
+class IndirectMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
+ IndirectMustWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::IndirectMustWriteSideEffect
+ }
final override MemoryAccessKind getResultMemoryAccess() { result instanceof IndirectMemoryAccess }
}
@@ -1197,18 +1265,34 @@ class IndirectWriteSideEffectInstruction extends SideEffectInstruction {
* An instruction representing the write of an indirect buffer parameter within a function call. The
* entire buffer is overwritten.
*/
-class BufferWriteSideEffectInstruction extends SideEffectInstruction {
- BufferWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferWriteSideEffect }
+class BufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
+ BufferMustWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::BufferMustWriteSideEffect
+ }
final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
}
+/**
+ * An instruction representing the write of an indirect buffer parameter within a function call. The
+ * entire buffer is overwritten.
+ */
+class SizedBufferMustWriteSideEffectInstruction extends WriteSideEffectInstruction {
+ SizedBufferMustWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::SizedBufferMustWriteSideEffect
+ }
+
+ final override MemoryAccessKind getResultMemoryAccess() { result instanceof BufferMemoryAccess }
+
+ Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).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.
*/
-class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
+class IndirectMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
IndirectMayWriteSideEffectInstruction() {
getOpcode() instanceof Opcode::IndirectMayWriteSideEffect
}
@@ -1222,7 +1306,7 @@ class IndirectMayWriteSideEffectInstruction extends SideEffectInstruction {
* 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 SideEffectInstruction {
+class BufferMayWriteSideEffectInstruction extends WriteSideEffectInstruction {
BufferMayWriteSideEffectInstruction() { getOpcode() instanceof Opcode::BufferMayWriteSideEffect }
final override MemoryAccessKind getResultMemoryAccess() {
@@ -1230,6 +1314,22 @@ class BufferMayWriteSideEffectInstruction extends SideEffectInstruction {
}
}
+/**
+ * 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 {
+ SizedBufferMayWriteSideEffectInstruction() {
+ getOpcode() instanceof Opcode::SizedBufferMayWriteSideEffect
+ }
+
+ final override MemoryAccessKind getResultMemoryAccess() {
+ result instanceof BufferMayMemoryAccess
+ }
+
+ Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
+}
+
/**
* An instruction representing a GNU or MSVC inline assembly statement.
*/
@@ -1300,7 +1400,7 @@ class CatchInstruction extends Instruction {
* An instruction that catches an exception of a specific type.
*/
class CatchByTypeInstruction extends CatchInstruction {
- Language::Type exceptionType;
+ Language::LanguageType exceptionType;
CatchByTypeInstruction() {
getOpcode() instanceof Opcode::CatchByType and
@@ -1312,7 +1412,7 @@ class CatchByTypeInstruction extends CatchInstruction {
/**
* Gets the type of exception to be caught.
*/
- final Language::Type getExceptionType() { result = exceptionType }
+ final Language::LanguageType getExceptionType() { result = exceptionType }
}
/**
@@ -1339,6 +1439,13 @@ class AliasedDefinitionInstruction extends Instruction {
final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess }
}
+/**
+ * An instruction that consumes all escaped memory on exit from the function.
+ */
+class AliasedUseInstruction extends Instruction {
+ AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
+}
+
class UnmodeledUseInstruction extends Instruction {
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
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 1ced8ef3282..07dd107bf12 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,9 +1,10 @@
private import internal.IRInternal
-import Instruction
-import IRBlock
+private import Instruction
+private import IRBlock
private import internal.OperandImports as Imports
-import Imports::MemoryAccessKind
-import Imports::Overlap
+private import Imports::MemoryAccessKind
+private import Imports::IRType
+private import Imports::Overlap
private import Imports::OperandTag
cached
@@ -143,22 +144,40 @@ class Operand extends TOperand {
* the definition type, such as in the case of a partial read or a read from a pointer that
* has been cast to a different type.
*/
- Language::Type getType() { result = getAnyDef().getResultType() }
+ Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() }
+
+ /**
+ * Gets the language-neutral type of the value consumed by this operand. This is usually the same
+ * as the result type of the definition instruction consumed by this operand. For register
+ * operands, this is always the case. For some memory operands, the operand type may be different
+ * from the definition type, such as in the case of a partial read or a read from a pointer that
+ * has been cast to a different type.
+ */
+ final IRType getIRType() { result = getLanguageType().getIRType() }
+
+ /**
+ * Gets the type of the value consumed by this operand. This is usually the same as the
+ * result type of the definition instruction consumed by this operand. For register operands,
+ * this is always the case. For some memory operands, the operand type may be different from
+ * the definition type, such as in the case of a partial read or a read from a pointer that
+ * has been cast to a different type.
+ */
+ final Language::Type getType() { getLanguageType().hasType(result, _) }
/**
* Holds if the value consumed by this operand is a glvalue. If this
* holds, the value of the operand represents the address of a location,
* and the type of the location is given by `getType()`. If this does
* not hold, the value of the operand represents a value whose type is
- * given by `getResultType()`.
+ * given by `getType()`.
*/
- predicate isGLValue() { getAnyDef().isGLValue() }
+ final predicate isGLValue() { getLanguageType().hasType(_, true) }
/**
* Gets the size of the value consumed by this operand, in bytes. If the operand does not have
* a known constant size, this predicate does not hold.
*/
- int getSize() { result = Language::getTypeSize(getType()) }
+ final int getSize() { result = getLanguageType().getByteSize() }
}
/**
@@ -170,11 +189,6 @@ class MemoryOperand extends Operand {
this = TPhiOperand(_, _, _, _)
}
- override predicate isGLValue() {
- // A `MemoryOperand` can never be a glvalue
- none()
- }
-
/**
* Gets the kind of memory access performed by the operand.
*/
@@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe
class TypedOperand extends NonPhiMemoryOperand {
override TypedOperandTag tag;
- final override Language::Type getType() {
+ final override Language::LanguageType getLanguageType() {
result = Construction::getInstructionOperandType(useInstr, tag)
}
}
@@ -254,6 +268,16 @@ class AddressOperand extends RegisterOperand {
override string toString() { result = "Address" }
}
+/**
+ * The buffer size operand of an instruction that represents a read or write of
+ * a buffer.
+ */
+class BufferSizeOperand extends RegisterOperand {
+ override BufferSizeOperandTag tag;
+
+ override string toString() { result = "BufferSize" }
+}
+
/**
* The source value operand of an instruction that loads a value from memory (e.g. `Load`,
* `ReturnValue`, `ThrowValue`).
@@ -371,13 +395,10 @@ class PositionalArgumentOperand extends ArgumentOperand {
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
- final override int getSize() {
- if getType() instanceof Language::UnknownType
- then result = Construction::getInstructionOperandSize(useInstr, tag)
- else result = Language::getTypeSize(getType())
- }
-
override MemoryAccessKind getMemoryAccess() {
+ useInstr instanceof AliasedUseInstruction and
+ result instanceof NonLocalMayMemoryAccess
+ or
useInstr instanceof CallSideEffectInstruction and
result instanceof EscapedMayMemoryAccess
or
@@ -390,10 +411,10 @@ class SideEffectOperand extends TypedOperand {
useInstr instanceof BufferReadSideEffectInstruction and
result instanceof BufferMemoryAccess
or
- useInstr instanceof IndirectWriteSideEffectInstruction and
+ useInstr instanceof IndirectMustWriteSideEffectInstruction and
result instanceof IndirectMemoryAccess
or
- useInstr instanceof BufferWriteSideEffectInstruction and
+ useInstr instanceof BufferMustWriteSideEffectInstruction and
result instanceof BufferMemoryAccess
or
useInstr instanceof IndirectMayWriteSideEffectInstruction and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll
index e3e52df8931..79e5a2b6713 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll
@@ -1,5 +1,5 @@
private import internal.ValueNumberingInternal
-private import cpp
+private import internal.ValueNumberingImports
private import IR
/**
@@ -23,31 +23,32 @@ newtype TValueNumber =
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
- TConstantValueNumber(IRFunction irFunc, Type type, string value) {
+ TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
- TStringConstantValueNumber(IRFunction irFunc, Type type, string value) {
+ TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
stringConstantValueNumber(_, irFunc, type, value)
} or
- TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) {
+ TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) {
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
- IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand
+ IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
- IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand,
+ IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
ValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
} or
- TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) {
+ TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, type, operand)
} or
TInheritanceConversionValueNumber(
- IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand
+ IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
+ ValueNumber operand
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
@@ -59,7 +60,7 @@ newtype TValueNumber =
class ValueNumber extends TValueNumber {
final string toString() { result = getExampleInstruction().getResultId() }
- final Location getLocation() { result = getExampleInstruction().getLocation() }
+ final Language::Location getLocation() { result = getExampleInstruction().getLocation() }
/**
* Gets the instructions that have been assigned this value number. This will always produce at
@@ -135,14 +136,14 @@ private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getVariable() = var
+ instr.getIRVariable() = var
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getVariable() = var
+ instr.getIRVariable() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
@@ -150,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
}
private predicate constantValueNumber(
- ConstantInstruction instr, IRFunction irFunc, Type type, string value
+ ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
instr.getValue() = value
}
private predicate stringConstantValueNumber(
- StringConstantInstruction instr, IRFunction irFunc, Type type, string value
+ StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
private predicate fieldAddressValueNumber(
- FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress
+ FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
@@ -174,43 +175,43 @@ private predicate fieldAddressValueNumber(
}
private predicate binaryValueNumber(
- BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand,
+ BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand,
ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
- PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize,
- ValueNumber leftOperand, ValueNumber rightOperand
+ PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
+ int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
instr.getElementSize() = elementSize and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
- UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand
+ UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
instr.getOpcode() = opcode and
- instr.getResultType() = type and
+ instr.getResultIRType() = type and
valueNumber(instr.getUnary()) = operand
}
private predicate inheritanceConversionValueNumber(
- InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass,
- Class derivedClass, ValueNumber operand
+ InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
+ Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
@@ -225,7 +226,7 @@ private predicate inheritanceConversionValueNumber(
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
- not instr.getResultType() instanceof VoidType and
+ not instr.getResultIRType() instanceof IRVoidType and
not numberableInstruction(instr)
}
@@ -269,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) {
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
- exists(Type type, string value |
+ exists(IRType type, string value |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
- exists(Type type, string value |
+ exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
- exists(Field field, ValueNumber objectAddress |
+ exists(Language::Field field, ValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
- exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand |
+ exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
)
or
- exists(Opcode opcode, Type type, ValueNumber operand |
+ exists(Opcode opcode, IRType type, ValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, type, operand) and
result = TUnaryValueNumber(irFunc, opcode, type, operand)
)
or
- exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand |
+ exists(
+ Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
+ |
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(
- Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
+ Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
+ ValueNumber rightOperand
|
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
rightOperand) and
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll
new file mode 100644
index 00000000000..0282f6887f1
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll
@@ -0,0 +1,2 @@
+import semmle.code.cpp.ir.internal.Overlap
+import semmle.code.cpp.ir.internal.IRCppLanguage as Language
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 dc50ef01354..6ecfd1470bb 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
@@ -109,7 +109,7 @@ private predicate operandIsPropagated(Operand operand, IntValue bitOffset) {
instr = operand.getUse() and
(
// Converting to a non-virtual base class adds the offset of the base class.
- exists(ConvertToBaseInstruction convert |
+ exists(ConvertToNonVirtualBaseInstruction convert |
convert = instr and
bitOffset = Ints::mul(convert.getDerivation().getByteOffset(), 8)
)
@@ -282,7 +282,7 @@ private predicate automaticVariableAddressEscapes(IRAutomaticVariable var) {
// The variable's address escapes if the result of any
// VariableAddressInstruction that computes the variable's address escapes.
exists(VariableAddressInstruction instr |
- instr.getVariable() = var and
+ instr.getIRVariable() = var and
resultEscapesNonReturn(instr)
)
}
@@ -305,7 +305,11 @@ predicate variableAddressEscapes(IRVariable var) {
*/
predicate resultPointsTo(Instruction instr, IRVariable var, IntValue bitOffset) {
// The address of a variable points to that variable, at offset 0.
- instr.(VariableAddressInstruction).getVariable() = var and
+ instr.(VariableAddressInstruction).getIRVariable() = var and
+ bitOffset = 0
+ or
+ // A string literal is just a special read-only global variable.
+ instr.(StringConstantInstruction).getIRVariable() = var and
bitOffset = 0
or
exists(Operand operand, IntValue originalBitOffset, IntValue propagatedBitOffset |
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll
index 74f7b4a2b64..42d6e7db693 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll
@@ -1,2 +1,3 @@
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll
index 1f0f5eaccde..8c60565defc 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll
@@ -1,3 +1,4 @@
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag
import semmle.code.cpp.ir.internal.IRUtilities as IRUtilities
import semmle.code.cpp.ir.internal.TempVariableTag as TTempVariableTag
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll
index 0138af075dc..a75f70dbe2f 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll
@@ -1,4 +1,5 @@
import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
import semmle.code.cpp.ir.implementation.Opcode as Opcode
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll
index e626662ccf2..3c781579cee 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll
@@ -1,3 +1,4 @@
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
+import semmle.code.cpp.ir.implementation.IRType as IRType
import semmle.code.cpp.ir.internal.Overlap as Overlap
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll
index a5c09c6401b..23121523a6b 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll
@@ -4,34 +4,52 @@ private import Alias
private import SSAConstruction
private import DebugSSA
+bindingset[offset]
+private string getKeySuffixForOffset(int offset) {
+ if offset % 2 = 0 then result = "" else result = "_Chi"
+}
+
+bindingset[offset]
+private int getIndexForOffset(int offset) { result = offset / 2 }
+
/**
* Property provide that dumps the memory access of each result. Useful for debugging SSA
* construction.
*/
class PropertyProvider extends IRPropertyProvider {
override string getInstructionProperty(Instruction instruction, string key) {
- exists(MemoryLocation location |
- location = getResultMemoryLocation(instruction) and
- (
- key = "ResultMemoryLocation" and result = location.toString()
- or
- key = "ResultVirtualVariable" and result = location.getVirtualVariable().toString()
+ key = "ResultMemoryLocation" and
+ result = strictconcat(MemoryLocation loc |
+ loc = getResultMemoryLocation(instruction)
+ |
+ loc.toString(), ","
)
- )
or
- exists(MemoryLocation location |
- location = getOperandMemoryLocation(instruction.getAnOperand()) and
- (
- key = "OperandMemoryAccess" and result = location.toString()
- or
- key = "OperandVirtualVariable" and result = location.getVirtualVariable().toString()
+ key = "ResultVirtualVariable" and
+ result = strictconcat(MemoryLocation loc |
+ loc = getResultMemoryLocation(instruction)
+ |
+ loc.getVirtualVariable().toString(), ","
)
- )
or
- exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
- hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
- defBlock.getInstruction(defIndex) = instruction and
- key = "DefinitionRank[" + useLocation.toString() + "]" and
+ key = "OperandMemoryLocation" and
+ result = strictconcat(MemoryLocation loc |
+ loc = getOperandMemoryLocation(instruction.getAnOperand())
+ |
+ loc.toString(), ","
+ )
+ or
+ key = "OperandVirtualVariable" and
+ result = strictconcat(MemoryLocation loc |
+ loc = getOperandMemoryLocation(instruction.getAnOperand())
+ |
+ loc.getVirtualVariable().toString(), ","
+ )
+ or
+ exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset |
+ hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and
+ defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and
+ key = "DefinitionRank" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString() + "]" and
result = defRank.toString()
)
or
@@ -41,10 +59,11 @@ class PropertyProvider extends IRPropertyProvider {
result = useRank.toString()
)
or
- exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex |
- hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and
- defBlock.getInstruction(defIndex) = instruction and
- key = "DefinitionReachesUse[" + useLocation.toString() + "]" and
+ exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset |
+ hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and
+ defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and
+ key = "DefinitionReachesUse" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString()
+ + "]" and
result = strictconcat(IRBlock useBlock, int useRank, int useIndex |
exists(Instruction useInstruction |
hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and
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 1ca6e099795..a789edc7590 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,8 +1,5 @@
import SSAConstructionInternal
-private import cpp
-private import semmle.code.cpp.ir.implementation.Opcode
-private import semmle.code.cpp.ir.implementation.internal.OperandTag
-private import semmle.code.cpp.ir.internal.Overlap
+private import SSAConstructionImports
private import NewIR
private class OldBlock = Reachability::ReachableBlock;
@@ -18,7 +15,7 @@ private module Cached {
}
cached
- predicate functionHasIR(Function func) {
+ predicate functionHasIR(Language::Function func) {
exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func)
}
@@ -42,7 +39,7 @@ private module Cached {
not oldInstruction instanceof OldIR::PhiInstruction and
hasChiNode(_, oldInstruction)
} or
- Unreached(Function function) {
+ Unreached(Language::Function function) {
exists(OldInstruction oldInstruction |
function = oldInstruction.getEnclosingFunction() and
Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _)
@@ -50,12 +47,14 @@ private module Cached {
}
cached
- predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, Type type) {
+ 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.getType() = type
+ var.getLanguageType() = type
)
}
@@ -135,24 +134,12 @@ private module Cached {
}
cached
- Type getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
+ Language::LanguageType getInstructionOperandType(Instruction instr, TypedOperandTag tag) {
exists(OldInstruction oldInstruction, OldIR::TypedOperand oldOperand |
oldInstruction = getOldInstruction(instr) and
oldOperand = oldInstruction.getAnOperand() and
tag = oldOperand.getOperandTag() and
- result = oldOperand.getType()
- )
- }
-
- cached
- int getInstructionOperandSize(Instruction instr, SideEffectOperandTag tag) {
- exists(OldInstruction oldInstruction, OldIR::SideEffectOperand oldOperand |
- oldInstruction = getOldInstruction(instr) and
- oldOperand = oldInstruction.getAnOperand() and
- tag = oldOperand.getOperandTag() and
- // Only return a result for operands that need an explicit result size.
- oldOperand.getType() instanceof UnknownType and
- result = oldOperand.getSize()
+ result = oldOperand.getLanguageType()
)
}
@@ -196,20 +183,21 @@ private module Cached {
}
cached
- Expr getInstructionConvertedResultExpression(Instruction instruction) {
+ Language::Expr getInstructionConvertedResultExpression(Instruction instruction) {
result = getOldInstruction(instruction).getConvertedResultExpression()
}
cached
- Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
+ 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
* the new instructions generated from the successors of the old instruction
*/
+
cached
Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) {
if hasChiNode(_, getOldInstruction(instruction))
@@ -252,7 +240,7 @@ private module Cached {
}
cached
- Locatable getInstructionAST(Instruction instruction) {
+ Language::AST getInstructionAST(Instruction instruction) {
exists(OldInstruction oldInstruction |
instruction = WrappedInstruction(oldInstruction)
or
@@ -270,29 +258,25 @@ private module Cached {
}
cached
- predicate instructionHasType(Instruction instruction, Type type, boolean isGLValue) {
+ Language::LanguageType getInstructionResultType(Instruction instruction) {
exists(OldInstruction oldInstruction |
instruction = WrappedInstruction(oldInstruction) and
- type = oldInstruction.getResultType() and
- if oldInstruction.isGLValue() then isGLValue = true else isGLValue = false
+ result = oldInstruction.getResultLanguageType()
)
or
exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar |
instruction = Chi(oldInstruction) and
hasChiNode(vvar, oldInstruction) and
- type = vvar.getType() and
- isGLValue = false
+ result = vvar.getType()
)
or
exists(Alias::MemoryLocation location |
instruction = Phi(_, location) and
- type = location.getType() and
- isGLValue = false
+ result = location.getType()
)
or
instruction = Unreached(_) and
- type instanceof VoidType and
- isGLValue = false
+ result = Language::getVoidType()
}
cached
@@ -334,16 +318,21 @@ private module Cached {
IRVariable getInstructionVariable(Instruction instruction) {
result = getNewIRVariable(getOldInstruction(instruction)
.(OldIR::VariableInstruction)
- .getVariable())
+ .getIRVariable())
}
cached
- Field getInstructionField(Instruction instruction) {
+ Language::Field getInstructionField(Instruction instruction) {
result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField()
}
cached
- Function getInstructionFunction(Instruction instruction) {
+ int getInstructionIndex(Instruction instruction) {
+ result = getOldInstruction(instruction).(OldIR::IndexedInstruction).getIndex()
+ }
+
+ cached
+ Language::Function getInstructionFunction(Instruction instruction) {
result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol()
}
@@ -353,19 +342,14 @@ private module Cached {
}
cached
- StringLiteral getInstructionStringLiteral(Instruction instruction) {
- result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue()
- }
-
- cached
- BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
+ Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) {
result = getOldInstruction(instruction)
.(OldIR::BuiltInOperationInstruction)
.getBuiltInOperation()
}
cached
- Type getInstructionExceptionType(Instruction instruction) {
+ Language::LanguageType getInstructionExceptionType(Instruction instruction) {
result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType()
}
@@ -375,14 +359,9 @@ private module Cached {
}
cached
- int getInstructionResultSize(Instruction instruction) {
- // Only return a result for instructions that needed an explicit result size.
- instruction.getResultType() instanceof UnknownType and
- result = getOldInstruction(instruction).getResultSize()
- }
-
- cached
- predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) {
+ predicate getInstructionInheritance(
+ Instruction instruction, Language::Class baseClass, Language::Class derivedClass
+ ) {
exists(OldIR::InheritanceConversionInstruction oldInstr |
oldInstr = getOldInstruction(instruction) and
baseClass = oldInstr.getBaseClass() and
@@ -417,7 +396,11 @@ private predicate hasChiNode(Alias::VirtualVariable vvar, OldInstruction def) {
defLocation.getVirtualVariable() = vvar and
// If the definition totally (or exactly) overlaps the virtual variable, then there's no need for a `Chi`
// instruction.
- Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap
+ (
+ Alias::getOverlap(defLocation, vvar) instanceof MayPartiallyOverlap or
+ def.getResultMemoryAccess() instanceof IndirectMayMemoryAccess or
+ def.getResultMemoryAccess() instanceof BufferMayMemoryAccess
+ )
)
}
@@ -730,7 +713,10 @@ module DefUse {
defLocation = Alias::getResultMemoryLocation(def) and
block.getInstruction(index) = def and
overlap = Alias::getOverlap(defLocation, useLocation) and
- if overlap instanceof MayPartiallyOverlap
+ if
+ overlap instanceof MayPartiallyOverlap or
+ def.getResultMemoryAccess() instanceof IndirectMayMemoryAccess or
+ def.getResultMemoryAccess() instanceof BufferMayMemoryAccess
then offset = (index * 2) + 1 // The use will be connected to the definition on the `Chi` instruction.
else offset = index * 2 // The use will be connected to the definition on the original instruction.
)
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
new file mode 100644
index 00000000000..00f12020a29
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll
@@ -0,0 +1,3 @@
+import semmle.code.cpp.ir.implementation.Opcode
+import semmle.code.cpp.ir.implementation.internal.OperandTag
+import semmle.code.cpp.ir.internal.Overlap
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 dc19a8130d9..4cfbdfe831e 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,4 +2,5 @@ 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.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 7cca5855a20..49bb908265c 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
@@ -1,24 +1,20 @@
import AliasAnalysis
-private import cpp
-private import semmle.code.cpp.ir.implementation.raw.IR
-private import semmle.code.cpp.ir.internal.IntegerConstant as Ints
-private import semmle.code.cpp.ir.implementation.internal.OperandTag
-private import semmle.code.cpp.ir.internal.Overlap
+private import SimpleSSAImports
private class IntValue = Ints::IntValue;
private predicate hasResultMemoryAccess(
- Instruction instr, IRVariable var, Type type, IntValue bitOffset
+ Instruction instr, IRVariable var, Language::LanguageType type, IntValue bitOffset
) {
resultPointsTo(instr.getResultAddressOperand().getAnyDef(), var, bitOffset) and
- type = instr.getResultType()
+ type = instr.getResultLanguageType()
}
private predicate hasOperandMemoryAccess(
- MemoryOperand operand, IRVariable var, Type type, IntValue bitOffset
+ MemoryOperand operand, IRVariable var, Language::LanguageType type, IntValue bitOffset
) {
resultPointsTo(operand.getAddressOperand().getAnyDef(), var, bitOffset) and
- type = operand.getType()
+ type = operand.getLanguageType()
}
/**
@@ -30,17 +26,21 @@ private predicate isVariableModeled(IRVariable var) {
not variableAddressEscapes(var) and
// There's no need to check for the right size. An `IRVariable` never has an `UnknownType`, so the test for
// `type = var.getType()` is sufficient.
- forall(Instruction instr, Type type, IntValue bitOffset |
+ forall(Instruction instr, Language::LanguageType type, IntValue bitOffset |
hasResultMemoryAccess(instr, var, type, bitOffset)
|
bitOffset = 0 and
- type = var.getType()
+ type.getIRType() = var.getIRType() and
+ not (
+ instr.getResultMemoryAccess() instanceof IndirectMayMemoryAccess or
+ instr.getResultMemoryAccess() instanceof BufferMayMemoryAccess
+ )
) and
- forall(MemoryOperand operand, Type type, IntValue bitOffset |
+ forall(MemoryOperand operand, Language::LanguageType type, IntValue bitOffset |
hasOperandMemoryAccess(operand, var, type, bitOffset)
|
bitOffset = 0 and
- type = var.getType()
+ type.getIRType() = var.getIRType()
)
}
@@ -59,7 +59,7 @@ class MemoryLocation extends TMemoryLocation {
final VirtualVariable getVirtualVariable() { result = this }
- final Type getType() { result = var.getType() }
+ final Language::LanguageType getType() { result = var.getLanguageType() }
final string getUniqueId() { result = var.getUniqueId() }
}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll
new file mode 100644
index 00000000000..8f160f5a456
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll
@@ -0,0 +1,5 @@
+import semmle.code.cpp.ir.implementation.raw.IR
+import semmle.code.cpp.ir.internal.IntegerConstant as Ints
+import semmle.code.cpp.ir.implementation.internal.OperandTag
+import semmle.code.cpp.ir.internal.IRCppLanguage as Language
+import semmle.code.cpp.ir.internal.Overlap
diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
new file mode 100644
index 00000000000..e967dc9920c
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll
@@ -0,0 +1,509 @@
+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 int getPointerSize() { result = max(any(NullPointerType t).getSize()) }
+
+/**
+ * Works around an extractor bug where a function reference gets a size of one byte.
+ */
+private int getTypeSizeWorkaround(Type type) {
+ exists(Type unspecifiedType |
+ unspecifiedType = type.getUnspecifiedType() and
+ (
+ unspecifiedType instanceof FunctionReferenceType and
+ result = getPointerSize()
+ or
+ exists(PointerToMemberType ptmType |
+ ptmType = unspecifiedType and
+ (
+ if ptmType.getBaseType().getUnspecifiedType() instanceof RoutineType
+ then result = getPointerSize() * 2
+ else result = getPointerSize()
+ )
+ )
+ or
+ exists(ArrayType arrayType |
+ // Treat `T[]` as `T*`.
+ arrayType = unspecifiedType and
+ not arrayType.hasArraySize() and
+ result = getPointerSize()
+ )
+ )
+ )
+}
+
+private int getTypeSize(Type type) {
+ if exists(getTypeSizeWorkaround(type))
+ then result = getTypeSizeWorkaround(type)
+ else result = type.getSize()
+}
+
+/**
+ * Holds if an `IRErrorType` should exist.
+ */
+predicate hasErrorType() { exists(ErroneousType t) }
+
+/**
+ * Holds if an `IRBooleanType` with the specified `byteSize` should exist.
+ */
+predicate hasBooleanType(int byteSize) { byteSize = getTypeSize(any(BoolType type)) }
+
+private predicate isSigned(IntegralOrEnumType type) {
+ type.(IntegralType).isSigned()
+ or
+ exists(Enum enumType |
+ // If the enum has an explicit underlying type, we'll determine signedness from that. If not,
+ // we'll assume unsigned. The actual rules for the implicit underlying type of an enum vary
+ // between compilers, so we'll need an extractor change to get this 100% right. Until then,
+ // unsigned is a reasonable default.
+ enumType = type.getUnspecifiedType() and
+ enumType.getExplicitUnderlyingType().getUnspecifiedType().(IntegralType).isSigned()
+ )
+}
+
+private predicate isSignedIntegerType(IntegralOrEnumType type) {
+ isSigned(type) and not type instanceof BoolType
+}
+
+private predicate isUnsignedIntegerType(IntegralOrEnumType type) {
+ not isSigned(type) and not type instanceof BoolType
+}
+
+/**
+ * Holds if an `IRSignedIntegerType` with the specified `byteSize` should exist.
+ */
+predicate hasSignedIntegerType(int byteSize) {
+ byteSize = any(IntegralOrEnumType type | isSignedIntegerType(type)).getSize()
+}
+
+/**
+ * Holds if an `IRUnsignedIntegerType` with the specified `byteSize` should exist.
+ */
+predicate hasUnsignedIntegerType(int byteSize) {
+ byteSize = any(IntegralOrEnumType type | isUnsignedIntegerType(type)).getSize()
+}
+
+/**
+ * Holds if an `IRFloatingPointType` with the specified `byteSize` should exist.
+ */
+predicate hasFloatingPointType(int byteSize) { byteSize = any(FloatingPointType type).getSize() }
+
+private predicate isPointerIshType(Type type) {
+ type instanceof PointerType
+ or
+ type instanceof ReferenceType
+ or
+ type instanceof NullPointerType
+ or
+ // Treat `T[]` as a pointer. The only place we should see these is as the type of a parameter. If
+ // the corresponding decayed `T*` type is available, we'll use that, but if it's not available,
+ // we're stuck with `T[]`. Just treat it as a pointer.
+ type instanceof ArrayType and not exists(type.getSize())
+}
+
+/**
+ * Holds if an `IRAddressType` with the specified `byteSize` should exist.
+ */
+predicate hasAddressType(int byteSize) {
+ // This covers all pointers, all references, and because it also looks at `NullPointerType`, it
+ // should always return a result that makes sense for arbitrary glvalues as well.
+ byteSize = any(Type type | isPointerIshType(type)).getSize()
+}
+
+/**
+ * Holds if an `IRFunctionAddressType` with the specified `byteSize` should exist.
+ */
+predicate hasFunctionAddressType(int byteSize) {
+ byteSize = getPointerSize() or // Covers function lvalues
+ byteSize = getTypeSize(any(FunctionPointerIshType type))
+}
+
+private predicate isOpaqueType(Type type) {
+ exists(type.getSize()) and // Only include complete types
+ (
+ type instanceof ArrayType or
+ type instanceof Class or
+ type instanceof GNUVectorType
+ )
+ or
+ type instanceof PointerToMemberType // PTMs are missing size info
+}
+
+/**
+ * Holds if an `IROpaqueType` with the specified `tag` and `byteSize` should exist.
+ */
+predicate hasOpaqueType(Type tag, int byteSize) {
+ isOpaqueType(tag) and byteSize = getTypeSize(tag)
+ or
+ tag instanceof UnknownType and IRConstruction::needsUnknownOpaqueType(byteSize)
+}
+
+/**
+ * Gets the `IRType` that represents a prvalue of the specified `Type`.
+ */
+private IRType getIRTypeForPRValue(Type type) {
+ exists(Type unspecifiedType | unspecifiedType = type.getUnspecifiedType() |
+ isOpaqueType(unspecifiedType) and
+ exists(IROpaqueType opaqueType | opaqueType = result |
+ opaqueType.getByteSize() = getTypeSize(type) and
+ opaqueType.getTag() = unspecifiedType
+ )
+ or
+ unspecifiedType instanceof BoolType and result.(IRBooleanType).getByteSize() = type.getSize()
+ or
+ isSignedIntegerType(unspecifiedType) and
+ result.(IRSignedIntegerType).getByteSize() = type.getSize()
+ or
+ isUnsignedIntegerType(unspecifiedType) and
+ result.(IRUnsignedIntegerType).getByteSize() = type.getSize()
+ or
+ unspecifiedType instanceof FloatingPointType and
+ result.(IRFloatingPointType).getByteSize() = type.getSize()
+ or
+ isPointerIshType(unspecifiedType) and result.(IRAddressType).getByteSize() = getTypeSize(type)
+ or
+ unspecifiedType instanceof FunctionPointerIshType and
+ result.(IRFunctionAddressType).getByteSize() = getTypeSize(type)
+ or
+ unspecifiedType instanceof VoidType and result instanceof IRVoidType
+ or
+ unspecifiedType instanceof ErroneousType and result instanceof IRErrorType
+ or
+ unspecifiedType instanceof UnknownType and result instanceof IRUnknownType
+ )
+}
+
+cached
+private newtype TCppType =
+ TPRValueType(Type type) { exists(getIRTypeForPRValue(type)) } or
+ TFunctionGLValueType() or
+ TGLValueAddressType(Type type) or
+ TUnknownOpaqueType(int byteSize) { IRConstruction::needsUnknownOpaqueType(byteSize) } or
+ TUnknownType()
+
+/**
+ * The C++ type of an IR entity.
+ * This cannot just be `Type` for a couple reasons:
+ * - Some types needed by the IR might not exist in the database (e.g. `RoutineType`s for functions
+ * that are always called directly)
+ * - Some types needed by the IR are not representable in the C++ type system (e.g. the result type
+ * of a `VariableAddress` where the variable is of reference type)
+ */
+class CppType extends TCppType {
+ string toString() { none() }
+
+ /** Gets a string used in IR dumps */
+ string getDumpString() { result = toString() }
+
+ /** Gets the size of the type in bytes, if known. */
+ final int getByteSize() { result = getIRType().getByteSize() }
+
+ /**
+ * Gets the `IRType` that represents this `CppType`. Many different `CppType`s can map to a single
+ * `IRType`.
+ */
+ cached
+ IRType getIRType() { none() }
+
+ /**
+ * Holds if the `CppType` represents a prvalue of type `Type` (if `isGLValue` is `false`), or if
+ * it represents a glvalue of type `Type` (if `isGLValue` is `true`).
+ */
+ predicate hasType(Type type, boolean isGLValue) { none() }
+
+ final predicate hasUnspecifiedType(Type type, boolean isGLValue) {
+ exists(Type specifiedType |
+ hasType(specifiedType, isGLValue) and
+ type = specifiedType.getUnspecifiedType()
+ )
+ }
+}
+
+/**
+ * A `CppType` that wraps an existing `Type` (either as a prvalue or a glvalue).
+ */
+private class CppWrappedType extends CppType {
+ Type ctype;
+
+ CppWrappedType() {
+ this = TPRValueType(ctype) or
+ this = TGLValueAddressType(ctype)
+ }
+}
+
+/**
+ * A `CppType` that represents a prvalue of an existing `Type`.
+ */
+private class CppPRValueType extends CppWrappedType, TPRValueType {
+ final override string toString() { result = ctype.toString() }
+
+ final override string getDumpString() { result = ctype.getUnspecifiedType().toString() }
+
+ final override IRType getIRType() { result = getIRTypeForPRValue(ctype) }
+
+ final override predicate hasType(Type type, boolean isGLValue) {
+ type = ctype and
+ isGLValue = false
+ }
+}
+
+/**
+ * A `CppType` that has unknown type but a known size. Generally to represent synthesized types that
+ * occur in certain cases during IR construction, such as the type of a zero-initialized segment of
+ * a partially-initialized array.
+ */
+private class CppUnknownOpaqueType extends CppType, TUnknownOpaqueType {
+ int byteSize;
+
+ CppUnknownOpaqueType() { this = TUnknownOpaqueType(byteSize) }
+
+ final override string toString() { result = "unknown[" + byteSize.toString() + "]" }
+
+ final override IROpaqueType getIRType() {
+ result.getByteSize() = byteSize and result.getTag() instanceof UnknownType
+ }
+
+ override predicate hasType(Type type, boolean isGLValue) {
+ type instanceof UnknownType and isGLValue = false
+ }
+}
+
+/**
+ * A `CppType` that represents a glvalue of an existing `Type`.
+ */
+private class CppGLValueAddressType extends CppWrappedType, TGLValueAddressType {
+ final override string toString() { result = "glval<" + ctype.toString() + ">" }
+
+ final override string getDumpString() {
+ result = "glval<" + ctype.getUnspecifiedType().toString() + ">"
+ }
+
+ final override IRAddressType getIRType() { result.getByteSize() = getPointerSize() }
+
+ final override predicate hasType(Type type, boolean isGLValue) {
+ type = ctype and
+ isGLValue = true
+ }
+}
+
+/**
+ * A `CppType` that represents a function lvalue.
+ */
+private class CppFunctionGLValueType extends CppType, TFunctionGLValueType {
+ final override string toString() { result = "glval" }
+
+ final override IRFunctionAddressType getIRType() { result.getByteSize() = getPointerSize() }
+
+ final override predicate hasType(Type type, boolean isGLValue) {
+ type instanceof UnknownType and isGLValue = true
+ }
+}
+
+/**
+ * A `CppType` that represents an unknown type.
+ */
+private class CppUnknownType extends CppType, TUnknownType {
+ final override string toString() { result = any(UnknownType type).toString() }
+
+ final override IRUnknownType getIRType() { any() }
+
+ final override predicate hasType(Type type, boolean isGLValue) {
+ type instanceof UnknownType and isGLValue = false
+ }
+}
+
+/**
+ * Gets the single instance of `CppUnknownType`.
+ */
+CppUnknownType getUnknownType() { any() }
+
+/**
+ * Gets the `CppType` that represents a prvalue of type `void`.
+ */
+CppPRValueType getVoidType() { exists(VoidType voidType | result.hasType(voidType, false)) }
+
+/**
+ * Gets the `CppType` that represents a prvalue of type `type`.
+ */
+CppType getTypeForPRValue(Type type) {
+ if type instanceof UnknownType
+ then result instanceof CppUnknownType
+ else result.hasType(type, false)
+}
+
+/**
+ * Gets the `CppType` that represents a prvalue of type `type`, if such a `CppType` exists.
+ * Otherwise, gets `CppUnknownType`.
+ */
+CppType getTypeForPRValueOrUnknown(Type type) {
+ result = getTypeForPRValue(type)
+ or
+ not exists(getTypeForPRValue(type)) and result = getUnknownType()
+}
+
+/**
+ * Gets the `CppType` that represents a glvalue of type `type`.
+ */
+CppType getTypeForGLValue(Type type) { result.hasType(type, true) }
+
+/**
+ * Gets the `CppType` that represents a prvalue of type `int`.
+ */
+CppPRValueType getIntType() {
+ exists(IntType type |
+ type.isImplicitlySigned() and
+ result.hasType(type, false)
+ )
+}
+
+/**
+ * Gets the `CppType` that represents a prvalue of type `bool`.
+ */
+CppPRValueType getBoolType() { exists(BoolType type | result.hasType(type, false)) }
+
+/**
+ * Gets the `CppType` that represents a glvalue of function type.
+ */
+CppFunctionGLValueType getFunctionGLValueType() { any() }
+
+/**
+ * Gets the `CppType` that represents a opaque of unknown type with size `byteSize`.
+ */
+CppUnknownOpaqueType getUnknownOpaqueType(int byteSize) { result.getByteSize() = byteSize }
+
+/**
+ * Gets the `CppType` that is the canonical type for an `IRBooleanType` with the specified
+ * `byteSize`.
+ */
+CppWrappedType getCanonicalBooleanType(int byteSize) {
+ exists(BoolType type | result = TPRValueType(type) and byteSize = type.getSize())
+}
+
+/**
+ * Compute the sorting priority of an `IntegralType` based on its signedness.
+ */
+private int getSignPriority(IntegralType type) {
+ // Explicitly unsigned types sort first. Explicitly signed types sort last. Types with no explicit
+ // signedness sort in between. This lets us always choose `int` over `signed int`, while also
+ // choosing `unsigned char`+`char` when `char` is signed, and `unsigned char`+`signed char` when
+ // `char` is unsigned.
+ if type.isExplicitlyUnsigned()
+ then result = 2
+ else
+ if type.isExplicitlySigned()
+ then result = 0
+ else result = 1
+}
+
+/**
+ * Gets the sort priority of an `IntegralType` based on its kind.
+ */
+private int getKindPriority(IntegralType type) {
+ // `CharType` sorts lower so that we prefer the plain integer types when they have the same size
+ // as a `CharType`.
+ if type instanceof CharType then result = 0 else result = 1
+}
+
+/**
+ * Gets the `CppType` that is the canonical type for an `IRSignedIntegerType` with the specified
+ * `byteSize`.
+ */
+CppPRValueType getCanonicalSignedIntegerType(int byteSize) {
+ result = TPRValueType(max(IntegralType type |
+ type.isSigned() and type.getSize() = byteSize
+ |
+ type order by getKindPriority(type), getSignPriority(type), type.toString() desc
+ ))
+}
+
+/**
+ * Gets the `CppType` that is the canonical type for an `IRUnsignedIntegerType` with the specified
+ * `byteSize`.
+ */
+CppPRValueType getCanonicalUnsignedIntegerType(int byteSize) {
+ result = TPRValueType(max(IntegralType type |
+ type.isUnsigned() and type.getSize() = byteSize
+ |
+ type order by getKindPriority(type), getSignPriority(type), type.toString() desc
+ ))
+}
+
+/**
+ * Gets the `CppType` that is the canonical type for an `IRFloatingPointType` with the specified
+ * `byteSize`.
+ */
+CppPRValueType getCanonicalFloatingPointType(int byteSize) {
+ result = TPRValueType(max(FloatingPointType type |
+ type.getSize() = byteSize
+ |
+ type order by type.toString() desc
+ ))
+}
+
+/**
+ * Gets the `CppType` that is the canonical type for an `IRAddressType` with the specified
+ * `byteSize`.
+ */
+CppPRValueType getCanonicalAddressType(int byteSize) {
+ // We just use `NullPointerType`, since it should be unique.
+ exists(NullPointerType type |
+ type.getSize() = byteSize and
+ result = TPRValueType(type)
+ )
+}
+
+/**
+ * Gets the `CppType` that is the canonical type for an `IRFunctionAddressType` with the specified
+ * `byteSize`.
+ */
+CppFunctionGLValueType getCanonicalFunctionAddressType(int byteSize) {
+ result.getByteSize() = byteSize
+}
+
+/**
+ * Gets the `CppType` that is the canonical type for `IRErrorType`.
+ */
+CppPRValueType getCanonicalErrorType() { result = TPRValueType(any(ErroneousType type)) }
+
+/**
+ * Gets the `CppType` that is the canonical type for `IRUnknownType`.
+ */
+CppUnknownType getCanonicalUnknownType() { any() }
+
+/**
+ * Gets the `CppType` that is the canonical type for `IRVoidType`.
+ */
+CppPRValueType getCanonicalVoidType() { result = TPRValueType(any(VoidType type)) }
+
+/**
+ * Gets the `CppType` that is the canonical type for an `IROpaqueType` with the specified `tag` and
+ * `byteSize`.
+ */
+CppType getCanonicalOpaqueType(Type tag, int byteSize) {
+ isOpaqueType(tag) and
+ result = TPRValueType(tag.getUnspecifiedType()) and
+ getTypeSize(tag) = byteSize
+ or
+ tag instanceof UnknownType and result = getUnknownOpaqueType(byteSize)
+}
+
+/**
+ * Gets a string that uniquely identifies an `IROpaqueType` tag. This may be different from the usual
+ * `toString()` of the tag in order to ensure uniqueness.
+ */
+string getOpaqueTagIdentityString(Type tag) {
+ hasOpaqueType(tag, _) and
+ result = getTypeIdentityString(tag)
+}
+
+module LanguageTypeSanity {
+ query predicate missingCppType(Type type, string message) {
+ not exists(getTypeForPRValue(type)) and
+ exists(type.getSize()) and
+ // `ProxyClass`es have a size, but only appear in uninstantiated templates
+ not type instanceof ProxyClass and
+ message = "Type does not have an associated `CppType`."
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll
index cda661bd216..613958a0d4c 100644
--- a/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll
+++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll
@@ -1,6 +1,13 @@
private import cpp as Cpp
private import semmle.code.cpp.Print as Print
private import IRUtilities
+private import semmle.code.cpp.ir.implementation.IRType
+private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
+import CppType
+
+class LanguageType = CppType;
+
+class OpaqueTypeTag = Cpp::Type;
class Function = Cpp::Function;
diff --git a/cpp/ql/src/semmle/code/cpp/models/Models.qll b/cpp/ql/src/semmle/code/cpp/models/Models.qll
index 0747b00c48d..c152a473259 100644
--- a/cpp/ql/src/semmle/code/cpp/models/Models.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/Models.qll
@@ -1,6 +1,7 @@
private import implementations.IdentityFunction
private import implementations.Inet
private import implementations.Memcpy
+private import implementations.Memset
private import implementations.Printf
private import implementations.Pure
private import implementations.Strcat
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 8e36b5c7210..865a74b5571 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/IdentityFunction.qll
@@ -16,9 +16,9 @@ class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFuncti
)
}
- override predicate neverReadsMemory() { any() }
+ override predicate hasOnlySpecificReadSideEffects() { any() }
- override predicate neverWritesMemory() { any() }
+ override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate parameterNeverEscapes(int index) { none() }
@@ -34,6 +34,6 @@ class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFuncti
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// These functions simply return the argument value.
- input.isInParameter(0) and output.isOutReturnValue()
+ input.isParameter(0) and output.isReturnValue()
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Inet.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Inet.qll
index a62ed77ebf2..f9b35b8907b 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Inet.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Inet.qll
@@ -5,8 +5,8 @@ class InetNtoa extends TaintFunction {
InetNtoa() { hasGlobalName("inet_ntoa") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- input.isInParameter(0) and
- output.isOutReturnPointer()
+ input.isParameter(0) and
+ output.isReturnValueDeref()
}
}
@@ -14,8 +14,8 @@ class InetAton extends TaintFunction, ArrayFunction {
InetAton() { hasGlobalName("inet_aton") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- input.isInParameterPointer(0) and
- output.isOutParameterPointer(1)
+ input.isParameterDeref(0) and
+ output.isParameterDeref(1)
}
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
@@ -34,8 +34,8 @@ class InetAddr extends TaintFunction, ArrayFunction {
InetAddr() { hasGlobalName("inet_addr") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- input.isInParameterPointer(0) and
- output.isOutReturnValue()
+ input.isParameterDeref(0) and
+ output.isReturnValue()
}
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
@@ -47,8 +47,8 @@ class InetNetwork extends TaintFunction, ArrayFunction {
InetNetwork() { hasGlobalName("inet_network") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- input.isInParameterPointer(1) and
- output.isOutReturnValue()
+ input.isParameterDeref(1) and
+ output.isReturnValue()
}
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
@@ -61,10 +61,10 @@ class InetMakeaddr extends TaintFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
(
- input.isInParameter(0) or
- input.isInParameter(1)
+ input.isParameter(0) or
+ input.isParameter(1)
) and
- output.isOutReturnValue()
+ output.isReturnValue()
}
}
@@ -72,8 +72,8 @@ class InetLnaof extends TaintFunction {
InetLnaof() { hasGlobalName("inet_lnaof") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- input.isInParameter(0) and
- output.isOutReturnValue()
+ input.isParameter(0) and
+ output.isReturnValue()
}
}
@@ -81,8 +81,8 @@ class InetNetof extends TaintFunction {
InetNetof() { hasGlobalName("inet_netof") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- input.isInParameter(0) and
- output.isOutReturnValue()
+ input.isParameter(0) and
+ output.isReturnValue()
}
}
@@ -91,10 +91,10 @@ class InetPton extends TaintFunction, ArrayFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
(
- input.isInParameter(0) or
- input.isInParameterPointer(1)
+ input.isParameter(0) or
+ input.isParameterDeref(1)
) and
- output.isOutParameterPointer(2)
+ output.isParameterDeref(2)
}
override predicate hasArrayInput(int bufParam) { bufParam = 1 }
@@ -110,8 +110,8 @@ class Gethostbyname extends TaintFunction, ArrayFunction {
Gethostbyname() { hasGlobalName("gethostbyname") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- input.isInParameterPointer(0) and
- output.isOutReturnPointer()
+ input.isParameterDeref(0) and
+ output.isReturnValueDeref()
}
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
@@ -124,11 +124,11 @@ class Gethostbyaddr extends TaintFunction, ArrayFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
(
- input.isInParameterPointer(0) or
- input.isInParameter(1) or
- input.isInParameter(2)
+ input.isParameterDeref(0) or
+ input.isParameter(1) or
+ input.isParameter(2)
) and
- output.isOutReturnPointer()
+ output.isReturnValueDeref()
}
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
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 19fe6225cda..0951daea11b 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Memcpy.qll
@@ -1,13 +1,14 @@
import semmle.code.cpp.Function
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
+import semmle.code.cpp.models.interfaces.SideEffect
import semmle.code.cpp.models.interfaces.Taint
/**
* The standard functions `memcpy` and `memmove`, and the gcc variant
* `__builtin___memcpy_chk`
*/
-class MemcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
+class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction, TaintFunction {
MemcpyFunction() {
this.hasName("memcpy") or
this.hasName("memmove") or
@@ -19,22 +20,22 @@ class MemcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
- input.isInParameterPointer(1) and
- output.isOutParameterPointer(0)
+ input.isParameterDeref(1) and
+ output.isParameterDeref(0)
or
- input.isInParameterPointer(1) and
- output.isOutReturnPointer()
+ input.isParameterDeref(1) and
+ output.isReturnValueDeref()
or
- input.isInParameter(0) and
- output.isOutReturnValue()
+ input.isParameter(0) and
+ output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
- input.isInParameter(2) and
- output.isOutParameterPointer(0)
+ input.isParameter(2) and
+ output.isParameterDeref(0)
or
- input.isInParameter(2) and
- output.isOutReturnPointer()
+ input.isParameter(2) and
+ output.isReturnValueDeref()
}
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
@@ -44,4 +45,24 @@ class MemcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
) and
countParam = 2
}
+
+ override predicate hasOnlySpecificReadSideEffects() { any() }
+
+ override predicate hasOnlySpecificWriteSideEffects() { any() }
+
+ override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
+ i = 0 and buffer = true and mustWrite = true
+ }
+
+ override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
+ i = 1 and buffer = true
+ }
+
+ override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
+ result = 2 and
+ (
+ i = 0 or
+ i = 1
+ )
+ }
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll
new file mode 100644
index 00000000000..7fc7fbfc9e5
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Memset.qll
@@ -0,0 +1,55 @@
+import semmle.code.cpp.Function
+import semmle.code.cpp.models.interfaces.ArrayFunction
+import semmle.code.cpp.models.interfaces.DataFlow
+import semmle.code.cpp.models.interfaces.Alias
+import semmle.code.cpp.models.interfaces.SideEffect
+
+/**
+ * The standard function `memset` and its assorted variants
+ */
+class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction, SideEffectFunction {
+ MemsetFunction() {
+ hasGlobalName("memset") or
+ hasGlobalName("wmemset") or
+ hasGlobalName("bzero") or
+ hasGlobalName("__builtin_memset") or
+ hasGlobalName("__builtin_memset_chk") or
+ hasQualifiedName("std", "memset") or
+ hasQualifiedName("std", "wmemset")
+ }
+
+ override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
+
+ override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
+ input.isParameter(0) and
+ output.isReturnValue()
+ }
+
+ override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
+ bufParam = 0 and
+ (if hasGlobalName("bzero") then countParam = 1 else countParam = 2)
+ }
+
+ override predicate parameterNeverEscapes(int index) { hasGlobalName("bzero") and index = 0 }
+
+ override predicate parameterEscapesOnlyViaReturn(int index) {
+ not hasGlobalName("bzero") and index = 0
+ }
+
+ override predicate parameterIsAlwaysReturned(int index) {
+ not hasGlobalName("bzero") and index = 0
+ }
+
+ override predicate hasOnlySpecificReadSideEffects() { any() }
+
+ override predicate hasOnlySpecificWriteSideEffects() { any() }
+
+ override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
+ i = 0 and buffer = true and mustWrite = true
+ }
+
+ override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
+ i = 0 and
+ if hasGlobalName("bzero") then result = 1 else result = 2
+ }
+}
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 5654c1a9c99..58721897802 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Printf.qll
@@ -7,9 +7,9 @@ class Printf extends FormattingFunction {
Printf() {
this instanceof TopLevelFunction and
(
- hasGlobalName("printf") or
+ hasGlobalOrStdName("printf") or
hasGlobalName("printf_s") or
- hasGlobalName("wprintf") or
+ hasGlobalOrStdName("wprintf") or
hasGlobalName("wprintf_s") or
hasGlobalName("g_printf")
) and
@@ -19,7 +19,7 @@ class Printf extends FormattingFunction {
override int getFormatParameterIndex() { result = 0 }
override predicate isWideCharDefault() {
- hasGlobalName("wprintf") or
+ hasGlobalOrStdName("wprintf") or
hasGlobalName("wprintf_s")
}
}
@@ -31,8 +31,8 @@ class Fprintf extends FormattingFunction {
Fprintf() {
this instanceof TopLevelFunction and
(
- hasGlobalName("fprintf") or
- hasGlobalName("fwprintf") or
+ hasGlobalOrStdName("fprintf") or
+ hasGlobalOrStdName("fwprintf") or
hasGlobalName("g_fprintf")
) and
not exists(getDefinition().getFile().getRelativePath())
@@ -40,7 +40,7 @@ class Fprintf extends FormattingFunction {
override int getFormatParameterIndex() { result = 1 }
- override predicate isWideCharDefault() { hasGlobalName("fwprintf") }
+ override predicate isWideCharDefault() { hasGlobalOrStdName("fwprintf") }
override int getOutputParameterIndex() { result = 0 }
}
@@ -52,10 +52,10 @@ class Sprintf extends FormattingFunction {
Sprintf() {
this instanceof TopLevelFunction and
(
- hasGlobalName("sprintf") or
+ hasGlobalOrStdName("sprintf") or
hasGlobalName("_sprintf_l") or
hasGlobalName("__swprintf_l") or
- hasGlobalName("wsprintf") or
+ hasGlobalOrStdName("wsprintf") or
hasGlobalName("g_strdup_printf") or
hasGlobalName("g_sprintf") or
hasGlobalName("__builtin___sprintf_chk")
@@ -99,8 +99,8 @@ class Snprintf extends FormattingFunction {
Snprintf() {
this instanceof TopLevelFunction and
(
- hasGlobalName("snprintf") or // C99 defines snprintf
- hasGlobalName("swprintf") or // The s version of wide-char printf is also always the n version
+ hasGlobalOrStdName("snprintf") or // C99 defines snprintf
+ hasGlobalOrStdName("swprintf") or // The s version of wide-char printf is also always the n version
// Microsoft has _snprintf as well as several other variations
hasGlobalName("sprintf_s") or
hasGlobalName("snprintf_s") or
@@ -160,7 +160,7 @@ class Snprintf extends FormattingFunction {
*/
predicate returnsFullFormatLength() {
(
- hasGlobalName("snprintf") or
+ hasGlobalOrStdName("snprintf") or
hasGlobalName("g_snprintf") or
hasGlobalName("__builtin___snprintf_chk") or
hasGlobalName("snprintf_s")
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Pure.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Pure.qll
index c723e55718b..59b8c25700f 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Pure.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Pure.qll
@@ -6,7 +6,7 @@ import semmle.code.cpp.models.interfaces.SideEffect
class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideEffectFunction {
PureStrFunction() {
exists(string name |
- hasGlobalName(name) and
+ hasGlobalOrStdName(name) and
(
name = "atof" or
name = "atoi" or
@@ -41,17 +41,17 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
exists(ParameterIndex i |
- input.isInParameter(i) and
+ input.isParameter(i) and
exists(getParameter(i))
or
- input.isInParameterPointer(i) and
+ input.isParameterDeref(i) and
getParameter(i).getUnspecifiedType() instanceof PointerType
) and
(
- output.isOutReturnPointer() and
+ output.isReturnValueDeref() and
getUnspecifiedType() instanceof PointerType
or
- output.isOutReturnValue()
+ output.isReturnValue()
)
}
@@ -67,15 +67,15 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
override predicate parameterIsAlwaysReturned(int i) { none() }
- override predicate neverReadsMemory() { none() }
+ override predicate hasOnlySpecificReadSideEffects() { none() }
- override predicate neverWritesMemory() { any() }
+ override predicate hasOnlySpecificWriteSideEffects() { any() }
}
class PureFunction extends TaintFunction, SideEffectFunction {
PureFunction() {
exists(string name |
- hasGlobalName(name) and
+ hasGlobalOrStdName(name) and
(
name = "abs" or
name = "labs"
@@ -85,13 +85,13 @@ class PureFunction extends TaintFunction, SideEffectFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
exists(ParameterIndex i |
- input.isInParameter(i) and
+ input.isParameter(i) and
exists(getParameter(i))
) and
- output.isOutReturnValue()
+ output.isReturnValue()
}
- override predicate neverReadsMemory() { any() }
+ override predicate hasOnlySpecificReadSideEffects() { any() }
- override predicate neverWritesMemory() { any() }
+ override predicate hasOnlySpecificWriteSideEffects() { any() }
}
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 36d61086631..d56ebf10bbc 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll
@@ -19,8 +19,8 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
}
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
- input.isInParameter(0) and
- output.isOutReturnValue()
+ input.isParameter(0) and
+ output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -31,19 +31,19 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
name = "_mbsncat" or
name = "_mbsncat_l"
) and
- input.isInParameter(2) and
- output.isOutParameterPointer(0)
+ input.isParameter(2) and
+ output.isParameterDeref(0)
or
name = "_mbsncat_l" and
- input.isInParameter(3) and
- output.isOutParameterPointer(0)
+ input.isParameter(3) and
+ output.isParameterDeref(0)
)
or
- input.isInParameterPointer(0) and
- output.isOutParameterPointer(0)
+ input.isParameterDeref(0) and
+ output.isParameterDeref(0)
or
- input.isInParameter(1) and
- output.isOutParameterPointer(0)
+ input.isParameter(1) and
+ output.isParameterDeref(0)
}
override predicate hasArrayInput(int param) {
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 4ba9151e69b..d309d32df54 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll
@@ -55,15 +55,15 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
this.hasName("wcscpy")
) and
(
- input.isInParameterPointer(1) and
- output.isOutParameterPointer(0)
+ input.isParameterDeref(1) and
+ output.isParameterDeref(0)
or
- input.isInParameterPointer(1) and
- output.isOutReturnPointer()
+ input.isParameterDeref(1) and
+ output.isReturnValueDeref()
)
or
- input.isInParameter(0) and
- output.isOutReturnValue()
+ input.isParameter(0) and
+ output.isReturnValue()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -78,12 +78,12 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
this.hasName("_wcsncpy_l")
) and
(
- input.isInParameter(2) or
- input.isInParameterPointer(1)
+ input.isParameter(2) or
+ input.isParameterDeref(1)
) and
(
- output.isOutParameterPointer(0) or
- output.isOutReturnPointer()
+ output.isParameterDeref(0) or
+ output.isReturnValueDeref()
)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strftime.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strftime.qll
index 3987f8ac66d..b4c7f69bde4 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strftime.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strftime.qll
@@ -6,13 +6,13 @@ class Strftime extends TaintFunction, ArrayFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
(
- input.isInParameter(1) or
- input.isInParameterPointer(2) or
- input.isInParameterPointer(3)
+ input.isParameter(1) or
+ input.isParameterDeref(2) or
+ input.isParameterDeref(3)
) and
(
- output.isOutParameterPointer(0) or
- output.isOutReturnValue()
+ output.isParameterDeref(0) or
+ output.isReturnValue()
)
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Swap.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Swap.qll
index 1c1e54a6420..a7474501ad7 100644
--- a/cpp/ql/src/semmle/code/cpp/models/implementations/Swap.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Swap.qll
@@ -8,10 +8,10 @@ class Swap extends DataFlowFunction {
Swap() { this.hasQualifiedName("std", "swap") }
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
- input.isInParameterPointer(0) and
- output.isOutParameterPointer(1)
+ input.isParameterDeref(0) and
+ output.isParameterDeref(1)
or
- input.isInParameterPointer(1) and
- output.isOutParameterPointer(0)
+ input.isParameterDeref(1) and
+ output.isParameterDeref(0)
}
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/ArrayFunction.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/ArrayFunction.qll
index 59b96d7eddf..cb4c531ebec 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/ArrayFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/ArrayFunction.qll
@@ -1,11 +1,11 @@
/**
* Provides an abstract class for accurate modeling of input and output buffers
* in library functions when source code is not available. To use this QL
- * library, create a QL class extending `BufferFunction` with a characteristic
+ * library, create a QL class extending `ArrayFunction` with a characteristic
* predicate that selects the function or set of functions you are trying to
- * model. Within that class, override the predicates provided by `BufferFunction`
+ * model. Within that class, override the predicates provided by `ArrayFunction`
* to match the flow within that function. Finally, add a private import
- * statement to `CustomModels.qll`
+ * statement to `Models.qll`
*/
import semmle.code.cpp.Function
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 56cce6d01b0..130c067f70c 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FormattingFunction.qll
@@ -115,7 +115,19 @@ abstract class FormattingFunction extends Function {
* Gets the position of the first format argument, corresponding with
* the first format specifier in the format string.
*/
- int getFirstFormatArgumentIndex() { result = getNumberOfParameters() }
+ int getFirstFormatArgumentIndex() {
+ result = getNumberOfParameters() and
+ // the formatting function either has a definition in the snapshot, or all
+ // `DeclarationEntry`s agree on the number of parameters (otherwise we don't
+ // really know the correct number)
+ (
+ hasDefinition()
+ or
+ forall(FunctionDeclarationEntry fde | fde = getADeclarationEntry() |
+ result = fde.getNumberOfParameters()
+ )
+ )
+ }
/**
* Gets the position of the buffer size argument, if any.
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 924051a6045..d0fbb50ebfa 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll
@@ -6,26 +6,106 @@
import semmle.code.cpp.Parameter
-/**
- * An `int` that is a parameter index for some function. This is needed for binding in certain cases.
- */
-class ParameterIndex extends int {
- ParameterIndex() { exists(Parameter p | this = p.getIndex()) }
-}
-
-newtype TFunctionInput =
+private newtype TFunctionInput =
TInParameter(ParameterIndex i) or
- TInParameterPointer(ParameterIndex i) or
- TInQualifier()
+ TInParameterDeref(ParameterIndex i) or
+ TInQualifierObject() or
+ TInQualifierAddress()
+/**
+ * An input to a function. This can be:
+ * - The value of one of the function's parameters
+ * - The value pointed to by one of function's pointer or reference parameters
+ * - The value of the function's `this` pointer
+ * - The value pointed to by the function's `this` pointer
+ */
class FunctionInput extends TFunctionInput {
abstract string toString();
- predicate isInParameter(ParameterIndex index) { none() }
+ /**
+ * Holds if this is the input value of the parameter with index `index`.
+ *
+ * Example:
+ * ```
+ * void func(int n, char* p, float& r);
+ * ```
+ * - `isParameter(0)` holds for the `FunctionInput` that represents the value of `n` (with type
+ * `int`) on entry to the function.
+ * - `isParameter(1)` holds for the `FunctionInput` that represents the value of `p` (with type
+ * `char*`) on entry to the function.
+ * - `isParameter(2)` holds for the `FunctionInput` that represents the "value" of the reference
+ * `r` (with type `float&`) on entry to the function, _not_ the value of the referred-to
+ * `float`.
+ */
+ predicate isParameter(ParameterIndex index) { none() }
- predicate isInParameterPointer(ParameterIndex index) { none() }
+ /**
+ * Holds if this is the input value of the parameter with index `index`.
+ * DEPRECATED: Use `isParameter(index)` instead.
+ */
+ deprecated final predicate isInParameter(ParameterIndex index) { isParameter(index) }
- predicate isInQualifier() { none() }
+ /**
+ * Holds if this is 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, where the parameter has index
+ * `index`.
+ *
+ * Example:
+ * ```
+ * void func(int n, char* p, float& r);
+ * ```
+ * - `isParameterDeref(1)` holds for the `FunctionInput` that represents the value of `*p` (with
+ * type `char`) on entry to the function.
+ * - `isParameterDeref(2)` holds for the `FunctionInput` that represents the value of `r` (with type
+ * `float`) on entry to the function.
+ * - There is no `FunctionInput` for which `isParameterDeref(0)` holds, because `n` is neither a
+ * pointer nor a reference.
+ */
+ predicate isParameterDeref(ParameterIndex index) { none() }
+
+ /**
+ * Holds if this is 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, where the parameter has index
+ * `index`.
+ * DEPRECATED: Use `isParameterDeref(index)` instead.
+ */
+ deprecated final predicate isInParameterPointer(ParameterIndex index) { isParameterDeref(index) }
+
+ /**
+ * Holds if this is 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;
+ * };
+ * ```
+ * - `isQualifierObject()` holds for the `FunctionInput` that represents the value of `*this`
+ * (with type `C const`) on entry to the function.
+ */
+ predicate isQualifierObject() { none() }
+
+ /**
+ * Holds if this is the input value pointed to by the `this` pointer of an instance member
+ * function.
+ * DEPRECATED: Use `isQualifierObject()` instead.
+ */
+ deprecated final predicate isInQualifier() { isQualifierObject() }
+
+ /**
+ * Holds if this is the input value of the `this` pointer of an instance member function.
+ *
+ * Example:
+ * ```
+ * struct C {
+ * void mfunc(int n, char* p, float& r) const;
+ * };
+ * ```
+ * - `isQualifierAddress()` holds for the `FunctionInput` that represents the value of `this`
+ * (with type `C const *`) on entry to the function.
+ */
+ predicate isQualifierAddress() { none() }
}
class InParameter extends FunctionInput, TInParameter {
@@ -35,73 +115,182 @@ class InParameter extends FunctionInput, TInParameter {
override string toString() { result = "InParameter " + index.toString() }
+ /** Gets the zero-based index of the parameter. */
ParameterIndex getIndex() { result = index }
- override predicate isInParameter(ParameterIndex i) { i = index }
+ override predicate isParameter(ParameterIndex i) { i = index }
}
-class InParameterPointer extends FunctionInput, TInParameterPointer {
+class InParameterDeref extends FunctionInput, TInParameterDeref {
ParameterIndex index;
- InParameterPointer() { this = TInParameterPointer(index) }
+ InParameterDeref() { this = TInParameterDeref(index) }
- override string toString() { result = "InParameterPointer " + index.toString() }
+ override string toString() { result = "InParameterDeref " + index.toString() }
+ /** Gets the zero-based index of the parameter. */
ParameterIndex getIndex() { result = index }
- override predicate isInParameterPointer(ParameterIndex i) { i = index }
+ override predicate isParameterDeref(ParameterIndex i) { i = index }
}
-class InQualifier extends FunctionInput, TInQualifier {
- override string toString() { result = "InQualifier" }
+class InQualifierObject extends FunctionInput, TInQualifierObject {
+ override string toString() { result = "InQualifierObject" }
- override predicate isInQualifier() { any() }
+ override predicate isQualifierObject() { any() }
}
-newtype TFunctionOutput =
- TOutParameterPointer(ParameterIndex i) or
- TOutQualifier() or
+class InQualifierAddress extends FunctionInput, TInQualifierAddress {
+ override string toString() { result = "InQualifierAddress" }
+
+ override predicate isQualifierAddress() { any() }
+}
+
+private newtype TFunctionOutput =
+ TOutParameterDeref(ParameterIndex i) or
+ TOutQualifierObject() or
TOutReturnValue() or
- TOutReturnPointer()
+ TOutReturnValueDeref()
+/**
+ * An output from a function. This can be:
+ * - The value pointed to by one of function's pointer or reference parameters
+ * - The value pointed to by the function's `this` pointer
+ * - The function's return value
+ * - The value pointed to by the function's return value, if the return value is a pointer or
+ * reference
+ */
class FunctionOutput extends TFunctionOutput {
abstract string toString();
- predicate isOutParameterPointer(ParameterIndex i) { none() }
+ /**
+ * Holds if this is 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, where the parameter has
+ * index `index`.
+ *
+ * Example:
+ * ```
+ * void func(int n, char* p, float& r);
+ * ```
+ * - `isParameterDeref(1)` holds for the `FunctionOutput` that represents the value of `*p` (with
+ * type `char`) on return from the function.
+ * - `isParameterDeref(2)` holds for the `FunctionOutput` that represents the value of `r` (with
+ * type `float`) on return from the function.
+ * - There is no `FunctionOutput` for which `isParameterDeref(0)` holds, because `n` is neither a
+ * pointer nor a reference.
+ */
+ predicate isParameterDeref(ParameterIndex i) { none() }
- predicate isOutQualifier() { none() }
+ /**
+ * Holds if this is 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, where the parameter has
+ * index `index`.
+ * DEPRECATED: Use `isParameterDeref(index)` instead.
+ */
+ deprecated final predicate isOutParameterPointer(ParameterIndex index) { isParameterDeref(index) }
- predicate isOutReturnValue() { none() }
+ /**
+ * Holds if this is 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);
+ * };
+ * ```
+ * - `isQualifierObject()` holds for the `FunctionOutput` that represents the value of `*this`
+ * (with type `C`) on return from the function.
+ */
+ predicate isQualifierObject() { none() }
- predicate isOutReturnPointer() { none() }
+ /**
+ * Holds if this is the output value pointed to by the `this` pointer of an instance member
+ * function.
+ * DEPRECATED: Use `isQualifierObject()` instead.
+ */
+ deprecated final predicate isOutQualifier() { isQualifierObject() }
+
+ /**
+ * Holds if this is the value returned by a function.
+ *
+ * Example:
+ * ```
+ * int getInt();
+ * char* getPointer();
+ * float& getReference();
+ * ```
+ * - `isReturnValue()` holds for the `FunctionOutput` that represents the value returned by
+ * `getInt()` (with type `int`).
+ * - `isReturnValue()` holds for the `FunctionOutput` that represents the value returned by
+ * `getPointer()` (with type `char*`).
+ * - `isReturnValue()` holds for the `FunctionOutput` that represents the "value" of the reference
+ * returned by `getReference()` (with type `float&`), _not_ the value of the referred-to
+ * `float`.
+ */
+ predicate isReturnValue() { none() }
+
+ /**
+ * Holds if this is the value returned by a function.
+ * DEPRECATED: Use `isReturnValue()` instead.
+ */
+ deprecated final predicate isOutReturnValue() { isReturnValue() }
+
+ /**
+ * Holds if this is 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();
+ * ```
+ * - `isReturnValueDeref()` holds for the `FunctionOutput` that represents the value of
+ * `*getPointer()` (with type `char`).
+ * - `isReturnValueDeref()` holds for the `FunctionOutput` that represents the value of
+ * `getReference()` (with type `float`).
+ * - There is no `FunctionOutput` of `getInt()` for which `isReturnValueDeref()` holds because the
+ * return type of `getInt()` is neither a pointer nor a reference.
+ */
+ predicate isReturnValueDeref() { none() }
+
+ /**
+ * Holds if this is 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.
+ * DEPRECATED: Use `isReturnValueDeref()` instead.
+ */
+ deprecated final predicate isOutReturnPointer() { isReturnValueDeref() }
}
-class OutParameterPointer extends FunctionOutput, TOutParameterPointer {
+class OutParameterDeref extends FunctionOutput, TOutParameterDeref {
ParameterIndex index;
- OutParameterPointer() { this = TOutParameterPointer(index) }
+ OutParameterDeref() { this = TOutParameterDeref(index) }
- override string toString() { result = "OutParameterPointer " + index.toString() }
+ override string toString() { result = "OutParameterDeref " + index.toString() }
ParameterIndex getIndex() { result = index }
- override predicate isOutParameterPointer(ParameterIndex i) { i = index }
+ override predicate isParameterDeref(ParameterIndex i) { i = index }
}
-class OutQualifier extends FunctionOutput, TOutQualifier {
- override string toString() { result = "OutQualifier" }
+class OutQualifierObject extends FunctionOutput, TOutQualifierObject {
+ override string toString() { result = "OutQualifierObject" }
- override predicate isOutQualifier() { any() }
+ override predicate isQualifierObject() { any() }
}
class OutReturnValue extends FunctionOutput, TOutReturnValue {
override string toString() { result = "OutReturnValue" }
- override predicate isOutReturnValue() { any() }
+ override predicate isReturnValue() { any() }
}
-class OutReturnPointer extends FunctionOutput, TOutReturnPointer {
- override string toString() { result = "OutReturnPointer" }
+class OutReturnValueDeref extends FunctionOutput, TOutReturnValueDeref {
+ override string toString() { result = "OutReturnValueDeref" }
- override predicate isOutReturnPointer() { any() }
+ override predicate isReturnValueDeref() { any() }
}
diff --git a/cpp/ql/src/semmle/code/cpp/models/interfaces/SideEffect.qll b/cpp/ql/src/semmle/code/cpp/models/interfaces/SideEffect.qll
index 0afd55501c6..905e79eb32b 100644
--- a/cpp/ql/src/semmle/code/cpp/models/interfaces/SideEffect.qll
+++ b/cpp/ql/src/semmle/code/cpp/models/interfaces/SideEffect.qll
@@ -9,6 +9,7 @@
import semmle.code.cpp.Function
import semmle.code.cpp.models.Models
+import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
/**
* Models the side effects of a library function.
@@ -19,12 +20,33 @@ abstract class SideEffectFunction extends Function {
* This memory could be from global variables, or from other memory that was reachable from a
* pointer that was passed into the function.
*/
- abstract predicate neverReadsMemory();
+ abstract predicate hasOnlySpecificReadSideEffects();
/**
* Holds if the function never writes to memory that remains allocated after the function
* returns. This memory could be from global variables, or from other memory that was reachable
* from a pointer that was passed into the function.
*/
- abstract predicate neverWritesMemory();
+ abstract predicate hasOnlySpecificWriteSideEffects();
+
+ /**
+ * Holds if the value pointed to by the parameter at index `i` is written to. `buffer` is true
+ * if the write may be at an offset. `mustWrite` is true if the write is unconditional.
+ */
+ predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
+ none()
+ }
+
+ /**
+ * Holds if the value pointed to by the parameter at index `i` is read from. `buffer` is true
+ * if the read may be at an offset.
+ */
+ predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { none() }
+
+ // TODO: name?
+ /**
+ * Gets the index of the parameter that indicates the size of the buffer pointed to by the
+ * parameter at index `i`.
+ */
+ ParameterIndex getParameterSizeIndex(ParameterIndex i) { none() }
}
diff --git a/cpp/ql/src/semmle/code/cpp/padding/Padding.qll b/cpp/ql/src/semmle/code/cpp/padding/Padding.qll
index b2065a0cb37..6b3d4c3d4aa 100644
--- a/cpp/ql/src/semmle/code/cpp/padding/Padding.qll
+++ b/cpp/ql/src/semmle/code/cpp/padding/Padding.qll
@@ -2,8 +2,8 @@ import cpp
/**
* Align the specified offset up to the specified alignment boundary.
- * The result is the smallest integer i such that (i % alignment) = 0
- * and (i >= offset)
+ * The result is the smallest integer `i` such that `(i % alignment) = 0`
+ * and `(i >= offset)`.
*/
bindingset[offset, alignment]
private int alignUp(int offset, int alignment) {
@@ -30,16 +30,16 @@ abstract class Architecture extends string {
/** Gets the size of a pointer, in bits. */
abstract int pointerSize();
- /** Gets the size of a 'long int', in bits. */
+ /** Gets the size of a `long int`, in bits. */
abstract int longSize();
- /** Gets the size of a 'long double', in bits. */
+ /** Gets the size of a `long double`, in bits. */
abstract int longDoubleSize();
- /** Gets the size of a 'long long', in bits. */
+ /** Gets the size of a `long long`, in bits. */
abstract int longLongSize();
- /** Gets the size of a 'wchar_t', in bits. */
+ /** Gets the size of a `wchar_t`, in bits. */
abstract int wideCharSize();
/** Gets the alignment boundary for doubles, in bits. */
@@ -479,8 +479,10 @@ class PaddedType extends Class {
int typeBitSize(Architecture arch) {
if this instanceof Union
then
- // A correct implementation for unions would be
+ // A correct implementation for unions would be:
+ // ```
// result = max(fieldSize(_, arch))
+ // ```
// but that uses a recursive aggregate, which isn't supported in
// QL. We therefore use this slightly more complex implementation
// instead.
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll
index 50fe6ab9a55..b663f336d0a 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll
@@ -80,7 +80,7 @@ private module RangeAnalysisCache {
cached
module RangeAnalysisPublic {
/**
- * Holds if `b + delta` is a valid bound for `i`.
+ * 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`
*
@@ -90,11 +90,12 @@ private module RangeAnalysisCache {
*/
cached
predicate boundedInstruction(Instruction i, Bound b, int delta, boolean upper, Reason reason) {
- boundedInstruction(i, b, delta, upper, _, _, reason)
+ boundedInstruction(i, b, delta, upper, _, _, reason) and
+ bestInstructionBound(i, b, delta, upper)
}
/**
- * Holds if `b + delta` is a valid bound for `op`.
+ * 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`
*
@@ -104,9 +105,8 @@ private module RangeAnalysisCache {
*/
cached
predicate boundedOperand(Operand op, Bound b, int delta, boolean upper, Reason reason) {
- boundedNonPhiOperand(op, b, delta, upper, _, _, reason)
- or
- boundedPhiOperand(op, b, delta, upper, _, _, reason)
+ boundedOperandCand(op, b, delta, upper, reason) and
+ bestOperandBound(op, b, delta, upper)
}
}
@@ -124,6 +124,43 @@ private module RangeAnalysisCache {
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`.
*
diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll
index a7d9b56a65c..61068801978 100644
--- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll
+++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeSSA.qll
@@ -2,7 +2,7 @@
* This library is a clone of semmle.code.cpp.controlflow.SSA, with
* only one difference: extra phi definitions are added after
* guards. For example:
- *
+ * ```
* x = f();
* if (x < 10) {
* // Block 1
@@ -11,12 +11,12 @@
* // Block 2
* ...
* }
- *
+ * ```
* In standard SSA, basic blocks 1 and 2 do not need phi definitions
- * for x, because they are dominated by the definition of x on the
- * first line. In RangeSSA, however, we add phi definitions for x at
+ * for `x`, because they are dominated by the definition of `x` on the
+ * first line. In RangeSSA, however, we add phi definitions for `x` at
* the beginning of blocks 1 and 2. This is useful for range analysis
- * because it enables us to deduce a more accurate range for x in the
+ * because it enables us to deduce a more accurate range for `x` in the
* two branches of the if-statement.
*/
@@ -74,19 +74,19 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
/**
* A string representation of the SSA variable represented by the pair
- * (this, v).
+ * `(this, v)`.
*/
string toString(LocalScopeVariable v) { exists(RangeSSA x | result = x.toString(this, v)) }
- /** Gets a use of the SSA variable represented by the pair (this, v) */
+ /** Gets a use of the SSA variable represented by the pair `(this, v)`. */
VariableAccess getAUse(LocalScopeVariable v) { exists(RangeSSA x | result = x.getAUse(this, v)) }
- /** Gets the control flow node for this definition */
+ /** Gets the control flow node for this definition. */
ControlFlowNode getDefinition() { result = this }
BasicBlock getBasicBlock() { result.contains(getDefinition()) }
- /** Whether this definition is a phi node for variable v */
+ /** Whether this definition is a phi node for variable `v`. */
predicate isPhiNode(LocalScopeVariable v) {
exists(RangeSSA x | x.phi_node(v, this.(BasicBlock)))
}
@@ -136,7 +136,7 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
)
}
- /** Gets the expression assigned to this SsaDefinition */
+ /** Gets the expression assigned to this SsaDefinition. */
Expr getDefiningValue(LocalScopeVariable v) {
exists(ControlFlowNode def | def = this.getDefinition() |
def = v.getInitializer().getExpr() and def = result
diff --git a/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll b/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll
index 9988e20290c..c7bea7eede7 100644
--- a/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/CommandExecution.qll
@@ -8,7 +8,7 @@ import semmle.code.cpp.security.FunctionWithWrappers
*/
class SystemFunction extends FunctionWithWrappers {
SystemFunction() {
- hasGlobalName("system") or
+ hasGlobalOrStdName("system") or
hasGlobalName("popen") or
// Windows variants
hasGlobalName("_popen") or
diff --git a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll
index 6363e3a5d14..219f3d0a75b 100644
--- a/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/FileWrite.qll
@@ -125,7 +125,7 @@ private predicate fileWrite(Call write, Expr source, Expr dest) {
exists(Function f, int s, int d |
f = write.getTarget() and source = write.getArgument(s) and dest = write.getArgument(d)
|
- exists(string name | f.hasGlobalName(name) |
+ exists(string name | f.hasGlobalOrStdName(name) |
// named functions
name = "fwrite" and s = 0 and d = 3
or
diff --git a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll
index 751a7a71ec7..06abfdb454d 100644
--- a/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/OutputWrite.qll
@@ -63,8 +63,8 @@ private predicate outputWrite(Expr write, Expr source) {
or
// puts, putchar
(
- f.hasGlobalName("puts") or
- f.hasGlobalName("putchar")
+ f.hasGlobalOrStdName("puts") or
+ f.hasGlobalOrStdName("putchar")
) and
arg = 0
or
diff --git a/cpp/ql/src/semmle/code/cpp/security/Security.qll b/cpp/ql/src/semmle/code/cpp/security/Security.qll
index 1a1c1d61ce8..c12a70b52c6 100644
--- a/cpp/ql/src/semmle/code/cpp/security/Security.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/Security.qll
@@ -70,11 +70,9 @@ class SecurityOptions extends string {
*/
predicate userInputArgument(FunctionCall functionCall, int arg) {
exists(string fname |
- functionCall.getTarget().hasGlobalName(fname) and
+ functionCall.getTarget().hasGlobalOrStdName(fname) and
exists(functionCall.getArgument(arg)) and
(
- fname = "read" and arg = 1
- or
fname = "fread" and arg = 0
or
fname = "fgets" and arg = 0
@@ -83,6 +81,16 @@ class SecurityOptions extends string {
or
fname = "gets" and arg = 0
or
+ fname = "scanf" and arg >= 1
+ or
+ fname = "fscanf" and arg >= 2
+ )
+ or
+ functionCall.getTarget().hasGlobalName(fname) and
+ exists(functionCall.getArgument(arg)) and
+ (
+ fname = "read" and arg = 1
+ or
fname = "getaddrinfo" and arg = 3
or
fname = "recv" and arg = 1
@@ -91,10 +99,6 @@ class SecurityOptions extends string {
(arg = 1 or arg = 4 or arg = 5)
or
fname = "recvmsg" and arg = 1
- or
- fname = "scanf" and arg >= 1
- or
- fname = "fscanf" and arg >= 2
)
)
}
diff --git a/cpp/ql/src/semmle/code/cpp/security/TaintTracking.qll b/cpp/ql/src/semmle/code/cpp/security/TaintTracking.qll
index 1ef0a6f7092..31d8ad00f9c 100644
--- a/cpp/ql/src/semmle/code/cpp/security/TaintTracking.qll
+++ b/cpp/ql/src/semmle/code/cpp/security/TaintTracking.qll
@@ -425,41 +425,41 @@ private int maxArgIndex(Function f) {
/** Functions that copy the value of one argument to another */
private predicate copyValueBetweenArguments(Function f, int sourceArg, int destArg) {
- f.hasGlobalName("memcpy") and sourceArg = 1 and destArg = 0
+ f.hasGlobalOrStdName("memcpy") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("__builtin___memcpy_chk") and sourceArg = 1 and destArg = 0
or
- f.hasGlobalName("memmove") and sourceArg = 1 and destArg = 0
+ f.hasGlobalOrStdName("memmove") and sourceArg = 1 and destArg = 0
or
- f.hasGlobalName("strcat") and sourceArg = 1 and destArg = 0
+ f.hasGlobalOrStdName("strcat") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("_mbscat") and sourceArg = 1 and destArg = 0
or
- f.hasGlobalName("wcsncat") and sourceArg = 1 and destArg = 0
+ f.hasGlobalOrStdName("wcscat") and sourceArg = 1 and destArg = 0
or
- f.hasGlobalName("strncat") and sourceArg = 1 and destArg = 0
+ f.hasGlobalOrStdName("strncat") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("_mbsncat") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("wcsncat") and sourceArg = 1 and destArg = 0
or
- f.hasGlobalName("strcpy") and sourceArg = 1 and destArg = 0
+ f.hasGlobalOrStdName("strcpy") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("_mbscpy") and sourceArg = 1 and destArg = 0
or
- f.hasGlobalName("wcscpy") and sourceArg = 1 and destArg = 0
+ f.hasGlobalOrStdName("wcscpy") and sourceArg = 1 and destArg = 0
or
- f.hasGlobalName("strncpy") and sourceArg = 1 and destArg = 0
+ f.hasGlobalOrStdName("strncpy") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("_mbsncpy") and sourceArg = 1 and destArg = 0
or
- f.hasGlobalName("wcsncpy") and sourceArg = 1 and destArg = 0
+ f.hasGlobalOrStdName("wcsncpy") and sourceArg = 1 and destArg = 0
or
f.hasGlobalName("inet_aton") and sourceArg = 0 and destArg = 1
or
f.hasGlobalName("inet_pton") and sourceArg = 1 and destArg = 2
or
- f.hasGlobalName("strftime") and sourceArg in [2 .. maxArgIndex(f)] and destArg = 0
+ f.hasGlobalOrStdName("strftime") and sourceArg in [2 .. maxArgIndex(f)] and destArg = 0
or
exists(FormattingFunction ff | ff = f |
sourceArg in [ff.getFormatParameterIndex() .. maxArgIndex(f)] and
@@ -473,31 +473,31 @@ private predicate returnArgument(Function f, int sourceArg) {
or
f.hasGlobalName("__builtin___memcpy_chk") and sourceArg = 0
or
- f.hasGlobalName("memmove") and sourceArg = 0
+ f.hasGlobalOrStdName("memmove") and sourceArg = 0
or
- f.hasGlobalName("strcat") and sourceArg = 0
+ f.hasGlobalOrStdName("strcat") and sourceArg = 0
or
f.hasGlobalName("_mbscat") and sourceArg = 0
or
- f.hasGlobalName("wcsncat") and sourceArg = 0
+ f.hasGlobalOrStdName("wcsncat") and sourceArg = 0
or
- f.hasGlobalName("strncat") and sourceArg = 0
+ f.hasGlobalOrStdName("strncat") and sourceArg = 0
or
f.hasGlobalName("_mbsncat") and sourceArg = 0
or
- f.hasGlobalName("wcsncat") and sourceArg = 0
+ f.hasGlobalOrStdName("wcsncat") and sourceArg = 0
or
- f.hasGlobalName("strcpy") and sourceArg = 0
+ f.hasGlobalOrStdName("strcpy") and sourceArg = 0
or
f.hasGlobalName("_mbscpy") and sourceArg = 0
or
- f.hasGlobalName("wcscpy") and sourceArg = 0
+ f.hasGlobalOrStdName("wcscpy") and sourceArg = 0
or
- f.hasGlobalName("strncpy") and sourceArg = 0
+ f.hasGlobalOrStdName("strncpy") and sourceArg = 0
or
f.hasGlobalName("_mbsncpy") and sourceArg = 0
or
- f.hasGlobalName("wcsncpy") and sourceArg = 0
+ f.hasGlobalOrStdName("wcsncpy") and sourceArg = 0
or
f.hasGlobalName("inet_ntoa") and sourceArg = 0
or
diff --git a/cpp/ql/src/semmle/code/cpp/security/boostorg/asio/protocols.qll b/cpp/ql/src/semmle/code/cpp/security/boostorg/asio/protocols.qll
new file mode 100644
index 00000000000..e113d5e5745
--- /dev/null
+++ b/cpp/ql/src/semmle/code/cpp/security/boostorg/asio/protocols.qll
@@ -0,0 +1,491 @@
+import cpp
+import semmle.code.cpp.dataflow.DataFlow
+
+module BoostorgAsio {
+ /**
+ * Represents the `boost::asio::ssl::context` enum.
+ */
+ class SslContextMethod extends Enum {
+ SslContextMethod() {
+ this.getName().toString() = "method" and
+ this.getQualifiedName().toString().matches("boost::asio::ssl::context%")
+ }
+
+ /**
+ * Gets an enumeration constant for a banned protocol.
+ */
+ EnumConstant getABannedProtocolConstant() {
+ result = this.getAnEnumConstant() and
+ (
+ /// Generic SSL version 2.
+ result.getName() = "sslv2"
+ or
+ /// SSL version 2 client.
+ result.getName() = "sslv2_client"
+ or
+ /// SSL version 2 server.
+ result.getName() = "sslv2_server"
+ or
+ /// Generic SSL version 3.
+ result.getName() = "sslv3"
+ or
+ /// SSL version 3 client.
+ result.getName() = "sslv3_client"
+ or
+ /// SSL version 3 server.
+ result.getName() = "sslv3_server"
+ or
+ /// Generic TLS version 1.
+ result.getName() = "tlsv1"
+ or
+ /// TLS version 1 client.
+ result.getName() = "tlsv1_client"
+ or
+ /// TLS version 1 server.
+ result.getName() = "tlsv1_server"
+ or
+ /// Generic TLS version 1.1.
+ result.getName() = "tlsv11"
+ or
+ /// TLS version 1.1 client.
+ result.getName() = "tlsv11_client"
+ or
+ /// TLS version 1.1 server.
+ result.getName() = "tlsv11_server"
+ )
+ }
+
+ /**
+ * Gets an enumeration constant for an approved protocol, that is hard-coded
+ * (no protocol negotiation).
+ */
+ EnumConstant getAnApprovedButHardcodedProtocolConstant() {
+ result = this.getATls12ProtocolConstant()
+ }
+
+ /**
+ * Gets an enumeration constant for a TLS v1.2 protocol.
+ */
+ EnumConstant getATls12ProtocolConstant() {
+ result = this.getAnEnumConstant() and
+ (
+ /// Generic TLS version 1.2.
+ result.getName() = "tlsv12"
+ or
+ /// TLS version 1.2 client.
+ result.getName() = "tlsv12_client"
+ or
+ /// TLS version 1.2 server.
+ result.getName() = "tlsv12_server"
+ )
+ }
+
+ /**
+ * Gets an enumeration constant for a TLS v1.3 protocol.
+ */
+ EnumConstant getATls13ProtocolConstant() {
+ result = this.getAnEnumConstant() and
+ (
+ /// Generic TLS version 1.3.
+ result.getName() = "tlsv13"
+ or
+ /// TLS version 1.3 client.
+ result.getName() = "tlsv13_client"
+ or
+ /// TLS version 1.3 server.
+ result.getName() = "tlsv13_server"
+ )
+ }
+
+ /**
+ * Gets an enumeration constant for a generic TLS or SSL/TLS protocol.
+ */
+ EnumConstant getAGenericTlsProtocolConstant() {
+ result = this.getAnEnumConstant() and
+ (
+ /// Generic TLS
+ result.getName() = "tls"
+ or
+ /// TLS client.
+ result.getName() = "tls_client"
+ or
+ /// TLS server.
+ result.getName() = "tls_server"
+ )
+ or
+ result = getASslv23ProtocolConstant()
+ }
+
+ /**
+ * Gets an enumeration constant for a generic SSL/TLS protocol.
+ */
+ EnumConstant getASslv23ProtocolConstant() {
+ result = this.getAnEnumConstant() and
+ (
+ /// OpenSSL - SSLv23 == A TLS/SSL connection established with these methods may understand the SSLv2, SSLv3, TLSv1, TLSv1.1 and TLSv1.2 protocols.
+ /// Generic SSL/TLS.
+ result.getName() = "sslv23"
+ or
+ /// SSL/TLS client.
+ result.getName() = "sslv23_client"
+ or
+ /// SSL/TLS server.
+ result.getName() = "sslv23_server"
+ )
+ }
+ }
+
+ /**
+ * Gets the value for the no_sslv2 constant, right shifted by 16 bits.
+ *
+ * Note that modern versions of OpelSSL do not support SSL v2, so this option is for backwards compatibility only.
+ */
+ int getShiftedSslOptionsNoSsl2() {
+ // SSL_OP_NO_SSLv2 was removed from modern OpenSSL versions
+ result = 0
+ }
+
+ /**
+ * Gets the value for the no_sslv3 constant, right shifted by 16 bits.
+ */
+ int getShiftedSslOptionsNoSsl3() {
+ // SSL_OP_NO_SSLv3 == 0x02000000U
+ result = 512
+ }
+
+ /**
+ * Gets the value for the no_tlsv1 constant, right shifted by 16 bits.
+ */
+ int getShiftedSslOptionsNoTls1() {
+ // SSL_OP_NO_TLSv1 == 0x04000000U
+ result = 1024
+ }
+
+ /**
+ * Gets the value for the no_tlsv1_1 constant, right shifted by 16 bits.
+ */
+ int getShiftedSslOptionsNoTls1_1() {
+ // SSL_OP_NO_TLSv1_1 == 0x10000000U
+ result = 4096
+ }
+
+ /**
+ * Gets the value for the no_tlsv1_2 constant, right shifted by 16 bits.
+ */
+ int getShiftedSslOptionsNoTls1_2() {
+ // SSL_OP_NO_TLSv1_2 == 0x08000000U
+ result = 2048
+ }
+
+ /**
+ * Gets the value for the no_tlsv1_3 constant, right shifted by 16 bits.
+ */
+ int getShiftedSslOptionsNoTls1_3() {
+ // SSL_OP_NO_TLSv1_2 == 0x20000000U
+ result = 8192
+ }
+
+ /**
+ * Represents the `boost::asio::ssl::context` class.
+ */
+ class SslContextClass extends Class {
+ SslContextClass() { this.getQualifiedName() = "boost::asio::ssl::context" }
+
+ ConstructorCall getAContructorCall() {
+ this.getAConstructor().getACallToThisFunction() = result and
+ not result.getLocation().getFile().toString().matches("%/boost/asio/%") and
+ result.fromSource()
+ }
+ }
+
+ /**
+ * Represents `boost::asio::ssl::context::set_options` member function.
+ */
+ class SslSetOptionsFunction extends Function {
+ SslSetOptionsFunction() {
+ this.getQualifiedName().matches("boost::asio::ssl::context::set_options")
+ }
+ }
+
+ /**
+ * Holds if the expression represents a banned protocol.
+ */
+ predicate isExprBannedBoostProtocol(Expr e) {
+ exists(Literal va | va = e |
+ va.getValue().toInt() = 0 or
+ va.getValue().toInt() = 1 or
+ va.getValue().toInt() = 2 or
+ va.getValue().toInt() = 3 or
+ va.getValue().toInt() = 4 or
+ va.getValue().toInt() = 5 or
+ va.getValue().toInt() = 6 or
+ va.getValue().toInt() = 7 or
+ va.getValue().toInt() = 8 or
+ va.getValue().toInt() = 12 or
+ va.getValue().toInt() = 13 or
+ va.getValue().toInt() = 14
+ )
+ or
+ exists(VariableAccess va | va = e |
+ va.getValue().toInt() = 0 or
+ va.getValue().toInt() = 1 or
+ va.getValue().toInt() = 2 or
+ va.getValue().toInt() = 3 or
+ va.getValue().toInt() = 4 or
+ va.getValue().toInt() = 5 or
+ va.getValue().toInt() = 6 or
+ va.getValue().toInt() = 7 or
+ va.getValue().toInt() = 8 or
+ va.getValue().toInt() = 12 or
+ va.getValue().toInt() = 13 or
+ va.getValue().toInt() = 14
+ )
+ or
+ exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
+ enum.getABannedProtocolConstant().getAnAccess() = eca
+ )
+ }
+
+ /**
+ * Holds if the expression represents a TLS v1.2 protocol.
+ */
+ predicate isExprTls12BoostProtocol(Expr e) {
+ exists(Literal va | va = e |
+ (
+ va.getValue().toInt() = 15 or /// Generic TLS version 1.2.
+ va.getValue().toInt() = 16 or /// TLS version 1.2 client.
+ va.getValue().toInt() = 17 /// TLS version 1.2 server.
+ )
+ )
+ or
+ exists(VariableAccess va | va = e |
+ (
+ va.getValue().toInt() = 15 or /// Generic TLS version 1.2.
+ va.getValue().toInt() = 16 or /// TLS version 1.2 client.
+ va.getValue().toInt() = 17 /// TLS version 1.2 server.
+ )
+ )
+ or
+ exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
+ enum.getATls12ProtocolConstant().getAnAccess() = eca
+ )
+ }
+
+ /**
+ * Holds if the expression represents a protocol that requires Crypto Board approval.
+ */
+ predicate isExprTls13BoostProtocol(Expr e) {
+ exists(Literal va | va = e |
+ (
+ va.getValue().toInt() = 18 or
+ va.getValue().toInt() = 19 or
+ va.getValue().toInt() = 20
+ )
+ )
+ or
+ exists(VariableAccess va | va = e |
+ (
+ va.getValue().toInt() = 18 or
+ va.getValue().toInt() = 19 or
+ va.getValue().toInt() = 20
+ )
+ )
+ or
+ exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
+ enum.getATls13ProtocolConstant().getAnAccess() = eca
+ )
+ }
+
+ /**
+ * Holds if the expression represents a generic TLS or SSL/TLS protocol.
+ */
+ predicate isExprTlsBoostProtocol(Expr e) {
+ exists(Literal va | va = e |
+ (
+ va.getValue().toInt() = 9 or /// Generic SSL/TLS.
+ va.getValue().toInt() = 10 or /// SSL/TLS client.
+ va.getValue().toInt() = 11 or /// SSL/TLS server.
+ va.getValue().toInt() = 21 or /// Generic TLS.
+ va.getValue().toInt() = 22 or /// TLS client.
+ va.getValue().toInt() = 23 /// TLS server.
+ )
+ )
+ or
+ exists(VariableAccess va | va = e |
+ (
+ va.getValue().toInt() = 9 or /// Generic SSL/TLS.
+ va.getValue().toInt() = 10 or /// SSL/TLS client.
+ va.getValue().toInt() = 11 or /// SSL/TLS server.
+ va.getValue().toInt() = 21 or /// Generic TLS.
+ va.getValue().toInt() = 22 or /// TLS client.
+ va.getValue().toInt() = 23 /// TLS server.
+ )
+ )
+ or
+ exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
+ enum.getAGenericTlsProtocolConstant().getAnAccess() = eca
+ )
+ }
+
+ /**
+ * Holds if the expression represents a generic SSl/TLS protocol.
+ */
+ predicate isExprSslV23BoostProtocol(Expr e) {
+ exists(Literal va | va = e |
+ (
+ va.getValue().toInt() = 9 or /// Generic SSL/TLS.
+ va.getValue().toInt() = 10 or /// SSL/TLS client.
+ va.getValue().toInt() = 11 /// SSL/TLS server.
+ )
+ )
+ or
+ exists(VariableAccess va | va = e |
+ (
+ va.getValue().toInt() = 9 or /// Generic SSL/TLS.
+ va.getValue().toInt() = 10 or /// SSL/TLS client.
+ va.getValue().toInt() = 11 /// SSL/TLS server.
+ )
+ )
+ or
+ exists(EnumConstantAccess eca, SslContextMethod enum | e = eca |
+ enum.getASslv23ProtocolConstant().getAnAccess() = eca
+ )
+ }
+
+ //////////////////////// Dataflow /////////////////////
+ /**
+ * Abstract class for flows of protocol values to the first argument of a context
+ * constructor.
+ */
+ abstract class SslContextCallAbstractConfig extends DataFlow::Configuration {
+ bindingset[this]
+ SslContextCallAbstractConfig() { any() }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(ConstructorCall cc, SslContextClass c, Expr e | e = sink.asExpr() |
+ c.getAContructorCall() = cc and
+ cc.getArgument(0) = e
+ )
+ }
+ }
+
+ /**
+ * Any protocol value that flows to the first argument of a context constructor.
+ */
+ class SslContextCallConfig extends SslContextCallAbstractConfig {
+ SslContextCallConfig() { this = "SslContextCallConfig" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(Expr e | e = source.asExpr() |
+ e.fromSource() and
+ not e.getLocation().getFile().toString().matches("%/boost/asio/%")
+ )
+ }
+ }
+
+ /**
+ * A banned protocol value that flows to the first argument of a context constructor.
+ */
+ class SslContextCallBannedProtocolConfig extends SslContextCallAbstractConfig {
+ SslContextCallBannedProtocolConfig() { this = "SslContextCallBannedProtocolConfig" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(Expr e | e = source.asExpr() |
+ e.fromSource() and
+ not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
+ isExprBannedBoostProtocol(e)
+ )
+ }
+ }
+
+ /**
+ * A TLS 1.2 protocol value that flows to the first argument of a context constructor.
+ */
+ class SslContextCallTls12ProtocolConfig extends SslContextCallAbstractConfig {
+ SslContextCallTls12ProtocolConfig() { this = "SslContextCallTls12ProtocolConfig" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(Expr e | e = source.asExpr() |
+ e.fromSource() and
+ not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
+ isExprTls12BoostProtocol(e)
+ )
+ }
+ }
+
+ /**
+ * A TLS 1.3 protocol value that flows to the first argument of a context constructor.
+ */
+ class SslContextCallTls13ProtocolConfig extends SslContextCallAbstractConfig {
+ SslContextCallTls13ProtocolConfig() { this = "SslContextCallTls12ProtocolConfig" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(Expr e | e = source.asExpr() |
+ e.fromSource() and
+ not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
+ isExprTls13BoostProtocol(e)
+ )
+ }
+ }
+
+ /**
+ * A generic TLS protocol value that flows to the first argument of a context constructor.
+ */
+ class SslContextCallTlsProtocolConfig extends SslContextCallAbstractConfig {
+ SslContextCallTlsProtocolConfig() { this = "SslContextCallTlsProtocolConfig" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(Expr e | e = source.asExpr() |
+ e.fromSource() and
+ not e.getLocation().getFile().toString().matches("%/boost/asio/%") and
+ isExprTlsBoostProtocol(e)
+ )
+ }
+ }
+
+ /**
+ * A context constructor call that flows to a call to `SetOptions()`.
+ */
+ class SslContextFlowsToSetOptionConfig extends DataFlow::Configuration {
+ SslContextFlowsToSetOptionConfig() { this = "SslContextFlowsToSetOptionConfig" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(SslContextClass c, ConstructorCall cc |
+ cc = source.asExpr() and
+ c.getAContructorCall() = cc
+ )
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(FunctionCall fc, SslSetOptionsFunction f, Variable v, VariableAccess va |
+ va = sink.asExpr()
+ |
+ f.getACallToThisFunction() = fc and
+ v.getAnAccess() = va and
+ va = fc.getQualifier()
+ )
+ }
+ }
+
+ /**
+ * An option value that flows to the first parameter of a call to `SetOptions()`.
+ */
+ class SslOptionConfig extends DataFlow::Configuration {
+ SslOptionConfig() { this = "SslOptionConfig" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(Expr e | e = source.asExpr() |
+ e.fromSource() and
+ not e.getLocation().getFile().toString().matches("%/boost/asio/%")
+ )
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(SslSetOptionsFunction f, FunctionCall call |
+ sink.asExpr() = call.getArgument(0) and
+ f.getACallToThisFunction() = call and
+ not sink.getLocation().getFile().toString().matches("%/boost/asio/%")
+ )
+ }
+ }
+}
diff --git a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
index 73d54c678b0..aa675261b14 100644
--- a/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
+++ b/cpp/ql/src/semmle/code/cpp/stmts/Stmt.qll
@@ -294,6 +294,8 @@ class IfStmt extends ConditionalStmt, @stmt_if {
* ```
*/
class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
+ override string getCanonicalQLClass() { result = "ConstexprIfStmt" }
+
/**
* Gets the condition expression of this 'constexpr if' statement.
*
@@ -1645,6 +1647,7 @@ class EnumSwitch extends SwitchStmt {
* } catch (std::exception &e) {
* g();
* }
+ * ```
* there is a handler that's associated with the `catch` block and controls
* entry to it.
*/
diff --git a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll
index aa41af86f97..1077a01c946 100644
--- a/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll
+++ b/cpp/ql/src/semmle/code/cpp/valuenumbering/HashCons.qll
@@ -729,6 +729,18 @@ private predicate mk_AlignofExpr(HashCons child, AlignofExprOperator e) {
child = hashCons(e.getAChild())
}
+/**
+ * Gets the hash cons of field initializer expressions [0..i), where i > 0, for
+ * the class aggregate literal `cal` of type `c`, where `head` is the hash cons
+ * of the i'th initializer expression.
+ */
+HC_Fields aggInitExprsUpTo(ClassAggregateLiteral cal, Class c, int i) {
+ exists(Field f, HashCons head, HC_Fields tail |
+ result = HC_FieldCons(c, i - 1, f, head, tail) and
+ mk_FieldCons(c, i - 1, f, head, tail, cal)
+ )
+}
+
private predicate mk_FieldCons(
Class c, int i, Field f, HashCons hc, HC_Fields hcf, ClassAggregateLiteral cal
) {
@@ -737,14 +749,11 @@ private predicate mk_FieldCons(
exists(Expr e |
e = cal.getFieldExpr(f).getFullyConverted() and
f.getInitializationOrder() = i and
- hc = hashCons(e) and
(
- exists(HashCons head, Field f2, HC_Fields tail |
- hcf = HC_FieldCons(c, i - 1, f2, head, tail) and
- f2.getInitializationOrder() = i - 1 and
- mk_FieldCons(c, i - 1, f2, head, tail, cal)
- )
+ hc = hashCons(e) and
+ hcf = aggInitExprsUpTo(cal, c, i)
or
+ hc = hashCons(e) and
i = 0 and
hcf = HC_EmptyFields(c)
)
@@ -765,12 +774,7 @@ private predicate mk_ClassAggregateLiteral(Class c, HC_Fields hcf, ClassAggregat
analyzableClassAggregateLiteral(cal) and
c = cal.getUnspecifiedType() and
(
- exists(HC_Fields tail, Expr e, Field f |
- f.getInitializationOrder() = cal.getNumChild() - 1 and
- e = cal.getFieldExpr(f).getFullyConverted() and
- hcf = HC_FieldCons(c, cal.getNumChild() - 1, f, hashCons(e), tail) and
- mk_FieldCons(c, cal.getNumChild() - 1, f, hashCons(e), tail, cal)
- )
+ hcf = aggInitExprsUpTo(cal, c, cal.getNumChild())
or
cal.getNumChild() = 0 and
hcf = HC_EmptyFields(c)
@@ -782,15 +786,23 @@ private predicate analyzableArrayAggregateLiteral(ArrayAggregateLiteral aal) {
strictcount(aal.getUnspecifiedType()) = 1
}
+/**
+ * Gets the hash cons of array elements in [0..i), where i > 0, for
+ * the array aggregate literal `aal` of type `t`.
+ */
+private HC_Array arrayElemsUpTo(ArrayAggregateLiteral aal, Type t, int i) {
+ exists(HC_Array tail, HashCons head |
+ result = HC_ArrayCons(t, i - 1, head, tail) and
+ mk_ArrayCons(t, i - 1, head, tail, aal)
+ )
+}
+
private predicate mk_ArrayCons(Type t, int i, HashCons hc, HC_Array hca, ArrayAggregateLiteral aal) {
analyzableArrayAggregateLiteral(aal) and
t = aal.getUnspecifiedType() and
hc = hashCons(aal.getChild(i)) and
(
- exists(HC_Array tail, HashCons head |
- hca = HC_ArrayCons(t, i - 1, head, tail) and
- mk_ArrayCons(t, i - 1, head, tail, aal)
- )
+ hca = arrayElemsUpTo(aal, t, i)
or
i = 0 and
hca = HC_EmptyArray(t)
@@ -800,9 +812,10 @@ private predicate mk_ArrayCons(Type t, int i, HashCons hc, HC_Array hca, ArrayAg
private predicate mk_ArrayAggregateLiteral(Type t, HC_Array hca, ArrayAggregateLiteral aal) {
t = aal.getUnspecifiedType() and
(
- exists(HashCons head, HC_Array tail |
- hca = HC_ArrayCons(t, aal.getNumChild() - 1, head, tail) and
- mk_ArrayCons(t, aal.getNumChild() - 1, head, tail, aal)
+ exists(HashCons head, HC_Array tail, int numElements |
+ numElements = aal.getNumChild() and
+ hca = HC_ArrayCons(t, numElements - 1, head, tail) and
+ mk_ArrayCons(t, numElements - 1, head, tail, aal)
)
or
aal.getNumChild() = 0 and
diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme b/cpp/ql/src/semmlecode.cpp.dbscheme
index c4c27a2661b..bd182f697bf 100644
--- a/cpp/ql/src/semmlecode.cpp.dbscheme
+++ b/cpp/ql/src/semmlecode.cpp.dbscheme
@@ -105,7 +105,7 @@ compilation_time(
*/
#keyset[compilation, file_number, file_number_diagnostic_number]
diagnostic_for(
- unique int diagnostic : @diagnostic ref,
+ int diagnostic : @diagnostic ref,
int compilation : @compilation ref,
int file_number : int ref,
int file_number_diagnostic_number : int ref
@@ -422,8 +422,9 @@ function_defaulted(unique int id: @function ref);
+#keyset[id, type_id]
fun_decls(
- unique int id: @fun_decl,
+ int id: @fun_decl,
int function: @function ref,
int type_id: @type ref,
string name: string ref,
@@ -460,8 +461,9 @@ param_decl_bind(
int fun_decl: @fun_decl ref
);
+#keyset[id, type_id]
var_decls(
- unique int id: @var_decl,
+ int id: @var_decl,
int variable: @variable ref,
int type_id: @type ref,
string name: string ref,
@@ -521,18 +523,21 @@ params(
overrides(int new: @function ref, int old: @function ref);
+#keyset[id, type_id]
membervariables(
- unique int id: @membervariable,
+ int id: @membervariable,
int type_id: @type ref,
string name: string ref
);
+#keyset[id, type_id]
globalvariables(
- unique int id: @globalvariable,
+ int id: @globalvariable,
int type_id: @type ref,
string name: string ref
);
+#keyset[id, type_id]
localvariables(
int id: @localvariable,
int type_id: @type ref,
@@ -675,7 +680,7 @@ decltypes(
| 2 = class
| 3 = union
| 4 = enum
- | 5 = typedef
+ | 5 = typedef // classic C: typedef typedef type name
| 6 = template
| 7 = template_parameter
| 8 = template_template_parameter
@@ -684,6 +689,7 @@ decltypes(
// ... 11 objc_protocol deprecated
// ... 12 objc_category deprecated
| 13 = scoped_enum
+ | 14 = using_alias // a using name = type style typedef
;
*/
usertypes(
@@ -725,6 +731,11 @@ class_template_argument(
int index: int ref,
int arg_type: @type ref
);
+class_template_argument_value(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
is_proxy_class_for(
unique int id: @usertype ref,
@@ -749,6 +760,11 @@ function_template_argument(
int index: int ref,
int arg_type: @type ref
);
+function_template_argument_value(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
is_variable_template(unique int id: @variable ref);
variable_instantiation(
@@ -760,6 +776,11 @@ variable_template_argument(
int index: int ref,
int arg_type: @type ref
);
+variable_template_argument_value(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
/*
Fixed point types
@@ -1060,10 +1081,12 @@ compgenerated(unique int id: @element ref);
* destructed in reverse construction order, so for a given `element`
* these should be called from highest to lowest `i`.
*/
+#keyset[element, destructor_call]
+#keyset[element, i]
synthetic_destructor_call(
int element: @element ref,
int i: int ref,
- unique int destructor_call: @routineexpr ref
+ int destructor_call: @routineexpr ref
);
namespaces(
@@ -1509,6 +1532,7 @@ case @expr.kind of
| 322 = @builtinaddressof
| 323 = @vec_fill
| 324 = @builtinconvertvector
+| 325 = @builtincomplex
;
new_allocated_type(
diff --git a/cpp/ql/src/semmlecode.cpp.dbscheme.stats b/cpp/ql/src/semmlecode.cpp.dbscheme.stats
index 85160eeeac5..3a73724456a 100644
--- a/cpp/ql/src/semmlecode.cpp.dbscheme.stats
+++ b/cpp/ql/src/semmlecode.cpp.dbscheme.stats
@@ -1,15 +1,15 @@
@compilation
-10590
+10177
@externalDataElement
-72
+70
@duplication
-185332
+185440
@similarity
@@ -17,7 +17,7 @@
@external_package
-133
+128
@svnentry
@@ -25,115 +25,115 @@
@location_default
-9666045
+9335283
@location_stmt
-2176932
+2178211
@location_expr
-9745314
+9746017
@diagnostic
-76172
+73204
@file
-66178
+63599
@folder
-12109
+11637
@macroinvocation
-40821746
+39320374
@function
-3566289
+3552236
@fun_decl
-3669417
+3635139
@var_decl
-5603886
+5495954
@type_decl
-1469408
+1413769
@namespace_decl
-151652
+145743
@using
-320654
+308161
@static_assert
-132361
+131017
@parameter
-4781221
+4724692
@membervariable
-329676
+321201
@globalvariable
-301143
+301136
@localvariable
-527113
+521758
@enumconstant
-94489
+94097
@builtintype
-559
+537
@derivedtype
-4572704
+4651908
@decltype
-45849
+44063
@usertype
-4328015
+4337028
@mangledname
-533074
+514256
@type_mention
-1699398
+1682135
@routinetype
-444646
+436132
@ptrtomember
-12182
+13227
@specifier
-547
+525
@gnuattribute
@@ -141,11 +141,11 @@
@stdattribute
-369
+365
@declspec
-58442
+57849
@msattribute
@@ -153,7 +153,7 @@
@alignas
-267
+257
@attribute_arg_empty
@@ -161,71 +161,55 @@
@attribute_arg_token
-15930
+15768
@attribute_arg_constant
-111500
+111565
@attribute_arg_type
-60
+58
@derivation
-370358
+392220
@frienddecl
-231217
+222208
@comment
-1743921
+1675974
@namespace
-8498
+8167
@specialnamequalifyingelement
-12
+11
@namequalifier
-1125647
+1116919
@value
-8776937
+8788031
@initialiser
-1685056
-
-
-@preincrexpr
-63965
-
-
-@predecrexpr
-26590
-
-
-@assignexpr
-551713
-
-
-@varaccess
-5376523
+1668506
@literal
-4374193
+4381545
@errorexpr
-48074
+46201
@address_of
@@ -233,15 +217,15 @@
@reference_to
-1078015
+1039133
@indirect
-294638
+294811
@ref_indirect
-1267930
+1225212
@array_to_pointer
@@ -249,31 +233,31 @@
@vacuous_destructor_call
-4449
+4288
@assume
-3328
+3294
@parexpr
-3001187
+3002950
@arithnegexpr
-654789
+654792
@unaryplusexpr
-206
+198
@complementexpr
-28027
+28043
@notexpr
-357915
+358125
@conjugation
@@ -281,43 +265,51 @@
@realpartexpr
-72
+70
@imagpartexpr
-72
+70
@postincrexpr
-43039
+43064
@postdecrexpr
-5401
+5404
+
+
+@preincrexpr
+61508
+
+
+@predecrexpr
+25554
@conditionalexpr
-154429
+154519
@addexpr
-209503
+209626
@subexpr
-134974
+135053
@mulexpr
-91616
+91670
@divexpr
-63973
+64010
@remexpr
-4657
+4660
@jmulexpr
@@ -345,59 +337,59 @@
@paddexpr
-87252
+87303
@psubexpr
-23151
+23165
@pdiffexpr
-25873
+24865
@lshiftexpr
-347564
+347768
@rshiftexpr
-59286
+59321
@andexpr
-236938
+237077
@orexpr
-145220
+143745
@xorexpr
-37281
+37303
@eqexpr
-212621
+212746
@neexpr
-88456
+88508
@gtexpr
-43936
+43962
@ltexpr
-54831
+54863
@geexpr
-22361
+22374
@leexpr
-213875
+214001
@minexpr
@@ -408,6 +400,10 @@
1
+@assignexpr
+552037
+
+
@assignaddexpr
68305
@@ -417,15 +413,15 @@
@assignmulexpr
-7599
+7302
@assigndivexpr
-2297
+2208
@assignremexpr
-303
+292
@assignlshiftexpr
@@ -437,39 +433,39 @@
@assignandexpr
-7740
+7744
@assignorexpr
-18663
+18674
@assignxorexpr
-21992
+22005
@assignpaddexpr
-13215
+13080
@assignpsubexpr
-583
+577
@andlogicalexpr
-116840
+116908
@orlogicalexpr
-57827
+57861
@commaexpr
-10723
+10329
@subscriptexpr
-165754
+165851
@virtfunptrexpr
@@ -477,15 +473,15 @@
@callexpr
-248555
+239197
@vastartexpr
-3710
+3673
@vaargexpr
-1094
+1051
@vaendexpr
@@ -496,60 +492,64 @@
30
+@varaccess
+5325591
+
+
@thisaccess
-1181367
+1169367
@new_expr
-29520
+28370
@delete_expr
-6565
+6309
@throw_expr
-23781
+22855
@condition_decl
-7647
+7349
@braced_init_list
-133
+128
@type_id
-4899
+4708
@runtime_sizeof
-281622
+281787
@runtime_alignof
-1560
+1561
@sizeof_pack
-267
+502
@expr_stmt
-160546
+160640
@routineexpr
-2308754
+2232038
@type_operand
-127928
+128003
@offsetofexpr
-35356
+35377
@hasassignexpr
@@ -597,11 +597,11 @@
@isbaseofexpr
-19
+39
@isclassexpr
-6
+215
@isconvtoexpr
@@ -609,15 +609,15 @@
@isemptyexpr
-5
+175
@isenumexpr
-3
+23
@ispodexpr
-3
+11
@ispolyexpr
@@ -629,7 +629,7 @@
@typescompexpr
-46285
+46312
@intaddrexpr
@@ -637,11 +637,11 @@
@hastrivialdestructor
-97
+105
@uuidof
-856
+848
@aggregateliteral
@@ -649,11 +649,11 @@
@delete_array_expr
-1495
+1437
@new_array_expr
-5365
+5310
@foldexpr
@@ -661,59 +661,59 @@
@ctordirectinit
-93279
+89645
@ctorvirtualinit
-6905
+6636
@ctorfieldinit
-209696
+201538
@ctordelegatinginit
-741
+712
@dtordirectdestruct
-30833
+30111
@dtorvirtualdestruct
-2699
+2594
@dtorfielddestruct
-32548
+31279
@static_cast
-224773
+226064
@reinterpret_cast
-31208
+30891
@const_cast
-5848
+5643
@dynamic_cast
-1094
+1051
@c_style_cast
-4242732
+4244118
@lambdaexpr
-12641
+12513
@param_ref
-53217
+51144
@noopexpr
@@ -745,7 +745,7 @@
@istrivialexpr
-2565
+2512
@isstandardlayoutexpr
@@ -753,7 +753,7 @@
@istriviallycopyableexpr
-48
+46
@isliteraltypeexpr
@@ -813,11 +813,11 @@
@isfinalexpr
-2
+175
@noexceptexpr
-449
+432
@builtinshufflevector
@@ -825,11 +825,11 @@
@builtinchooseexpr
-7028
+7032
@builtinaddressof
-4389
+4218
@vec_fill
@@ -840,68 +840,72 @@
1
+@builtincomplex
+4
+
+
@lambdacapture
-21730
+21509
@stmt_expr
-1284491
-
-
-@stmt_return
-1197363
-
-
-@stmt_block
-1398476
+1271443
@stmt_if
-524558
+524866
@stmt_while
-32912
+31630
@stmt_goto
-111432
+111498
@stmt_label
-85508
+85558
+
+
+@stmt_return
+1154730
+
+
+@stmt_block
+1348007
@stmt_end_test_while
-149900
+149988
@stmt_for
-32388
+32059
@stmt_switch_case
-281530
+281695
@stmt_switch
-55225
+55258
@stmt_asm
-251715
+251863
@stmt_try_block
-18967
+18228
@stmt_microsoft_try
-171
+169
@stmt_decl
-647242
+623601
@stmt_set_vla_size
@@ -913,79 +917,79 @@
@stmt_assigned_goto
-9137
+9142
@stmt_empty
-103683
+102630
@stmt_continue
-8603
+8608
@stmt_break
-232406
+232543
@stmt_range_based_for
-24
+23
@stmt_handler
-21888
+21666
@stmt_constexpr_if
3
-@ppd_if
-171105
-
-
-@ppd_ifdef
-66834
-
-
-@ppd_ifndef
-91808
-
-
-@ppd_elif
-22772
-
-
-@ppd_else
-63649
-
-
-@ppd_endif
-329749
-
-
@ppd_plain_include
-321760
+309224
@ppd_define
-350005
+336367
+
+
+@ppd_if
+164439
+
+
+@ppd_ifdef
+64230
+
+
+@ppd_ifndef
+88231
+
+
+@ppd_elif
+21885
+
+
+@ppd_else
+61169
+
+
+@ppd_endif
+316901
@ppd_undef
-21155
+20331
@ppd_line
-12523
+12530
@ppd_error
-48
+46
@ppd_pragma
-37041
+36665
@ppd_objc_import
@@ -993,7 +997,7 @@
@ppd_include_next
-97
+93
@ppd_warning
@@ -1001,7 +1005,7 @@
@link_target
-644
+619
@xmldtd
@@ -1030,15 +1034,15 @@
compilations
-10590
+10177
id
-10590
+10177
cwd
-12
+11
@@ -1052,7 +1056,7 @@
1
2
-10590
+10177
@@ -1068,7 +1072,7 @@
871
872
-12
+11
@@ -1078,11 +1082,11 @@
compilation_args
-394124
+394355
id
-4637
+4640
num
@@ -1090,7 +1094,7 @@
arg
-20097
+20109
@@ -1109,7 +1113,7 @@
87
88
-3527
+3529
88
@@ -1145,7 +1149,7 @@
86
87
-3529
+3531
87
@@ -1318,12 +1322,12 @@
1
2
-19123
+19134
2
2575
-974
+975
@@ -1339,7 +1343,7 @@
1
2
-19748
+19760
2
@@ -1354,19 +1358,19 @@
compilation_compiling_files
-10590
+10177
id
-10590
+10177
num
-12
+11
file
-5374
+5164
@@ -1380,7 +1384,7 @@
1
2
-10590
+10177
@@ -1396,7 +1400,7 @@
1
2
-10590
+10177
@@ -1412,7 +1416,7 @@
871
872
-12
+11
@@ -1428,7 +1432,7 @@
442
443
-12
+11
@@ -1444,17 +1448,17 @@
1
2
-303
+292
2
3
-5045
+4849
3
14
-24
+23
@@ -1470,7 +1474,7 @@
1
2
-5374
+5164
@@ -1480,23 +1484,23 @@
compilation_time
-42262
+40616
id
-10565
+10154
num
-12
+11
kind
-48
+46
seconds
-12669
+11965
@@ -1510,7 +1514,7 @@
1
2
-10565
+10154
@@ -1526,7 +1530,7 @@
4
5
-10565
+10154
@@ -1542,17 +1546,17 @@
2
3
-24
+58
3
4
-3027
+2792
4
5
-7513
+7302
@@ -1568,7 +1572,7 @@
869
870
-12
+11
@@ -1584,7 +1588,7 @@
4
5
-12
+11
@@ -1598,9 +1602,9 @@
12
-1042
-1043
-12
+1024
+1025
+11
@@ -1616,7 +1620,7 @@
869
870
-48
+46
@@ -1632,7 +1636,7 @@
1
2
-48
+46
@@ -1646,24 +1650,24 @@
12
-6
-7
-12
+7
+8
+11
10
11
-12
+11
-563
-564
-12
+560
+561
+11
-620
-621
-12
+591
+592
+11
@@ -1679,22 +1683,27 @@
1
2
-8036
+7501
2
3
-2711
+2617
3
4
-996
+759
4
-597
-924
+8
+899
+
+
+8
+632
+186
@@ -1710,7 +1719,7 @@
1
2
-12669
+11965
@@ -1726,17 +1735,17 @@
1
2
-10796
+10305
2
3
-1835
+1635
3
4
-36
+23
@@ -1746,23 +1755,23 @@
diagnostic_for
-940552
+903906
diagnostic
-940552
+73204
compilation
-10225
+9826
file_number
-12
+11
file_number_diagnostic_number
-7209
+6929
@@ -1776,7 +1785,17 @@
1
2
-940552
+9861
+
+
+2
+3
+60456
+
+
+118
+840
+2886
@@ -1792,7 +1811,7 @@
1
2
-940552
+73204
@@ -1808,7 +1827,12 @@
1
2
-940552
+73181
+
+
+2
+3
+23
@@ -1824,37 +1848,37 @@
2
3
-24
+23
7
8
-5228
+5024
8
9
-1665
+1600
9
10
-218
+210
247
248
-2066
+1986
263
444
-802
+771
446
594
-218
+210
@@ -1870,7 +1894,7 @@
1
2
-10225
+9826
@@ -1886,37 +1910,37 @@
2
3
-24
+23
7
8
-5228
+5024
8
9
-1665
+1600
9
10
-218
+210
247
248
-2066
+1986
263
444
-802
+771
446
594
-218
+210
@@ -1932,7 +1956,7 @@
6265
6266
-12
+11
@@ -1948,7 +1972,7 @@
841
842
-12
+11
@@ -1964,7 +1988,7 @@
593
594
-12
+11
@@ -1980,47 +2004,47 @@
1
2
-2954
+2839
2
5
-656
+630
5
6
-1057
+1016
7
14
-571
+549
15
16
-60
+58
17
18
-632
+607
18
23
-486
+467
26
40
-583
+560
42
842
-206
+198
@@ -2036,52 +2060,52 @@
4
9
-620
+595
10
11
-1057
+1016
14
27
-571
+549
30
31
-60
+58
34
35
-632
+607
36
45
-486
+467
52
79
-583
+560
84
85
-194
+186
254
255
-2893
+2780
272
842
-109
+105
@@ -2097,7 +2121,7 @@
1
2
-7209
+6929
@@ -2107,19 +2131,19 @@
compilation_finished
-10590
+10177
id
-10590
+10177
cpu_seconds
-8450
+7957
elapsed_seconds
-182
+186
@@ -2133,7 +2157,7 @@
1
2
-10590
+10177
@@ -2149,7 +2173,7 @@
1
2
-10590
+10177
@@ -2165,17 +2189,17 @@
1
2
-7064
+6730
2
3
-984
+712
3
9
-401
+514
@@ -2191,12 +2215,12 @@
1
2
-7951
+7560
2
3
-498
+397
@@ -2212,62 +2236,62 @@
1
2
-36
+35
2
3
-24
+35
-4
-5
-12
+3
+4
+11
+
+
+7
+8
+11
10
11
-12
+11
-12
-13
-12
+21
+22
+11
-18
-19
-12
+27
+28
+11
-23
-24
-12
+53
+54
+11
-57
-58
-12
+120
+121
+11
-127
-128
-12
+125
+126
+11
-131
-132
-12
+233
+234
+11
-237
-238
-12
-
-
-245
-246
-12
+263
+264
+11
@@ -2283,62 +2307,62 @@
1
2
-36
+35
2
3
-24
+35
-4
-5
-12
+3
+4
+11
+
+
+7
+8
+11
10
11
-12
+11
-12
-13
-12
+21
+22
+11
-17
-18
-12
+26
+27
+11
-23
-24
-12
+51
+52
+11
-54
-55
-12
+92
+93
+11
-105
-106
-12
+118
+119
+11
-114
-115
-12
+163
+164
+11
-168
-169
-12
-
-
-222
-223
-12
+215
+216
+11
@@ -2348,23 +2372,23 @@
externalData
-145
+140
id
-72
+70
path
-12
+11
column
-24
+23
value
-145
+140
@@ -2378,7 +2402,7 @@
1
2
-72
+70
@@ -2394,7 +2418,7 @@
2
3
-72
+70
@@ -2410,7 +2434,7 @@
2
3
-72
+70
@@ -2426,7 +2450,7 @@
6
7
-12
+11
@@ -2442,7 +2466,7 @@
2
3
-12
+11
@@ -2458,7 +2482,7 @@
12
13
-12
+11
@@ -2474,7 +2498,7 @@
6
7
-24
+23
@@ -2490,7 +2514,7 @@
1
2
-24
+23
@@ -2506,7 +2530,7 @@
6
7
-24
+23
@@ -2522,7 +2546,7 @@
1
2
-145
+140
@@ -2538,7 +2562,7 @@
1
2
-145
+140
@@ -2554,7 +2578,7 @@
1
2
-145
+140
@@ -2564,41 +2588,41 @@
snapshotDate
-12
+11
snapshotDate
-12
+11
sourceLocationPrefix
-12
+11
prefix
-12
+11
duplicateCode
-185332
+185440
id
-185332
+185440
relativePath
-761
+762
equivClass
-76596
+76641
@@ -2612,7 +2636,7 @@
1
2
-185332
+185440
@@ -2628,7 +2652,7 @@
1
2
-185332
+185440
@@ -2756,27 +2780,27 @@
1
2
-20462
+20474
2
3
-32635
+32655
3
4
-10716
+10723
4
5
-6015
+6019
5
9
-5954
+5957
9
@@ -2797,7 +2821,7 @@
1
2
-75730
+75775
2
@@ -3113,27 +3137,27 @@
tokens
-39620249
+39643528
id
-279171
+279335
offset
-21169
+21181
beginLine
-785436
+785898
beginColumn
-1302
+1303
endLine
-785436
+785898
endColumn
@@ -3151,72 +3175,72 @@
100
101
-8596
+8601
101
102
-27640
+27656
102
105
-22613
+22626
105
108
-24506
+24520
108
111
-13844
+13852
111
114
-23787
+23801
114
116
-22681
+22694
116
124
-23658
+23672
124
132
-21660
+21673
132
154
-21347
+21360
154
186
-21323
+21335
186
202
-23418
+23432
202
416
-20978
+20991
416
3446
-3115
+3117
@@ -3237,47 +3261,47 @@
5
6
-109755
+109819
6
7
-15399
+15408
7
8
-28395
+28412
8
12
-23645
+23659
12
17
-22797
+22811
17
19
-18969
+18980
19
22
-22791
+22805
22
28
-21470
+21483
28
151
-14545
+14553
@@ -3293,42 +3317,42 @@
2
26
-22933
+22946
26
31
-22318
+22331
31
32
-2642
+2643
32
33
-163572
+163668
33
51
-21310
+21323
51
61
-22472
+22485
61
80
-21058
+21071
80
132
-2863
+2865
@@ -3349,47 +3373,47 @@
5
6
-109755
+109819
6
7
-15399
+15408
7
8
-28395
+28412
8
12
-23645
+23659
12
17
-22797
+22811
17
19
-18969
+18980
19
22
-22791
+22805
22
28
-21470
+21483
28
151
-14545
+14553
@@ -3405,42 +3429,42 @@
2
26
-21667
+21679
26
31
-24352
+24366
31
32
-1622
+1623
32
33
-163861
+163957
33
54
-22103
+22116
54
63
-20942
+20954
63
82
-21488
+21501
82
133
-3133
+3135
@@ -3456,17 +3480,17 @@
2
3
-4559
+4562
4
5
-1339
+1340
6
7
-2593
+2594
8
@@ -3476,37 +3500,37 @@
11
12
-1609
+1610
13
23
-1855
+1856
24
62
-1622
+1623
64
130
-1646
+1647
141
250
-1597
+1598
251
982
-1591
+1592
986
45432
-1929
+1930
@@ -3522,17 +3546,17 @@
2
3
-4559
+4562
4
5
-1339
+1340
6
7
-2593
+2594
8
@@ -3542,42 +3566,42 @@
11
12
-1609
+1610
13
23
-1855
+1856
24
62
-1622
+1623
64
130
-1646
+1647
141
246
-1597
+1598
247
964
-1591
+1592
969
32541
-1591
+1592
32546
33841
-337
+338
@@ -3593,47 +3617,47 @@
1
2
-5899
+5902
2
3
-3416
+3418
3
4
-1609
+1610
4
7
-1886
+1887
7
12
-1837
+1838
12
15
-1640
+1641
15
23
-1591
+1592
23
68
-1609
+1610
68
161
-1591
+1592
161
@@ -3654,17 +3678,17 @@
2
3
-4559
+4562
4
5
-1339
+1340
6
7
-2593
+2594
8
@@ -3674,42 +3698,42 @@
11
12
-1609
+1610
13
23
-1855
+1856
24
62
-1622
+1623
64
130
-1646
+1647
141
246
-1597
+1598
247
964
-1591
+1592
969
32541
-1591
+1592
32546
33841
-337
+338
@@ -3725,47 +3749,47 @@
1
2
-5899
+5902
2
3
-3416
+3418
3
4
-1609
+1610
4
7
-1911
+1912
7
12
-1825
+1826
12
15
-1609
+1610
15
24
-1677
+1678
24
74
-1616
+1617
74
169
-1591
+1592
170
@@ -3786,37 +3810,37 @@
1
2
-403939
+404176
2
3
-103118
+103179
3
4
-44274
+44300
4
6
-70568
+70610
6
8
-58457
+58491
8
13
-65861
+65900
13
138
-39217
+39240
@@ -3832,52 +3856,52 @@
1
7
-64810
+64849
7
12
-64350
+64387
12
23
-60079
+60114
23
32
-36740
+36762
32
33
-267244
+267401
33
41
-62672
+62709
41
55
-61271
+61307
55
69
-61541
+61577
69
94
-59243
+59278
94
248
-47482
+47510
@@ -3893,47 +3917,47 @@
1
5
-64429
+64467
5
9
-62611
+62647
9
15
-63016
+63053
15
29
-59870
+59905
29
32
-9205
+9210
32
33
-349310
+349515
33
37
-61959
+61996
37
42
-61400
+61436
42
122
-53633
+53664
@@ -3949,7 +3973,7 @@
1
2
-785436
+785898
@@ -3965,47 +3989,47 @@
1
5
-64294
+64332
5
9
-62611
+62647
9
15
-63133
+63170
15
29
-59735
+59770
29
32
-9254
+9259
32
33
-350355
+350560
33
37
-63588
+63625
37
43
-65241
+65279
43
123
-47224
+47251
@@ -4421,37 +4445,37 @@
1
2
-403939
+404176
2
3
-103118
+103179
3
4
-44274
+44300
4
6
-70568
+70610
6
8
-58457
+58491
8
13
-65861
+65900
13
138
-39217
+39240
@@ -4467,52 +4491,52 @@
1
7
-64810
+64849
7
12
-64350
+64387
12
23
-60079
+60114
23
32
-36740
+36762
32
33
-267244
+267401
33
41
-62672
+62709
41
55
-61271
+61307
55
69
-61541
+61577
69
94
-59243
+59278
94
248
-47482
+47510
@@ -4528,7 +4552,7 @@
1
2
-785436
+785898
@@ -4544,47 +4568,47 @@
1
5
-64429
+64467
5
9
-62611
+62647
9
15
-63016
+63053
15
29
-59870
+59905
29
32
-9205
+9210
32
33
-349310
+349515
33
37
-61959
+61996
37
42
-61400
+61436
42
122
-53633
+53664
@@ -4600,47 +4624,47 @@
1
5
-64294
+64332
5
9
-62611
+62647
9
15
-63133
+63170
15
29
-59735
+59770
29
32
-9254
+9259
32
33
-350355
+350560
33
37
-63588
+63625
37
43
-65241
+65279
43
123
-47224
+47251
@@ -5030,23 +5054,23 @@
external_packages
-133
+128
id
-133
+128
namespace
-12
+11
package_name
-133
+128
version
-133
+128
@@ -5060,7 +5084,7 @@
1
2
-133
+128
@@ -5076,7 +5100,7 @@
1
2
-133
+128
@@ -5092,7 +5116,7 @@
1
2
-133
+128
@@ -5108,7 +5132,7 @@
11
12
-12
+11
@@ -5124,7 +5148,7 @@
11
12
-12
+11
@@ -5140,7 +5164,7 @@
11
12
-12
+11
@@ -5156,7 +5180,7 @@
1
2
-133
+128
@@ -5172,7 +5196,7 @@
1
2
-133
+128
@@ -5188,7 +5212,7 @@
1
2
-133
+128
@@ -5204,7 +5228,7 @@
1
2
-133
+128
@@ -5220,7 +5244,7 @@
1
2
-133
+128
@@ -5236,7 +5260,7 @@
1
2
-133
+128
@@ -5246,15 +5270,15 @@
header_to_external_package
-9094
+8740
fileid
-9094
+8740
package
-133
+128
@@ -5268,7 +5292,7 @@
1
2
-9094
+8740
@@ -5284,42 +5308,42 @@
1
2
-36
+35
2
3
-12
+11
15
16
-24
+23
63
64
-12
+11
71
72
-12
+11
85
86
-12
+11
243
244
-12
+11
251
252
-12
+11
@@ -6537,31 +6561,31 @@
locations_default
-9666045
+9335283
id
-9666045
+9335283
container
-78288
+75237
startLine
-161111
+154834
startColumn
-6018
+5818
endLine
-160929
+154658
endColumn
-8170
+11182
@@ -6575,7 +6599,7 @@
1
2
-9666045
+9335283
@@ -6591,7 +6615,7 @@
1
2
-9666045
+9335283
@@ -6607,7 +6631,7 @@
1
2
-9666045
+9335283
@@ -6623,7 +6647,7 @@
1
2
-9666045
+9335283
@@ -6639,7 +6663,7 @@
1
2
-9666045
+9335283
@@ -6655,62 +6679,62 @@
1
2
-12754
+12257
2
19
-6626
+6368
19
25
-6091
+5818
25
31
-6067
+5830
31
41
-6492
+6204
41
54
-6140
+5854
54
72
-6176
+5877
72
-99
-5969
+98
+5690
-99
-137
-5945
+98
+136
+5702
-137
-221
-5896
+136
+216
+5690
-221
-431
-5872
+216
+417
+5655
-431
+417
19703
-4255
+4288
@@ -6726,62 +6750,62 @@
1
2
-12754
+12257
2
15
-6504
+6251
15
20
-6760
+6496
20
25
-6103
+5842
25
32
-6711
+6461
32
41
-6285
+6052
41
53
-6322
+6064
53
70
-5933
+5690
70
96
-5909
+5678
96
152
-5884
+5667
152
-325
-5872
+322
+5643
-325
+324
8863
-3246
+3131
@@ -6797,62 +6821,62 @@
1
2
-12754
+12257
2
4
-6723
+6461
4
8
-7088
+6812
8
11
-5896
+5632
11
14
-6419
+6099
14
18
-6881
+6601
18
23
-6395
+6134
23
29
-6456
+6227
29
37
-6261
+6029
37
50
-6225
+6052
50
78
-5945
+5737
78
-167
-1240
+168
+1191
@@ -6868,62 +6892,62 @@
1
2
-12754
+12257
2
15
-6480
+6227
15
20
-6796
+6531
20
25
-6030
+5772
25
32
-6711
+6473
32
41
-6298
+6052
41
53
-6310
+6052
53
70
-6018
+5772
70
96
-5921
+5690
96
153
-5933
+5725
153
332
-5884
+5643
332
8863
-3149
+3038
@@ -6939,62 +6963,62 @@
1
2
-12754
+12257
2
14
-6237
+5994
14
19
-6711
+6414
19
23
-6225
+5994
23
27
-6006
+5690
27
32
-6188
+5994
32
38
-6006
+5690
38
45
-6140
+5830
45
54
-5921
+5713
54
65
-5994
+5807
65
-79
-5884
+80
+5842
-79
-184
-4218
+80
+336
+4007
@@ -7010,62 +7034,62 @@
1
2
-31198
+29982
2
3
-19806
+19034
3
4
-18237
+17527
4
5
-10261
+9861
5
6
-9130
+8775
6
7
-6735
+6473
7
9
-14346
+13787
9
13
-13945
+13390
13
30
-12352
+11871
30
112
-12121
+11637
112
-2168
-12085
+2116
+11614
-2193
+2135
6440
-887
+876
@@ -7081,42 +7105,42 @@
1
2
-57278
+55046
2
3
-35502
+34119
3
4
-11343
+10901
4
5
-8985
+8635
5
8
-14796
+14220
8
26
-12365
+11871
26
115
-12097
+11637
115
6440
-8741
+8401
@@ -7132,57 +7156,57 @@
1
2
-32292
+31034
2
3
-19696
+18929
3
4
-20547
+19747
4
5
-9738
+9359
5
6
-9386
+9020
6
7
-6857
+6590
7
9
-14614
+14033
9
13
-14030
+13495
13
27
-12438
+11953
27
60
-12134
+11637
60
153
-9374
+9032
@@ -7198,22 +7222,22 @@
1
2
-119930
+115246
2
3
-18979
+18239
3
7
-13532
+13016
7
184
-8668
+8331
@@ -7229,57 +7253,57 @@
1
2
-32317
+31057
2
3
-19587
+18824
3
4
-19027
+18286
4
5
-10249
+9850
5
6
-9410
+9043
6
7
-6638
+6379
7
9
-14638
+14068
9
13
-13702
+13168
13
28
-12146
+11673
28
-68
-12121
+69
+11813
-68
-190
-11270
+69
+258
+10668
@@ -7295,57 +7319,57 @@
1
2
-948
+934
2
3
-1142
+1074
3
5
-534
+537
5
8
-547
+514
8
16
-474
+467
16
-38
-462
+37
+444
-39
-126
-462
+37
+119
+444
-135
-517
-462
+125
+511
+444
-553
-5009
-462
+518
+4987
+444
-5423
-19562
-462
+5021
+19113
+444
-23702
-199709
-60
+19590
+199744
+70
@@ -7361,47 +7385,47 @@
1
2
-2589
+2500
2
3
-522
+525
3
6
-474
+455
6
15
-486
+467
15
55
-462
+444
59
-223
-462
+226
+444
229
-1196
-462
+1198
+444
-1266
+1275
2344
-462
+444
-2536
+2542
6440
-97
+93
@@ -7417,57 +7441,57 @@
1
2
-984
+969
2
3
-1130
+1063
3
4
-291
+280
4
6
-498
+502
6
10
-486
+455
10
-21
-474
+20
+444
-21
-60
-462
+20
+55
+444
-60
-208
-462
+56
+195
+444
-209
-929
-462
+196
+829
+444
-957
-1965
-462
+851
+1958
+444
-1969
+1958
6490
-303
+327
@@ -7483,55 +7507,55 @@
1
2
-984
+969
2
3
-1130
+1063
3
4
-291
+280
4
6
-498
+502
6
10
-486
+455
10
-21
-474
+20
+444
-21
-60
-462
+20
+55
+444
-60
-208
-462
+56
+195
+444
-209
-931
-462
+205
+828
+444
-958
-1966
-462
+851
+1962
+467
-1969
+1965
6462
303
@@ -7549,42 +7573,42 @@
1
2
-2735
+2640
2
3
-522
+525
3
7
-547
+525
7
13
-474
+455
13
29
-498
+479
29
-51
-474
+54
+455
-51
-90
-462
+54
+109
+444
-91
+110
428
-303
+292
@@ -7600,62 +7624,62 @@
1
2
-30675
+29480
2
3
-20243
+19455
3
4
-17982
+17281
4
5
-10431
+10025
5
6
-8899
+8553
6
7
-6869
+6601
7
9
-14371
+13811
9
13
-13994
+13437
13
30
-12304
+11824
30
111
-12146
+11649
111
-2100
-12073
+2058
+11602
-2100
+2083
6440
-936
+934
@@ -7671,42 +7695,42 @@
1
2
-56767
+54555
2
3
-35660
+34271
3
4
-11343
+10901
4
5
-9106
+8751
5
7
-11951
+11474
7
19
-12340
+11859
19
76
-12085
+11614
76
6440
-11672
+11229
@@ -7722,22 +7746,22 @@
1
2
-118812
+114171
2
3
-19271
+18508
3
7
-12985
+12502
7
46
-9860
+9476
@@ -7753,57 +7777,57 @@
1
2
-31757
+30520
2
3
-20085
+19303
3
4
-20255
+19466
4
5
-9994
+9604
5
6
-9264
+8903
6
7
-6978
+6707
7
9
-14602
+14021
9
13
-14103
+13565
13
27
-12352
+11871
27
60
-12109
+11626
60
153
-9422
+9067
@@ -7819,57 +7843,57 @@
1
2
-31806
+30567
2
3
-19976
+19197
3
4
-18748
+18017
4
5
-10492
+10083
5
6
-9264
+8903
6
7
-6711
+6449
7
9
-14529
+13963
9
13
-14176
+13612
13
29
-12352
+11871
29
71
-12134
+11602
71
-190
-10735
+258
+10387
@@ -7885,52 +7909,47 @@
1
2
-2310
+4428
2
3
-1094
+1180
3
4
-607
+888
4
-7
-753
+6
+1004
-7
-17
-656
+6
+13
+841
-17
-49
-620
+13
+61
+841
-55
-236
-620
+61
+698
+841
-241
-4440
-620
+705
+10765
+841
-4617
-11969
-620
-
-
-12066
-25444
-267
+11014
+25447
+315
@@ -7946,47 +7965,42 @@
1
2
-3258
+5106
2
3
-1033
+1285
3
4
-583
+1028
4
-9
-644
+7
+969
-9
-29
-620
+7
+33
+841
-29
-166
-620
+33
+376
+841
-172
-1667
-620
+387
+2684
+841
-1667
-2846
-620
-
-
-2870
+2688
6440
-170
+268
@@ -8002,52 +8016,47 @@
1
2
-2334
+4463
2
3
-1106
+1180
3
4
-632
+899
4
-7
-741
+6
+1004
-7
-17
-632
+6
+14
+852
-17
-45
-620
+14
+57
+852
-45
-189
-620
+58
+510
+841
-192
-1191
-620
+518
+2401
+841
-1218
-2414
-620
-
-
-2469
+2415
4808
-243
+245
@@ -8063,42 +8072,42 @@
1
2
-3367
+5141
2
3
-1130
+1343
3
4
-595
+1028
4
-11
-668
+7
+969
-11
+7
23
-632
+852
23
-40
-632
+47
+864
-40
-68
-632
+47
+81
+864
-68
-82
-510
+81
+83
+116
@@ -8114,52 +8123,47 @@
1
2
-2334
+4463
2
3
-1106
+1180
3
4
-632
+911
4
-7
-741
+6
+993
-7
-17
-644
+6
+13
+841
-17
-46
-620
+13
+56
+841
-46
-194
-620
+56
+501
+841
-202
-1220
-620
+505
+2374
+841
-1252
-2470
-620
-
-
-2481
+2398
4808
-231
+268
@@ -8169,31 +8173,31 @@
locations_stmt
-2176932
+2178211
id
-2176932
+2178211
container
-3078
+3080
startLine
-296727
+296902
startColumn
-1228
+1229
endLine
-294872
+295045
endColumn
-1493
+1494
@@ -8207,7 +8211,7 @@
1
2
-2176932
+2178211
@@ -8223,7 +8227,7 @@
1
2
-2176932
+2178211
@@ -8239,7 +8243,7 @@
1
2
-2176932
+2178211
@@ -8255,7 +8259,7 @@
1
2
-2176932
+2178211
@@ -8271,7 +8275,7 @@
1
2
-2176932
+2178211
@@ -8484,7 +8488,7 @@
38
48
-251
+252
48
@@ -8525,7 +8529,7 @@
6
15
-251
+252
15
@@ -8672,37 +8676,37 @@
1
2
-113847
+113914
2
3
-78993
+79039
3
4
-31327
+31345
4
6
-24942
+24957
6
16
-22619
+22632
16
129
-22275
+22288
129
222
-2722
+2723
@@ -8718,32 +8722,32 @@
1
2
-175967
+176070
2
3
-51906
+51936
3
5
-22552
+22565
5
13
-22373
+22386
13
125
-22257
+22270
125
176
-1671
+1672
@@ -8759,32 +8763,32 @@
1
2
-130943
+131019
2
3
-73506
+73549
3
4
-30767
+30785
4
7
-26583
+26598
7
16
-22312
+22325
16
45
-12615
+12623
@@ -8800,27 +8804,27 @@
1
2
-192251
+192364
2
3
-47310
+47337
3
4
-21765
+21778
4
9
-23264
+23278
9
30
-12136
+12143
@@ -8836,32 +8840,32 @@
1
2
-156444
+156536
2
3
-62371
+62408
3
4
-20782
+20794
4
9
-24659
+24674
9
30
-22398
+22411
30
73
-10071
+10077
@@ -9257,37 +9261,37 @@
1
2
-116305
+116374
2
3
-72848
+72891
3
4
-31996
+32015
4
6
-25618
+25633
6
16
-23031
+23044
16
126
-22121
+22134
126
228
-2949
+2951
@@ -9303,27 +9307,27 @@
1
2
-175604
+175707
2
3
-52066
+52096
3
5
-22207
+22220
5
14
-23375
+23389
14
173
-21617
+21630
@@ -9339,27 +9343,27 @@
1
2
-194592
+194706
2
3
-42314
+42339
3
4
-20677
+20689
4
8
-22760
+22774
8
32
-14526
+14535
@@ -9375,32 +9379,32 @@
1
2
-133173
+133251
2
3
-67871
+67910
3
4
-32611
+32630
4
7
-26503
+26518
7
16
-22183
+22196
16
46
-12529
+12536
@@ -9416,32 +9420,32 @@
1
2
-156272
+156364
2
3
-62230
+62266
3
4
-21132
+21144
4
9
-23756
+23770
9
33
-22318
+22331
33
73
-9162
+9167
@@ -9821,15 +9825,15 @@
locations_expr
-9745314
+9746017
id
-9745314
+9746017
container
-3559
+3563
startLine
@@ -9859,7 +9863,7 @@
1
2
-9745314
+9746017
@@ -9875,7 +9879,7 @@
1
2
-9745314
+9746017
@@ -9891,7 +9895,7 @@
1
2
-9745314
+9746017
@@ -9907,7 +9911,7 @@
1
2
-9745314
+9746017
@@ -9923,7 +9927,7 @@
1
2
-9745314
+9746017
@@ -9944,67 +9948,67 @@
4
11
-280
+284
11
-36
-267
+37
+272
-36
-112
-270
+37
+114
+269
-112
-207
+114
+210
+269
+
+
+210
+328
268
-207
-326
+328
+498
268
-326
-493
-267
+499
+771
+269
-493
-760
-267
+772
+1223
+268
-760
-1208
-267
+1223
+1918
+268
-1210
-1899
-267
+1918
+3307
+268
-1900
-3251
-267
+3322
+7569
+268
-3258
-7330
-267
+7607
+160039
+268
-7359
-51115
-267
-
-
-51126
+170837
491727
-16
+3
@@ -10020,17 +10024,17 @@
1
3
-300
+296
3
7
-315
+319
7
17
-271
+272
17
@@ -10040,47 +10044,47 @@
35
59
-279
+278
59
88
-270
+271
88
138
-268
+271
138
-206
-267
+207
+271
-206
-336
+207
+339
268
-336
-525
+339
+532
268
-525
-946
-267
+532
+949
+268
-947
-2013
-267
+951
+2069
+268
-2019
+2076
142752
-248
+242
@@ -10096,67 +10100,67 @@
1
3
-308
+307
3
7
-293
+296
7
17
-273
+274
17
-35
-267
+36
+283
-35
-51
-278
+36
+52
+287
-51
-63
-289
+52
+64
+297
-63
-73
-290
+64
+74
+279
-73
-83
+74
+84
+296
+
+
+84
+93
+270
+
+
+93
+106
282
-83
-92
-276
+106
+122
+271
-92
-104
-270
+122
+150
+268
-104
-120
-276
-
-
-120
-145
-270
-
-
-145
+150
330
-187
+153
@@ -10172,17 +10176,17 @@
1
3
-300
+296
3
7
-312
+316
7
17
-274
+275
17
@@ -10192,17 +10196,17 @@
35
59
-278
+277
59
89
-275
+277
89
139
-271
+273
139
@@ -10221,18 +10225,18 @@
547
-971
-267
+973
+269
-972
-2152
-267
+975
+2167
+268
-2152
+2170
143560
-242
+239
@@ -10248,7 +10252,7 @@
1
3
-278
+279
3
@@ -10258,57 +10262,57 @@
7
19
-278
+281
19
-41
-268
+42
+275
-41
+42
60
-279
+272
60
-72
-267
+73
+294
-72
-83
-280
+73
+84
+277
-83
-94
-283
+84
+95
+291
-94
-105
-292
-
-
-105
-119
-278
-
-
-119
-136
+95
+106
279
-136
-162
-269
+106
+120
+282
-162
+120
+138
+275
+
+
+138
+166
+270
+
+
+166
416
-210
+190
@@ -10658,22 +10662,22 @@
16130
-46279
+46280
29
-48722
-86671
+48729
+86681
29
-87010
-224371
+87033
+224378
29
-237409
-710014
+237411
+710028
6
@@ -10734,17 +10738,17 @@
1424
-2061
+2063
29
-2064
-2425
+2066
+2429
29
-2427
-2796
+2429
+2797
22
@@ -11327,18 +11331,18 @@
35
-20304
-51564
+20305
+51570
35
-54934
+54937
117747
35
-117846
-185890
+117863
+185908
35
@@ -11399,12 +11403,12 @@
1983
-2458
+2459
35
-2468
-2637
+2470
+2640
32
@@ -11460,7 +11464,7 @@
2070
-5989
+5990
35
@@ -11607,7 +11611,7 @@
2067
-5989
+5990
35
@@ -11638,23 +11642,23 @@
numlines
-544795
+523603
element_id
-537220
+516324
num_lines
-10273
+9873
num_code
-7939
+7630
num_comment
-4364
+4194
@@ -11668,12 +11672,12 @@
1
2
-529731
+509126
2
7
-7489
+7197
@@ -11689,12 +11693,12 @@
1
2
-529791
+509184
2
7
-7428
+7139
@@ -11710,12 +11714,12 @@
1
2
-537135
+516242
2
3
-85
+81
@@ -11731,393 +11735,41 @@
1
2
-4656
+4475
2
3
-1325
+1273
3
4
-765
+736
4
6
-838
+806
6
12
-814
+782
12
24
-778
+747
24
118
-778
+747
118
7658
-316
-
-
-
-
-
-
-num_lines
-num_code
-
-
-12
-
-
-1
-2
-4729
-
-
-2
-3
-1337
-
-
-3
-4
-802
-
-
-4
-6
-851
-
-
-6
-11
-924
-
-
-11
-18
-790
-
-
-18
-29
-790
-
-
-29
-32
-48
-
-
-
-
-
-
-num_lines
-num_comment
-
-
-12
-
-
-1
-2
-4717
-
-
-2
-3
-1337
-
-
-3
-4
-826
-
-
-4
-6
-838
-
-
-6
-10
-802
-
-
-10
-16
-899
-
-
-16
-26
-814
-
-
-26
-27
-36
-
-
-
-
-
-
-num_code
-element_id
-
-
-12
-
-
-1
-2
-3185
-
-
-2
-3
-1240
-
-
-3
-4
-632
-
-
-4
-6
-607
-
-
-6
-10
-620
-
-
-10
-21
-620
-
-
-21
-67
-595
-
-
-68
-7861
-437
-
-
-
-
-
-
-num_code
-num_lines
-
-
-12
-
-
-1
-2
-3209
-
-
-2
-3
-1264
-
-
-3
-4
-644
-
-
-4
-6
-607
-
-
-6
-10
-693
-
-
-10
-21
-644
-
-
-21
-34
-607
-
-
-34
-42
-267
-
-
-
-
-
-
-num_code
-num_comment
-
-
-12
-
-
-1
-2
-3234
-
-
-2
-3
-1228
-
-
-3
-4
-680
-
-
-4
-6
-620
-
-
-6
-10
-680
-
-
-10
-19
-668
-
-
-19
-28
-595
-
-
-28
-33
-231
-
-
-
-
-
-
-num_comment
-element_id
-
-
-12
-
-
-1
-2
-2030
-
-
-2
-3
-559
-
-
-3
-4
-328
-
-
-4
-7
-340
-
-
-7
-14
-340
-
-
-14
-38
-328
-
-
-39
-280
-328
-
-
-283
-35679
-109
-
-
-
-
-
-
-num_comment
-num_lines
-
-
-12
-
-
-1
-2
-2042
-
-
-2
-3
-571
-
-
-3
-5
-389
-
-
-5
-8
-364
-
-
-8
-18
-364
-
-
-18
-58
-328
-
-
-61
-118
303
@@ -12125,6 +11777,358 @@
+num_lines
+num_code
+
+
+12
+
+
+1
+2
+4545
+
+
+2
+3
+1285
+
+
+3
+4
+771
+
+
+4
+6
+817
+
+
+6
+11
+888
+
+
+11
+18
+759
+
+
+18
+29
+759
+
+
+29
+32
+46
+
+
+
+
+
+
+num_lines
+num_comment
+
+
+12
+
+
+1
+2
+4533
+
+
+2
+3
+1285
+
+
+3
+4
+794
+
+
+4
+6
+806
+
+
+6
+10
+771
+
+
+10
+16
+864
+
+
+16
+26
+782
+
+
+26
+27
+35
+
+
+
+
+
+
+num_code
+element_id
+
+
+12
+
+
+1
+2
+3061
+
+
+2
+3
+1191
+
+
+3
+4
+607
+
+
+4
+6
+584
+
+
+6
+10
+595
+
+
+10
+21
+595
+
+
+21
+67
+572
+
+
+68
+7861
+420
+
+
+
+
+
+
+num_code
+num_lines
+
+
+12
+
+
+1
+2
+3084
+
+
+2
+3
+1215
+
+
+3
+4
+619
+
+
+4
+6
+584
+
+
+6
+10
+666
+
+
+10
+21
+619
+
+
+21
+34
+584
+
+
+34
+42
+257
+
+
+
+
+
+
+num_code
+num_comment
+
+
+12
+
+
+1
+2
+3108
+
+
+2
+3
+1180
+
+
+3
+4
+654
+
+
+4
+6
+595
+
+
+6
+10
+654
+
+
+10
+19
+642
+
+
+19
+28
+572
+
+
+28
+33
+222
+
+
+
+
+
+
+num_comment
+element_id
+
+
+12
+
+
+1
+2
+1951
+
+
+2
+3
+537
+
+
+3
+4
+315
+
+
+4
+7
+327
+
+
+7
+14
+327
+
+
+14
+38
+315
+
+
+39
+280
+315
+
+
+283
+35679
+105
+
+
+
+
+
+
+num_comment
+num_lines
+
+
+12
+
+
+1
+2
+1963
+
+
+2
+3
+549
+
+
+3
+5
+373
+
+
+5
+8
+350
+
+
+8
+18
+350
+
+
+18
+58
+315
+
+
+61
+118
+292
+
+
+
+
+
+
num_comment
num_code
@@ -12134,37 +12138,37 @@
1
2
-2042
+1963
2
3
-571
+549
3
5
-389
+373
5
8
-364
+350
8
17
-340
+327
17
52
-328
+315
56
108
-328
+315
@@ -12174,31 +12178,31 @@
diagnostics
-76172
+73204
id
-76172
+73204
severity
-36
+35
error_tag
-97
+93
error_message
-121
+116
full_error_message
-65959
+63389
location
-145
+140
@@ -12212,7 +12216,7 @@
1
2
-76172
+73204
@@ -12228,7 +12232,7 @@
1
2
-76172
+73204
@@ -12244,7 +12248,7 @@
1
2
-76172
+73204
@@ -12260,7 +12264,7 @@
1
2
-76172
+73204
@@ -12276,7 +12280,7 @@
1
2
-76172
+73204
@@ -12292,17 +12296,17 @@
1
2
-12
+11
2
3
-12
+11
6262
6263
-12
+11
@@ -12318,12 +12322,12 @@
1
2
-24
+23
6
7
-12
+11
@@ -12339,12 +12343,12 @@
1
2
-24
+23
8
9
-12
+11
@@ -12360,17 +12364,17 @@
1
2
-12
+11
2
3
-12
+11
5422
5423
-12
+11
@@ -12386,17 +12390,17 @@
1
2
-12
+11
2
3
-12
+11
9
10
-12
+11
@@ -12412,32 +12416,32 @@
1
2
-24
+23
2
3
-24
+23
5
6
-12
+11
417
418
-12
+11
841
842
-12
+11
4996
4997
-12
+11
@@ -12453,7 +12457,7 @@
1
2
-97
+93
@@ -12469,12 +12473,12 @@
1
2
-85
+81
3
4
-12
+11
@@ -12490,27 +12494,27 @@
1
2
-36
+35
2
3
-24
+23
5
6
-12
+11
417
418
-12
+11
4996
4997
-12
+11
@@ -12526,17 +12530,17 @@
1
2
-60
+58
2
3
-24
+23
5
6
-12
+11
@@ -12552,42 +12556,42 @@
1
2
-24
+23
2
3
-24
+23
5
6
-12
+11
10
11
-12
+11
75
76
-12
+11
332
333
-12
+11
841
842
-12
+11
4996
4997
-12
+11
@@ -12603,7 +12607,7 @@
1
2
-121
+116
@@ -12619,7 +12623,7 @@
1
2
-121
+116
@@ -12635,37 +12639,37 @@
1
2
-36
+35
2
3
-24
+23
5
6
-12
+11
10
11
-12
+11
75
76
-12
+11
332
333
-12
+11
4996
4997
-12
+11
@@ -12681,17 +12685,17 @@
1
2
-85
+81
2
3
-24
+23
5
6
-12
+11
@@ -12707,12 +12711,12 @@
1
2
-65947
+63377
841
842
-12
+11
@@ -12728,7 +12732,7 @@
1
2
-65959
+63389
@@ -12744,7 +12748,7 @@
1
2
-65959
+63389
@@ -12760,7 +12764,7 @@
1
2
-65959
+63389
@@ -12776,7 +12780,7 @@
1
2
-65959
+63389
@@ -12792,12 +12796,12 @@
1
2
-133
+128
6254
6255
-12
+11
@@ -12813,7 +12817,7 @@
1
2
-145
+140
@@ -12829,12 +12833,12 @@
1
2
-133
+128
3
4
-12
+11
@@ -12850,12 +12854,12 @@
1
2
-133
+128
5
6
-12
+11
@@ -12871,12 +12875,12 @@
1
2
-133
+128
5414
5415
-12
+11
@@ -12886,27 +12890,27 @@
files
-66178
+63599
id
-66178
+63599
name
-66178
+63599
simple
-45156
+43397
ext
-109
+105
fromSource
-12
+11
@@ -12920,7 +12924,7 @@
1
2
-66178
+63599
@@ -12936,7 +12940,7 @@
1
2
-66178
+63599
@@ -12952,7 +12956,7 @@
1
2
-66178
+63599
@@ -12968,7 +12972,7 @@
1
2
-66178
+63599
@@ -12984,7 +12988,7 @@
1
2
-66178
+63599
@@ -13000,7 +13004,7 @@
1
2
-66178
+63599
@@ -13016,7 +13020,7 @@
1
2
-66178
+63599
@@ -13032,7 +13036,7 @@
1
2
-66178
+63599
@@ -13048,22 +13052,22 @@
1
2
-34201
+32869
2
3
-6869
+6601
3
7
-3586
+3446
7
42
-498
+479
@@ -13079,22 +13083,22 @@
1
2
-34201
+32869
2
3
-6869
+6601
3
7
-3586
+3446
7
42
-498
+479
@@ -13110,17 +13114,17 @@
1
2
-40329
+38758
2
3
-4194
+4031
3
6
-632
+607
@@ -13136,7 +13140,7 @@
1
2
-45156
+43397
@@ -13152,47 +13156,47 @@
1
2
-12
+11
3
4
-12
+11
15
16
-12
+11
38
39
-12
+11
80
81
-12
+11
114
115
-12
+11
441
442
-12
+11
738
739
-12
+11
4013
4014
-12
+11
@@ -13208,47 +13212,47 @@
1
2
-12
+11
3
4
-12
+11
15
16
-12
+11
38
39
-12
+11
80
81
-12
+11
114
115
-12
+11
441
442
-12
+11
738
739
-12
+11
4013
4014
-12
+11
@@ -13264,47 +13268,47 @@
1
2
-12
+11
3
4
-12
+11
15
16
-12
+11
38
39
-12
+11
75
76
-12
+11
112
113
-12
+11
428
429
-12
+11
658
659
-12
+11
2838
2839
-12
+11
@@ -13320,7 +13324,7 @@
1
2
-109
+105
@@ -13336,7 +13340,7 @@
5443
5444
-12
+11
@@ -13352,7 +13356,7 @@
5443
5444
-12
+11
@@ -13368,7 +13372,7 @@
3714
3715
-12
+11
@@ -13384,7 +13388,7 @@
9
10
-12
+11
@@ -13394,19 +13398,19 @@
folders
-12109
+11637
id
-12109
+11637
name
-12109
+11637
simple
-3477
+3341
@@ -13420,7 +13424,7 @@
1
2
-12109
+11637
@@ -13436,7 +13440,7 @@
1
2
-12109
+11637
@@ -13452,7 +13456,7 @@
1
2
-12109
+11637
@@ -13468,7 +13472,7 @@
1
2
-12109
+11637
@@ -13484,27 +13488,27 @@
1
2
-1872
+1799
2
3
-741
+712
3
4
-498
+479
4
28
-279
+268
28
121
-85
+81
@@ -13520,27 +13524,27 @@
1
2
-1872
+1799
2
3
-741
+712
3
4
-498
+479
4
28
-279
+268
28
121
-85
+81
@@ -13550,15 +13554,15 @@
containerparent
-78263
+75214
parent
-12109
+11637
child
-78263
+75214
@@ -13572,42 +13576,42 @@
1
2
-5507
+5293
2
3
-1568
+1507
3
4
-668
+642
4
6
-1118
+1074
6
10
-899
+864
10
14
-960
+923
14
30
-924
+888
30
153
-462
+444
@@ -13623,7 +13627,7 @@
1
2
-78263
+75214
@@ -13633,23 +13637,23 @@
fileannotations
-5689566
+5467887
id
-5349
+5141
kind
-24
+23
name
-60196
+57851
value
-49326
+47404
@@ -13663,12 +13667,12 @@
1
2
-145
+140
2
3
-5203
+5001
@@ -13684,62 +13688,62 @@
1
96
-401
+385
96
219
-401
+385
221
285
-401
+385
285
444
-401
+385
445
521
-401
+385
526
619
-425
+408
619
708
-401
+385
708
895
-401
+385
896
928
-97
+93
930
931
-1531
+1472
1076
1667
-401
+385
1689
2272
-85
+81
@@ -13755,57 +13759,57 @@
1
108
-401
+385
108
269
-401
+385
269
356
-401
+385
371
630
-401
+385
630
729
-401
+385
733
948
-401
+385
948
1062
-401
+385
1079
1494
-279
+268
1495
1496
-1531
+1472
1496
1865
-401
+385
1963
4058
-328
+315
@@ -13821,12 +13825,12 @@
428
429
-12
+11
440
441
-12
+11
@@ -13842,12 +13846,12 @@
2
3
-12
+11
4949
4950
-12
+11
@@ -13863,12 +13867,12 @@
1
2
-12
+11
4057
4058
-12
+11
@@ -13884,62 +13888,62 @@
1
2
-9872
+9487
2
3
-6492
+6239
3
6
-4997
+4802
6
8
-4936
+4743
8
14
-4851
+4662
14
18
-4425
+4253
18
21
-4620
+4440
21
34
-4863
+4673
34
128
-4814
+4627
129
236
-4595
+4416
236
395
-4559
+4381
395
440
-1167
+1121
@@ -13955,7 +13959,7 @@
1
2
-60196
+57851
@@ -13971,62 +13975,62 @@
1
2
-11039
+10609
2
3
-8729
+8389
3
4
-2808
+2699
4
6
-4352
+4183
6
10
-5471
+5258
10
14
-3586
+3446
14
17
-4644
+4463
17
22
-4644
+4463
22
40
-4559
+4381
40
83
-4583
+4405
83
163
-4535
+4358
164
1933
-1240
+1191
@@ -14042,67 +14046,67 @@
1
2
-7526
+7232
2
5
-2419
+2325
5
8
-3574
+3435
8
21
-3805
+3657
21
23
-2735
+2629
23
25
-4462
+4288
25
40
-3586
+3446
40
195
-3890
+3739
195
207
-3902
+3750
207
273
-4048
+3891
273
327
-3744
+3598
328
407
-4316
+4148
407
441
-1313
+1261
@@ -14118,12 +14122,12 @@
1
2
-49314
+47393
2
3
-12
+11
@@ -14139,67 +14143,67 @@
1
2
-7550
+7256
2
5
-2796
+2687
5
8
-3611
+3470
8
16
-4060
+3902
16
18
-3343
+3213
18
21
-4073
+3914
21
31
-4462
+4288
31
42
-4024
+3867
42
54
-3769
+3622
54
81
-3781
+3633
81
109
-3829
+3680
109
133
-3756
+3610
133
149
-267
+257
@@ -14209,15 +14213,15 @@
inmacroexpansion
-57126596
+57160160
id
-15544516
+15553649
inv
-2493763
+2495229
@@ -14231,42 +14235,42 @@
1
2
-3895411
+3897700
2
3
-2466113
+2467562
3
4
-2018102
+2019287
4
5
-1769908
+1770948
5
6
-1845270
+1846354
6
7
-884925
+885445
7
8
-1535759
+1536661
8
6265
-1129025
+1129688
@@ -14282,52 +14286,52 @@
1
2
-651228
+651611
2
3
-350884
+351091
3
4
-145822
+145908
4
5
-276335
+276498
5
8
-230677
+230812
8
11
-202111
+202230
11
19
-210471
+210594
19
34
-192538
+192651
34
167
-188020
+188130
167
153127
-45673
+45699
@@ -14337,15 +14341,15 @@
affectedbymacroexpansion
-35527526
+35548400
id
-4083475
+4085874
inv
-3282712
+3284641
@@ -14359,42 +14363,42 @@
1
2
-1341531
+1342319
2
3
-719304
+719727
3
4
-682963
+683364
4
6
-320945
+321133
6
10
-313534
+313718
10
24
-308059
+308240
24
64
-309945
+310127
64
9803
-87190
+87242
@@ -14410,72 +14414,72 @@
1
2
-253184
+253333
2
3
-209979
+210102
3
4
-202347
+202466
4
5
-258125
+258276
5
6
-301422
+301599
6
7
-247881
+248027
7
8
-231148
+231284
8
9
-228002
+228136
9
10
-182499
+182606
10
12
-285679
+285847
12
16
-297410
+297584
16
23
-275398
+275560
23
60
-248963
+249109
60
526
-60669
+60704
@@ -14485,23 +14489,23 @@
macroinvocations
-40821746
+39320374
id
-40821746
+39320374
macro_id
-89388
+85906
location
-837108
+804492
kind
-24
+23
@@ -14515,7 +14519,7 @@
1
2
-40821746
+39320374
@@ -14531,7 +14535,7 @@
1
2
-40821746
+39320374
@@ -14547,7 +14551,7 @@
1
2
-40821746
+39320374
@@ -14563,52 +14567,52 @@
1
2
-19271
+18286
2
3
-16948
+16475
3
4
-3744
+3598
4
6
-7927
+7630
6
11
-7659
+7326
11
21
-7161
+6823
21
41
-6760
+6555
41
-104
-6711
+107
+6485
-104
-454
-6711
+107
+450
+6449
-454
-201153
-6492
+453
+201240
+6274
@@ -14624,37 +14628,37 @@
1
2
-47490
+45640
2
3
-11805
+11345
3
4
-5896
+5667
4
6
-7684
+7384
6
13
-7404
+7115
13
65
-6735
+6473
65
3614
-2370
+2278
@@ -14670,12 +14674,12 @@
1
2
-82580
+79362
2
3
-6808
+6543
@@ -14691,42 +14695,42 @@
1
2
-314271
+299713
2
3
-186425
+180341
3
4
-44439
+42999
4
5
-58542
+56378
5
8
-67698
+65247
8
16
-64123
+61777
16
46
-64840
+62536
46
-262302
-36767
+262546
+35498
@@ -14742,12 +14746,12 @@
1
2
-783173
+752659
2
353
-53934
+51833
@@ -14763,7 +14767,7 @@
1
2
-837108
+804492
@@ -14779,12 +14783,12 @@
37566
37567
-12
+11
-3319916
-3319917
-12
+3327545
+3327546
+11
@@ -14800,12 +14804,12 @@
2199
2200
-12
+11
5713
5714
-12
+11
@@ -14821,12 +14825,12 @@
6537
6538
-12
+11
62313
62314
-12
+11
@@ -14836,15 +14840,15 @@
macroparent
-35828140
+34476404
id
-35828140
+34476404
parent_id
-27899913
+26856682
@@ -14858,7 +14862,7 @@
1
2
-35828140
+34476404
@@ -14874,17 +14878,17 @@
1
2
-21481229
+20687700
2
3
-5475383
+5262423
3
88
-943300
+906558
@@ -14894,15 +14898,15 @@
macrolocationbind
-4016679
+4019039
id
-2801506
+2803152
location
-2004363
+2005541
@@ -14916,22 +14920,22 @@
1
2
-2200726
+2202019
2
3
-339324
+339524
3
7
-231800
+231936
7
57
-29655
+29673
@@ -14947,22 +14951,22 @@
1
2
-1602305
+1603246
2
3
-171008
+171108
3
8
-155504
+155595
8
723
-75546
+75590
@@ -14972,19 +14976,19 @@
macro_argument_unexpanded
-104821103
+100941827
invocation
-31137110
+30008413
argument_index
-802
+771
text
-345992
+332512
@@ -14998,22 +15002,22 @@
1
2
-8805677
+8477672
2
3
-12967718
+12498009
3
4
-7093708
+6840222
4
67
-2270005
+2192508
@@ -15029,22 +15033,22 @@
1
2
-8881753
+8550795
2
3
-13154922
+12678071
3
4
-6886443
+6640869
4
67
-2213991
+2138677
@@ -15060,17 +15064,17 @@
51815
51816
-705
+677
52017
-186703
-60
+187640
+58
-770141
-2560947
-36
+773038
+2568177
+35
@@ -15086,17 +15090,17 @@
2
3
-705
+677
13
1002
-60
+58
6617
19688
-36
+35
@@ -15112,62 +15116,62 @@
1
2
-42542
+39868
2
3
-69303
+64826
3
4
-15623
+15435
4
5
-46384
+44296
5
8
-26517
+26980
8
12
-15562
+15447
12
16
-24535
+23275
16
22
-26408
+25414
22
-42
-26262
+41
+25075
-42
-113
-25982
+41
+101
+24970
-113
-51881
-25958
+101
+4363
+24958
-51881
-580841
-911
+4608
+581891
+1963
@@ -15183,17 +15187,17 @@
1
2
-250123
+240378
2
3
-84464
+81173
3
9
-11404
+10960
@@ -15203,19 +15207,19 @@
macro_argument_expanded
-104821103
+100941827
invocation
-31137110
+30008413
argument_index
-802
+771
text
-209526
+201362
@@ -15229,22 +15233,22 @@
1
2
-8805677
+8477672
2
3
-12967718
+12498009
3
4
-7093708
+6840222
4
67
-2270005
+2192508
@@ -15260,22 +15264,22 @@
1
2
-12797439
+12315365
2
3
-11171126
+10770085
3
4
-5962973
+5753415
4
9
-1205570
+1169546
@@ -15291,17 +15295,17 @@
51815
51816
-705
+677
52017
-186703
-60
+187640
+58
-770141
-2560947
-36
+773038
+2568177
+35
@@ -15317,17 +15321,17 @@
1
2
-693
+666
2
75
-60
+58
867
13964
-48
+46
@@ -15343,62 +15347,57 @@
1
2
-25824
+23871
2
3
-42591
+40791
3
4
-6760
+6636
4
-5
-15793
-
-
-5
6
-3367
+18450
6
7
-24560
+23743
7
9
-16705
+16475
9
15
-19149
+17597
15
31
-15854
+15984
31
-87
-15915
+86
+15108
-87
-393
-15745
+86
+365
+15225
-396
-1164221
-7258
+365
+1165688
+7478
@@ -15414,22 +15413,22 @@
1
2
-105669
+101551
2
3
-87990
+84562
3
6
-15781
+15166
6
66
-85
+81
@@ -15439,19 +15438,19 @@
functions
-3566289
+3552236
id
-3566289
+3552236
name
-315900
+303954
kind
-85
+81
@@ -15465,7 +15464,7 @@
1
2
-3566289
+3552236
@@ -15481,7 +15480,7 @@
1
2
-3566289
+3552236
@@ -15497,27 +15496,27 @@
1
2
-214718
+206387
2
3
-31271
+29924
3
5
-28377
+27435
5
13
-23818
+23100
13
-109481
-17714
+118759
+17106
@@ -15533,12 +15532,12 @@
1
2
-314283
+302400
2
3
-1617
+1554
@@ -15554,37 +15553,37 @@
32
33
-12
+11
-477
-478
-12
+478
+479
+11
2735
2736
-12
+11
-5928
-5929
-12
+6052
+6053
+11
-43770
-43771
-12
+43879
+43880
+11
-108976
-108977
-12
+109948
+109949
+11
-131400
-131401
-12
+140883
+140884
+11
@@ -15600,37 +15599,37 @@
11
12
-12
+11
42
43
-12
+11
-228
-229
-12
+229
+230
+11
-1485
-1486
-12
+1494
+1495
+11
2735
2736
-12
+11
-2858
-2859
-12
+2879
+2880
+11
18756
18757
-12
+11
@@ -15640,15 +15639,15 @@
function_entry_point
-1068641
+1030977
id
-1065322
+1027857
entry_point
-1068641
+1030977
@@ -15662,12 +15661,12 @@
1
2
-1062307
+1025030
2
9
-3015
+2827
@@ -15683,7 +15682,7 @@
1
2
-1068641
+1030977
@@ -15693,15 +15692,15 @@
function_return_type
-3577961
+3562951
id
-3566253
+3551686
return_type
-1030658
+1048715
@@ -15715,12 +15714,12 @@
1
2
-3555042
+3540901
2
6
-11210
+10784
@@ -15736,17 +15735,17 @@
1
2
-316545
+309177
2
3
-649625
+677865
3
-81730
-64488
+82133
+61671
@@ -15756,60 +15755,60 @@
purefunctions
-21018
+20805
id
-21018
+20805
function_deleted
-51187
+49379
id
-51187
+49379
function_defaulted
-9556
+9184
id
-9556
+9184
fun_decls
-3669417
+3646017
id
-3669417
+3635139
function
-3485630
+3469555
type_id
-1023363
+1041634
name
-282550
+271389
location
-876623
+843835
@@ -15823,7 +15822,7 @@
1
2
-3669417
+3635139
@@ -15839,81 +15838,86 @@
1
2
-3669417
-
-
-
-
-
-
-id
-name
-
-
-12
-
-
-1
-2
-3669417
-
-
-
-
-
-
-id
-location
-
-
-12
-
-
-1
-2
-3669417
-
-
-
-
-
-
-function
-id
-
-
-12
-
-
-1
-2
-3344385
-
-
-2
-9
-141244
-
-
-
-
-
-
-function
-type_id
-
-
-12
-
-
-1
-2
-3467246
+3624611
2
6
-18383
+10527
+
+
+
+
+
+
+id
+name
+
+
+12
+
+
+1
+2
+3635139
+
+
+
+
+
+
+id
+location
+
+
+12
+
+
+1
+2
+3635139
+
+
+
+
+
+
+function
+id
+
+
+12
+
+
+1
+2
+3333988
+
+
+2
+9
+135566
+
+
+
+
+
+
+function
+type_id
+
+
+12
+
+
+1
+2
+3451876
+
+
+2
+6
+17678
@@ -15929,7 +15933,7 @@
1
2
-3485630
+3469555
@@ -15945,12 +15949,12 @@
1
2
-3390527
+3378332
2
9
-95103
+91222
@@ -15966,17 +15970,17 @@
1
2
-302538
+295716
2
3
-648142
+676381
3
-87014
-72683
+87408
+69535
@@ -15992,17 +15996,17 @@
1
2
-315572
+308231
2
3
-644154
+672572
3
-80906
-63637
+81309
+60830
@@ -16018,12 +16022,12 @@
1
2
-953671
+974669
2
-7429
-69692
+7459
+66965
@@ -16039,17 +16043,17 @@
1
2
-917755
+940538
2
5
-82057
+78474
5
-22296
-23550
+22349
+22621
@@ -16065,32 +16069,32 @@
1
2
-169914
+162826
2
3
-32560
+31420
3
4
-18882
+17994
4
7
-25642
+24841
7
19
-21265
+20518
19
-109747
-14286
+119022
+13787
@@ -16106,32 +16110,32 @@
1
2
-181720
+174172
2
3
-31417
+30099
3
4
-16669
+16171
4
7
-22614
+21862
7
-26
-21386
+25
+20366
-26
-109465
-8741
+25
+118743
+8716
@@ -16147,17 +16151,17 @@
1
2
-246621
+236860
2
5
-23186
+22271
5
-56195
-12742
+60936
+12257
@@ -16173,27 +16177,27 @@
1
2
-181051
+173599
2
3
-47831
+46213
3
4
-18043
+17340
4
8
-22699
+21815
8
-8793
-12924
+8910
+12420
@@ -16209,27 +16213,27 @@
1
2
-590438
+567012
2
3
-147883
+142319
3
5
-69801
+67350
5
-179
-65777
+141
+63307
-179
-3027
-2723
+142
+3039
+3844
@@ -16245,22 +16249,22 @@
1
2
-603399
+579468
2
3
-164734
+158514
3
9
-69765
+67747
9
-3027
-38724
+3039
+38103
@@ -16276,17 +16280,17 @@
1
2
-774589
+744445
2
4
-68208
+65890
4
-1514
-33824
+1520
+33500
@@ -16302,12 +16306,12 @@
1
2
-848975
+817018
2
134
-27648
+26816
@@ -16317,22 +16321,22 @@
fun_def
-1297573
+1250592
id
-1297573
+1250592
fun_specialized
-6505
+6439
id
-6505
+6439
@@ -16350,11 +16354,11 @@
fun_decl_specifiers
-512313
+512614
id
-274687
+274848
name
@@ -16372,17 +16376,17 @@
1
2
-72048
+72090
2
3
-167651
+167750
3
4
-34987
+35007
@@ -16569,26 +16573,26 @@
fun_decl_empty_throws
-1416969
+1471304
fun_decl
-1416969
+1471304
fun_decl_noexcept
-16571
+15937
fun_decl
-15696
+15096
constant
-16438
+15809
@@ -16602,12 +16606,12 @@
1
2
-14821
+14255
2
3
-875
+841
@@ -16623,12 +16627,12 @@
1
2
-16304
+15680
2
3
-133
+128
@@ -16638,26 +16642,26 @@
fun_decl_empty_noexcept
-364619
+350681
fun_decl
-364619
+350681
fun_decl_typedef_type
-194
+186
fun_decl
-194
+186
typedeftype_id
-97
+93
@@ -16671,7 +16675,7 @@
1
2
-194
+186
@@ -16687,7 +16691,7 @@
2
3
-97
+93
@@ -16697,19 +16701,19 @@
param_decl_bind
-4832591
+4766932
id
-4832591
+4766932
index
-389
+373
fun_decl
-3159917
+3153051
@@ -16723,7 +16727,7 @@
1
2
-4832591
+4766932
@@ -16739,7 +16743,7 @@
1
2
-4832591
+4766932
@@ -16755,57 +16759,57 @@
2
3
-158
+151
4
10
-24
+23
15
44
-24
+23
115
191
-24
+23
263
337
-24
+23
422
615
-24
+23
-882
-1212
-24
+885
+1215
+23
-1692
-2653
-24
+1695
+2656
+23
-4904
-13237
-24
+4907
+13252
+23
-34857
-76103
-24
+34902
+76574
+23
-259895
-259896
-12
+269844
+269845
+11
@@ -16821,57 +16825,57 @@
2
3
-158
+151
4
10
-24
+23
15
44
-24
+23
115
191
-24
+23
263
337
-24
+23
422
615
-24
+23
-882
-1212
-24
+885
+1215
+23
-1692
-2653
-24
+1695
+2656
+23
-4904
-13237
-24
+4907
+13252
+23
-34857
-76103
-24
+34902
+76574
+23
-259895
-259896
-12
+269844
+269845
+11
@@ -16887,22 +16891,22 @@
1
2
-2234636
+2258317
2
3
-501474
+486913
3
4
-262877
+252985
4
33
-160929
+154834
@@ -16918,22 +16922,22 @@
1
2
-2234636
+2258317
2
3
-501474
+486913
3
4
-262877
+252985
4
33
-160929
+154834
@@ -16943,27 +16947,27 @@
var_decls
-5603886
+5518014
id
-5603886
+5495954
variable
-5342029
+5266618
type_id
-2038350
+2068405
name
-139615
+134175
location
-1360152
+1305755
@@ -16977,7 +16981,7 @@
1
2
-5603886
+5495954
@@ -16993,7 +16997,12 @@
1
2
-5603886
+5474337
+
+
+2
+7
+21616
@@ -17009,7 +17018,7 @@
1
2
-5603886
+5495954
@@ -17025,7 +17034,12 @@
1
2
-5603886
+5495907
+
+
+2
+3
+46
@@ -17041,12 +17055,12 @@
1
2
-5147701
+5079674
2
9
-194328
+186943
@@ -17062,12 +17076,12 @@
1
2
-5292362
+5218605
2
7
-49667
+48012
@@ -17083,12 +17097,12 @@
1
2
-5323281
+5248600
2
3
-18748
+18017
@@ -17104,12 +17118,12 @@
1
2
-5226682
+5155963
2
9
-115347
+110654
@@ -17125,22 +17139,22 @@
1
2
-1580670
+1627085
2
3
-244992
+236522
3
-9
-154667
+10
+155430
-9
-5708
-58020
+10
+5721
+49367
@@ -17156,22 +17170,22 @@
1
2
-1607114
+1652441
2
3
-235059
+226741
3
-11
-155141
+13
+156353
-11
-5254
-41034
+13
+5267
+32869
@@ -17187,17 +17201,17 @@
1
2
-1850964
+1888086
2
5
-162473
+156341
5
770
-24912
+23977
@@ -17213,17 +17227,17 @@
1
2
-1771740
+1811867
2
4
-165196
+159052
4
3604
-101413
+97485
@@ -17239,42 +17253,42 @@
1
2
-58956
+56530
2
3
-20851
+20039
3
4
-12304
+11801
4
6
-12644
+12268
6
10
-11514
+11088
10
22
-10675
+10270
22
255
-10492
+10083
263
-149390
-2176
+159500
+2091
@@ -17290,37 +17304,37 @@
1
2
-62299
+59814
2
3
-20183
+19314
3
4
-13386
+12841
4
6
-11939
+11532
6
11
-11416
+11077
11
28
-10517
+10095
28
-148417
-9872
+158536
+9499
@@ -17336,32 +17350,32 @@
1
2
-84975
+81606
2
3
-18164
+17445
3
4
-9702
+9347
4
7
-11684
+11275
7
28
-10577
+10165
28
-111442
-4510
+120730
+4335
@@ -17377,32 +17391,32 @@
1
2
-80342
+77212
2
3
-21277
+20448
3
4
-7781
+7478
4
7
-12425
+11941
7
21
-10565
+10154
21
9993
-7222
+6940
@@ -17418,22 +17432,22 @@
1
2
-1010196
+967927
2
3
-144296
+139422
3
6
-124101
+119546
6
-114124
-81558
+123459
+78860
@@ -17449,22 +17463,22 @@
1
2
-1062416
+1018112
2
3
-107517
+103970
3
6
-111918
+107943
6
-113868
-78300
+123212
+75728
@@ -17480,17 +17494,17 @@
1
2
-1167781
+1119863
2
3
-93060
+89797
3
-104373
-99310
+113652
+96095
@@ -17506,12 +17520,12 @@
1
2
-1350207
+1296197
2
52
-9945
+9558
@@ -17521,11 +17535,11 @@
var_def
-2574671
+2477557
id
-2574671
+2477557
@@ -17595,19 +17609,19 @@
type_decls
-1469408
+1413769
id
-1469408
+1413769
type_id
-1434233
+1379965
location
-1203770
+1156868
@@ -17621,7 +17635,7 @@
1
2
-1469408
+1413769
@@ -17637,7 +17651,7 @@
1
2
-1469408
+1413769
@@ -17653,12 +17667,12 @@
1
2
-1408664
+1355392
2
24
-25569
+24572
@@ -17674,12 +17688,12 @@
1
2
-1410099
+1356771
2
24
-24134
+23194
@@ -17695,12 +17709,12 @@
1
2
-1142771
+1098234
2
469
-60998
+58633
@@ -17716,12 +17730,12 @@
1
2
-1144413
+1099812
2
469
-59357
+57056
@@ -17731,45 +17745,45 @@
type_def
-1035826
+995467
id
-1035826
+995467
type_decl_top
-297602
+286006
type_decl
-297602
+286006
namespace_decls
-151652
+145743
id
-151652
+145743
namespace_id
-8486
+8155
location
-135445
+130167
bodylocation
-135809
+130518
@@ -17783,7 +17797,7 @@
1
2
-151652
+145743
@@ -17799,7 +17813,7 @@
1
2
-151652
+145743
@@ -17815,7 +17829,7 @@
1
2
-151652
+145743
@@ -17831,42 +17845,42 @@
1
2
-4012
+3855
2
3
-1191
+1145
3
4
-449
+432
4
7
-717
+689
7
13
-656
+630
13
27
-656
+630
28
163
-644
+619
172
3743
-158
+151
@@ -17882,42 +17896,42 @@
1
2
-4012
+3855
2
3
-1191
+1145
3
4
-449
+432
4
7
-717
+689
7
13
-656
+630
13
27
-656
+630
28
163
-644
+619
172
3743
-158
+151
@@ -17933,42 +17947,42 @@
1
2
-4012
+3855
2
3
-1191
+1145
3
4
-449
+432
4
7
-717
+689
7
13
-656
+630
13
27
-656
+630
28
163
-644
+619
172
3742
-158
+151
@@ -17984,12 +17998,12 @@
1
2
-126155
+121240
2
8
-9289
+8927
@@ -18005,12 +18019,12 @@
1
2
-126155
+121240
2
8
-9289
+8927
@@ -18026,12 +18040,12 @@
1
2
-134618
+129373
2
3
-826
+794
@@ -18047,12 +18061,12 @@
1
2
-126921
+121976
2
11
-8887
+8541
@@ -18068,12 +18082,12 @@
1
2
-126921
+121976
2
9
-8887
+8541
@@ -18089,12 +18103,12 @@
1
2
-135372
+130097
2
5
-437
+420
@@ -18104,19 +18118,19 @@
usings
-320654
+308161
id
-320654
+308161
element_id
-49010
+47101
location
-26468
+25437
@@ -18130,7 +18144,7 @@
1
2
-320654
+308161
@@ -18146,7 +18160,7 @@
1
2
-320654
+308161
@@ -18162,17 +18176,17 @@
1
2
-41241
+39634
2
4
-4170
+4007
4
127
-3598
+3458
@@ -18188,17 +18202,17 @@
1
2
-41241
+39634
2
4
-4170
+4007
4
127
-3598
+3458
@@ -18214,22 +18228,22 @@
1
2
-20037
+19256
2
3
-2541
+2442
3
21
-2054
+1974
21
347
-1835
+1764
@@ -18245,22 +18259,22 @@
1
2
-20037
+19256
2
3
-2541
+2442
3
21
-2054
+1974
21
347
-1835
+1764
@@ -18270,15 +18284,15 @@
using_container
-485267
+466360
parent
-8668
+8331
child
-301079
+289348
@@ -18292,47 +18306,47 @@
1
2
-3380
+3248
2
3
-559
+537
3
6
-668
+642
6
15
-656
+630
16
31
-705
+677
31
143
-291
+280
178
179
-1398
+1343
179
182
-765
+736
182
501
-243
+233
@@ -18348,22 +18362,22 @@
1
2
-217514
+209039
2
3
-55782
+53609
3
6
-22663
+21780
6
47
-5118
+4919
@@ -18373,23 +18387,23 @@
static_asserts
-132361
+131017
id
-132361
+131017
condition
-132361
+131017
message
-30094
+29789
location
-16939
+16766
@@ -18403,7 +18417,7 @@
1
2
-132361
+131017
@@ -18419,7 +18433,7 @@
1
2
-132361
+131017
@@ -18435,7 +18449,7 @@
1
2
-132361
+131017
@@ -18451,7 +18465,7 @@
1
2
-132361
+131017
@@ -18467,7 +18481,7 @@
1
2
-132361
+131017
@@ -18483,7 +18497,7 @@
1
2
-132361
+131017
@@ -18499,32 +18513,32 @@
1
2
-22396
+22168
2
3
-428
+424
3
4
-2867
+2837
4
11
-1463
+1448
12
17
-2379
+2355
17
513
-560
+554
@@ -18540,32 +18554,32 @@
1
2
-22396
+22168
2
3
-428
+424
3
4
-2867
+2837
4
11
-1463
+1448
12
17
-2379
+2355
17
513
-560
+554
@@ -18581,12 +18595,12 @@
1
2
-27913
+27629
2
33
-2181
+2159
@@ -18602,32 +18616,32 @@
1
2
-2939
+2909
2
3
-2801
+2772
3
4
-1344
+1330
5
6
-3776
+3738
6
7
-177
+176
14
15
-2063
+2042
16
@@ -18637,12 +18651,12 @@
17
18
-3447
+3412
19
52
-349
+345
@@ -18658,32 +18672,32 @@
1
2
-2939
+2909
2
3
-2801
+2772
3
4
-1344
+1330
5
6
-3776
+3738
6
7
-177
+176
14
15
-2063
+2042
16
@@ -18693,12 +18707,12 @@
17
18
-3447
+3412
19
52
-349
+345
@@ -18714,22 +18728,22 @@
1
2
-4475
+4429
2
3
-6063
+6002
3
4
-6208
+6145
4
7
-191
+189
@@ -18739,23 +18753,23 @@
params
-4805368
+4749557
id
-4781221
+4724692
function
-3119223
+3117564
index
-389
+373
type_id
-1865919
+1906163
@@ -18769,12 +18783,12 @@
1
2
-4780480
+4723979
2
69
-741
+712
@@ -18790,7 +18804,7 @@
1
2
-4781221
+4724692
@@ -18806,12 +18820,12 @@
1
2
-4759555
+4702211
2
7
-21666
+22481
@@ -18827,22 +18841,22 @@
1
2
-2207948
+2232786
2
3
-483079
+472740
3
4
-264652
+254691
4
33
-163543
+157346
@@ -18858,22 +18872,22 @@
1
2
-2207948
+2232786
2
3
-483079
+472740
3
4
-264652
+254691
4
33
-163543
+157346
@@ -18889,22 +18903,22 @@
1
2
-2315246
+2335471
2
3
-493170
+480884
3
5
-271011
+262941
5
20
-39794
+38267
@@ -18920,57 +18934,57 @@
2
3
-158
+151
4
10
-24
+23
15
44
-24
+23
115
191
-24
+23
263
337
-24
+23
422
615
-24
+23
-880
-1206
-24
+883
+1209
+23
-1656
-2593
-24
+1659
+2596
+23
-4877
-13452
-24
+4880
+13467
+23
-35218
-74951
-24
+35263
+75722
+23
-256377
-256378
-12
+266636
+266637
+11
@@ -18986,57 +19000,57 @@
2
3
-158
+151
4
10
-24
+23
15
44
-24
+23
115
191
-24
+23
263
337
-24
+23
422
615
-24
+23
-880
-1206
-24
+883
+1209
+23
-1656
-2593
-24
+1659
+2596
+23
-4877
-13452
-24
+4880
+13467
+23
-35218
-74951
-24
+35263
+75722
+23
-256548
-256549
-12
+266807
+266808
+11
@@ -19052,57 +19066,57 @@
1
2
-158
+151
3
7
-24
+23
8
20
-24
+23
36
40
-24
+23
42
45
-24
+23
61
105
-24
+23
173
257
-24
+23
406
661
-24
+23
1178
2710
-24
+23
-7491
-16697
-24
+7492
+16795
+23
-135721
-135722
-12
+145286
+145287
+11
@@ -19118,22 +19132,22 @@
1
2
-1495062
+1547921
2
3
-199653
+193171
3
-13
-141706
+18
+144002
-13
-5275
-29496
+18
+5288
+21067
@@ -19149,22 +19163,22 @@
1
2
-1517178
+1569164
2
3
-187288
+181276
3
-17
-140916
+25
+143324
-17
-4798
-20535
+25
+4811
+12397
@@ -19180,12 +19194,12 @@
1
2
-1751155
+1795906
2
33
-114763
+110257
@@ -19195,15 +19209,15 @@
overrides
-161131
+159494
new
-126172
+124890
old
-15666
+15507
@@ -19217,12 +19231,12 @@
1
2
-91220
+90293
2
3
-34945
+34590
3
@@ -19243,37 +19257,37 @@
1
2
-8232
+8148
2
3
-2043
+2022
3
4
-883
+874
4
5
-1278
+1265
5
10
-1245
+1233
10
43
-1179
+1167
44
218
-804
+795
@@ -19283,19 +19297,19 @@
membervariables
-329676
+325582
id
-329676
+321201
type_id
-145110
+139772
name
-59077
+56776
@@ -19309,7 +19323,12 @@
1
2
-329676
+316912
+
+
+2
+7
+4288
@@ -19325,7 +19344,7 @@
1
2
-329676
+321201
@@ -19341,22 +19360,22 @@
1
2
-118447
+113949
2
3
-13362
+12934
3
11
-10991
+10656
11
-1212
-2310
+1715
+2231
@@ -19372,17 +19391,17 @@
1
2
-125937
+121310
2
3
-9884
+9511
3
266
-9289
+8950
@@ -19398,32 +19417,32 @@
1
2
-31222
+29971
2
3
-9058
+8705
3
4
-5945
+5725
4
6
-4498
+4311
6
15
-4522
+4358
15
-1317
-3829
+1953
+3704
@@ -19439,32 +19458,32 @@
1
2
-38262
+36736
2
3
-7526
+7232
3
4
-4036
+3891
4
7
-4766
+4592
7
-319
-4437
+291
+4264
-323
+318
605
-48
+58
@@ -19478,7 +19497,7 @@
id
-301143
+301136
type_id
@@ -19500,7 +19519,12 @@
1
2
-301143
+301129
+
+
+2
+3
+7
@@ -19516,7 +19540,7 @@
1
2
-301143
+301136
@@ -19640,19 +19664,19 @@
localvariables
-527192
+521836
id
-527113
+521758
type_id
-48002
+47599
name
-76218
+75444
@@ -19666,12 +19690,12 @@
1
2
-527034
+521680
2
3
-79
+78
@@ -19687,7 +19711,7 @@
1
2
-527113
+521758
@@ -19703,37 +19727,32 @@
1
2
-26614
+26487
2
3
-6887
+6772
3
4
-2893
+2864
4
6
-4132
+4077
6
13
-3862
+3823
13
-3187
-3605
-
-
-4907
4908
-6
+3575
@@ -19749,22 +19768,22 @@
1
2
-35934
+35706
2
3
-5009
+4919
3
5
-4336
+4279
5
1158
-2722
+2694
@@ -19780,32 +19799,32 @@
1
2
-43527
+43085
2
3
-13096
+12963
3
4
-5141
+5088
4
7
-6821
+6752
7
31
-5767
+5708
31
6112
-1865
+1846
@@ -19821,17 +19840,17 @@
1
2
-64565
+63910
2
3
-6472
+6406
3
819
-5180
+5127
@@ -19841,11 +19860,11 @@
autoderivation
-19759
+19559
var
-19759
+19559
derivation_type
@@ -19863,7 +19882,7 @@
1
2
-19759
+19559
@@ -19909,31 +19928,31 @@
enumconstants
-94489
+94097
id
-94489
+94097
parent
-7540
+8031
index
-7678
+7600
type_id
-7395
+7887
name
-73852
+73102
location
-76858
+76077
@@ -19947,7 +19966,7 @@
1
2
-94489
+94097
@@ -19963,7 +19982,7 @@
1
2
-94489
+94097
@@ -19979,7 +19998,7 @@
1
2
-94489
+94097
@@ -19995,7 +20014,7 @@
1
2
-94489
+94097
@@ -20011,7 +20030,7 @@
1
2
-94489
+94097
@@ -20027,47 +20046,47 @@
1
2
-1041
+1598
2
3
-830
+822
3
4
-2484
+2459
4
5
-586
+580
5
6
-586
+580
6
-8
-540
+9
+730
-8
-13
-659
+9
+16
+613
-13
-46
-573
+16
+392
+606
-46
+426
1166
-237
+39
@@ -20083,47 +20102,42 @@
1
2
-1041
+1598
2
3
-843
+835
3
4
-2530
+2505
4
5
-580
+574
5
6
-593
+587
6
-8
-527
+9
+704
-8
-13
-632
+9
+17
+626
-13
-49
-566
-
-
-50
+17
1166
-224
+600
@@ -20139,12 +20153,12 @@
1
2
-6788
+7287
2
3
-751
+743
@@ -20160,47 +20174,42 @@
1
2
-1041
+1598
2
3
-843
+835
3
4
-2530
+2505
4
5
-580
+574
5
6
-593
+587
6
-8
-527
+9
+704
-8
-13
-632
+9
+17
+626
-13
-49
-566
-
-
-50
+17
1166
-224
+600
@@ -20216,47 +20225,47 @@
1
2
-1080
+1637
2
3
-843
+835
3
4
-2484
+2459
4
5
-586
+580
5
6
-566
+561
6
-8
-514
+9
+704
-8
-13
-659
+9
+16
+613
-13
-46
-566
+16
+427
+606
-46
+466
1166
-237
+32
@@ -20272,47 +20281,47 @@
1
2
-2643
+2616
2
3
-942
+932
3
4
-935
+926
4
7
-580
+574
7
10
-698
+691
10
11
-355
+352
11
12
-560
+554
12
30
-606
+600
30
-1166
-355
+1253
+352
@@ -20328,47 +20337,47 @@
1
2
-2643
+2616
2
3
-942
+932
3
4
-935
+926
4
7
-580
+574
7
9
-685
+678
9
11
-369
+365
11
12
-560
+554
12
29
-606
+600
29
-1145
-355
+1232
+352
@@ -20384,47 +20393,47 @@
1
2
-2643
+2616
2
3
-942
+932
3
4
-935
+926
4
7
-580
+574
7
9
-685
+678
9
11
-369
+365
11
12
-560
+554
12
28
-606
+600
28
-1123
-355
+1210
+352
@@ -20440,47 +20449,47 @@
1
2
-2643
+2616
2
3
-942
+932
3
4
-935
+926
4
7
-580
+574
7
10
-698
+691
10
11
-355
+352
11
12
-560
+554
12
28
-606
+600
28
674
-355
+352
@@ -20496,47 +20505,47 @@
1
2
-2643
+2616
2
3
-942
+932
3
4
-935
+926
4
7
-580
+574
7
10
-698
+691
10
11
-355
+352
11
12
-560
+554
12
28
-606
+600
28
774
-355
+352
@@ -20552,47 +20561,47 @@
1
2
-1041
+1598
2
3
-810
+802
3
4
-2458
+2433
4
5
-566
+561
5
6
-566
+561
6
-8
-520
+9
+704
-8
-13
-639
+9
+16
+600
-13
-46
-560
+16
+470
+593
-46
+479
1166
-230
+32
@@ -20608,7 +20617,7 @@
1
2
-7388
+7881
137
@@ -20629,47 +20638,42 @@
1
2
-1041
+1598
2
3
-823
+815
3
4
-2504
+2479
4
5
-560
+554
5
6
-573
+567
6
-8
-507
+9
+678
-8
-13
-612
+9
+17
+613
-13
-51
-560
-
-
-52
+17
1166
-210
+580
@@ -20685,47 +20689,42 @@
1
2
-1041
+1598
2
3
-823
+815
3
4
-2504
+2479
4
5
-560
+554
5
6
-573
+567
6
-8
-507
+9
+678
-8
-13
-612
+9
+17
+613
-13
-51
-560
-
-
-52
+17
1166
-210
+580
@@ -20741,47 +20740,47 @@
1
2
-1080
+1637
2
3
-823
+815
3
4
-2458
+2433
4
5
-566
+561
5
6
-547
+541
6
-8
-494
+9
+678
-8
-13
-639
+9
+16
+600
-13
-47
-560
+16
+470
+593
-48
+621
1166
-224
+26
@@ -20797,17 +20796,17 @@
1
2
-66398
+65723
2
3
-6927
+6856
3
239
-527
+521
@@ -20823,17 +20822,17 @@
1
2
-67050
+66369
2
3
-6274
+6210
3
239
-527
+521
@@ -20849,12 +20848,12 @@
1
2
-68764
+68065
2
13
-5088
+5036
@@ -20870,17 +20869,17 @@
1
2
-61942
+61313
2
3
-11382
+11267
3
239
-527
+521
@@ -20896,12 +20895,12 @@
1
2
-71348
+70623
2
23
-2504
+2479
@@ -20917,12 +20916,12 @@
1
2
-71532
+70773
2
229
-5325
+5304
@@ -20938,12 +20937,12 @@
1
2
-71644
+70884
2
229
-5213
+5193
@@ -20959,12 +20958,12 @@
1
2
-73213
+72469
2
17
-3644
+3607
@@ -20980,17 +20979,17 @@
1
2
-66536
+65828
2
3
-10090
+9994
3
229
-230
+254
@@ -21006,12 +21005,12 @@
1
2
-76686
+75907
2
27
-171
+169
@@ -21021,31 +21020,31 @@
builtintypes
-559
+537
id
-559
+537
name
-559
+537
kind
-559
+537
size
-85
+81
sign
-36
+35
alignment
-60
+58
@@ -21059,7 +21058,7 @@
1
2
-559
+537
@@ -21075,7 +21074,7 @@
1
2
-559
+537
@@ -21091,7 +21090,7 @@
1
2
-559
+537
@@ -21107,7 +21106,7 @@
1
2
-559
+537
@@ -21123,7 +21122,7 @@
1
2
-559
+537
@@ -21139,7 +21138,7 @@
1
2
-559
+537
@@ -21155,7 +21154,7 @@
1
2
-559
+537
@@ -21171,7 +21170,7 @@
1
2
-559
+537
@@ -21187,7 +21186,7 @@
1
2
-559
+537
@@ -21203,7 +21202,7 @@
1
2
-559
+537
@@ -21219,7 +21218,7 @@
1
2
-559
+537
@@ -21235,7 +21234,7 @@
1
2
-559
+537
@@ -21251,7 +21250,7 @@
1
2
-559
+537
@@ -21267,7 +21266,7 @@
1
2
-559
+537
@@ -21283,7 +21282,7 @@
1
2
-559
+537
@@ -21299,37 +21298,37 @@
1
2
-12
+11
2
3
-12
+11
4
5
-12
+11
6
7
-12
+11
9
10
-12
+11
11
12
-12
+11
13
14
-12
+11
@@ -21345,37 +21344,37 @@
1
2
-12
+11
2
3
-12
+11
4
5
-12
+11
6
7
-12
+11
9
10
-12
+11
11
12
-12
+11
13
14
-12
+11
@@ -21391,37 +21390,37 @@
1
2
-12
+11
2
3
-12
+11
4
5
-12
+11
6
7
-12
+11
9
10
-12
+11
11
12
-12
+11
13
14
-12
+11
@@ -21437,12 +21436,12 @@
1
2
-24
+23
3
4
-60
+58
@@ -21458,12 +21457,12 @@
1
2
-60
+58
2
3
-24
+23
@@ -21479,17 +21478,17 @@
6
7
-12
+11
12
13
-12
+11
28
29
-12
+11
@@ -21505,17 +21504,17 @@
6
7
-12
+11
12
13
-12
+11
28
29
-12
+11
@@ -21531,17 +21530,17 @@
6
7
-12
+11
12
13
-12
+11
28
29
-12
+11
@@ -21557,12 +21556,12 @@
5
6
-24
+23
7
8
-12
+11
@@ -21578,7 +21577,7 @@
5
6
-36
+35
@@ -21594,27 +21593,27 @@
4
5
-12
+11
7
8
-12
+11
10
11
-12
+11
12
13
-12
+11
13
14
-12
+11
@@ -21630,27 +21629,27 @@
4
5
-12
+11
7
8
-12
+11
10
11
-12
+11
12
13
-12
+11
13
14
-12
+11
@@ -21666,27 +21665,27 @@
4
5
-12
+11
7
8
-12
+11
10
11
-12
+11
12
13
-12
+11
13
14
-12
+11
@@ -21702,12 +21701,12 @@
1
2
-12
+11
2
3
-48
+46
@@ -21723,7 +21722,7 @@
3
4
-60
+58
@@ -21733,23 +21732,23 @@
derivedtypes
-4572704
+4651908
id
-4572704
+4651908
name
-2240812
+2328554
kind
-97
+93
type_id
-2743126
+2780308
@@ -21763,7 +21762,7 @@
1
2
-4572704
+4651908
@@ -21779,7 +21778,7 @@
1
2
-4572704
+4651908
@@ -21795,7 +21794,7 @@
1
2
-4572704
+4651908
@@ -21811,17 +21810,17 @@
1
2
-1641146
+1709696
2
3
-478908
+500222
3
-42612
-120757
+43371
+118634
@@ -21837,12 +21836,12 @@
1
2
-2240788
+2328530
2
3
-24
+23
@@ -21858,17 +21857,17 @@
1
2
-1641146
+1709965
2
3
-478908
+499954
3
-42594
-120757
+43353
+118634
@@ -21884,42 +21883,42 @@
21
22
-12
+11
-38
-39
-12
+61
+62
+11
2051
2052
-12
+11
-25096
-25097
-12
+27346
+27347
+11
-42664
-42665
-12
+43423
+43424
+11
-53931
-53932
-12
+58521
+58522
+11
-97752
-97753
-12
+102765
+102766
+11
-154514
-154515
-12
+163929
+163930
+11
@@ -21935,42 +21934,42 @@
1
2
-12
+11
13
14
-12
+11
38
39
-12
+11
1152
1153
-12
+11
-13367
-13368
-12
+15027
+15028
+11
-36098
-36099
-12
+39331
+39332
+11
-50248
-50249
-12
+53789
+53790
+11
-83386
-83387
-12
+89933
+89934
+11
@@ -21986,42 +21985,42 @@
12
13
-12
+11
21
22
-12
+11
969
970
-12
+11
-25096
-25097
-12
+27346
+27347
+11
-42664
-42665
-12
+43423
+43424
+11
-53931
-53932
-12
+58521
+58522
+11
-97422
-97423
-12
+102434
+102435
+11
-154514
-154515
-12
+163929
+163930
+11
@@ -22037,22 +22036,22 @@
1
2
-1661730
+1683160
2
3
-405544
+392781
3
4
-615800
+646083
4
-198
-60050
+202
+58283
@@ -22068,22 +22067,22 @@
1
2
-1663019
+1684457
2
3
-405398
+392618
3
4
-614621
+644949
4
198
-60087
+58283
@@ -22099,22 +22098,22 @@
1
2
-1663408
+1684784
2
3
-406809
+393985
3
4
-614584
+644902
4
7
-58324
+56635
@@ -22124,19 +22123,19 @@
pointerishsize
-3358477
+3426426
id
-3358477
+3426426
size
-24
+23
alignment
-12
+11
@@ -22150,7 +22149,7 @@
1
2
-3358477
+3426426
@@ -22166,7 +22165,7 @@
1
2
-3358477
+3426426
@@ -22182,12 +22181,12 @@
21
22
-12
+11
-276205
-276206
-12
+293219
+293220
+11
@@ -22203,7 +22202,7 @@
1
2
-24
+23
@@ -22217,9 +22216,9 @@
12
-276226
-276227
-12
+293240
+293241
+11
@@ -22235,7 +22234,7 @@
2
3
-12
+11
@@ -22245,23 +22244,23 @@
arraysizes
-18553
+18099
id
-18553
+18099
num_elements
-2395
+2301
bytesize
-2784
+2675
alignment
-85
+81
@@ -22275,7 +22274,7 @@
1
2
-18553
+18099
@@ -22291,7 +22290,7 @@
1
2
-18553
+18099
@@ -22307,7 +22306,7 @@
1
2
-18553
+18099
@@ -22323,37 +22322,37 @@
1
2
-182
+175
2
3
-1422
+1367
3
4
-85
+81
4
5
-182
+175
5
13
-182
+175
13
25
-182
+175
-37
-112
-158
+38
+116
+151
@@ -22369,27 +22368,27 @@
1
2
-1762
+1694
2
3
-243
+233
3
4
-133
+128
4
6
-182
+175
6
11
-72
+70
@@ -22405,27 +22404,27 @@
1
2
-1775
+1705
2
3
-255
+245
3
4
-158
+151
4
7
-182
+175
7
8
-24
+23
@@ -22441,42 +22440,42 @@
1
2
-170
+163
2
3
-1556
+1495
3
4
-133
+128
4
6
-218
+210
6
9
-218
+210
9
17
-218
+210
17
53
-231
+210
55
-70
-36
+75
+46
@@ -22492,22 +22491,22 @@
1
2
-2115
+2033
2
3
-389
+373
3
6
-243
+233
6
7
-36
+35
@@ -22523,22 +22522,22 @@
1
2
-2176
+2091
2
3
-303
+292
3
5
-255
+245
5
7
-48
+46
@@ -22552,39 +22551,39 @@
12
-11
-12
-12
+17
+18
+11
-12
-13
-12
+18
+19
+11
33
34
-12
+11
-44
-45
-12
+50
+51
+11
181
182
-12
+11
-384
-385
-12
+389
+390
+11
861
862
-12
+11
@@ -22600,37 +22599,37 @@
4
5
-12
+11
5
6
-12
+11
13
14
-12
+11
16
17
-12
+11
39
40
-12
+11
40
41
-12
+11
190
191
-12
+11
@@ -22646,37 +22645,37 @@
1
2
-12
+11
2
3
-12
+11
14
15
-12
+11
17
18
-12
+11
41
42
-12
+11
53
54
-12
+11
191
192
-12
+11
@@ -22686,15 +22685,15 @@
typedefbase
-1884327
+1867884
id
-1884327
+1867884
type_id
-897232
+871937
@@ -22708,7 +22707,7 @@
1
2
-1884327
+1867884
@@ -22724,22 +22723,22 @@
1
2
-702441
+676919
2
3
-93218
+90264
3
-7
-76014
+6
+70856
-7
-5391
-25557
+6
+5485
+33897
@@ -22749,23 +22748,23 @@
decltypes
-45849
+44063
id
-45849
+44063
expr
-43332
+41644
base_type
-6480
+6227
parentheses_would_change_meaning
-24
+23
@@ -22779,7 +22778,7 @@
1
2
-45849
+44063
@@ -22795,7 +22794,7 @@
1
2
-45849
+44063
@@ -22811,7 +22810,7 @@
1
2
-45849
+44063
@@ -22827,12 +22826,12 @@
1
2
-40815
+39225
2
3
-2516
+2418
@@ -22848,12 +22847,12 @@
1
2
-40815
+39225
2
3
-2516
+2418
@@ -22869,7 +22868,7 @@
1
2
-43332
+41644
@@ -22885,17 +22884,17 @@
1
2
-3623
+3482
2
3
-2553
+2453
3
277
-303
+292
@@ -22911,22 +22910,22 @@
1
2
-2541
+2442
2
3
-3282
+3154
3
12
-522
+502
13
2445
-133
+128
@@ -22942,7 +22941,7 @@
1
2
-6480
+6227
@@ -22958,12 +22957,12 @@
8
9
-12
+11
1082
1083
-12
+11
@@ -22979,12 +22978,12 @@
8
9
-12
+11
3556
3557
-12
+11
@@ -23000,12 +22999,12 @@
8
9
-12
+11
525
526
-12
+11
@@ -23015,19 +23014,19 @@
usertypes
-4328015
+4337028
id
-4328015
+4337028
name
-899578
+917472
kind
-121
+128
@@ -23041,7 +23040,7 @@
1
2
-4328015
+4337028
@@ -23057,7 +23056,7 @@
1
2
-4328015
+4337028
@@ -23073,22 +23072,22 @@
1
2
-597490
+606962
2
3
-199592
+203676
3
-9
-71783
+8
+69886
-9
-31201
-30712
+8
+32070
+36947
@@ -23104,12 +23103,12 @@
1
2
-842701
+861619
2
-9
-56877
+10
+55852
@@ -23125,52 +23124,57 @@
23
24
-12
+11
372
373
-12
+11
773
774
-12
+11
-1526
-1527
-12
+1856
+1857
+11
-4119
-4120
-12
+4195
+4196
+11
-17519
-17520
-12
+14604
+14605
+11
-20287
-20288
-12
+17544
+17545
+11
-75247
-75248
-12
+20425
+20426
+11
-81118
-81119
-12
+75551
+75552
+11
-154981
-154982
-12
+90575
+90576
+11
+
+
+145253
+145254
+11
@@ -23186,47 +23190,52 @@
16
17
-12
+11
38
39
-24
+23
404
405
-12
+11
-776
-777
-12
+548
+549
+11
-2846
-2847
-12
+777
+778
+11
-5034
-5035
-12
+2848
+2849
+11
-9121
-9122
-12
+4545
+4546
+11
+
+
+9129
+9130
+11
11659
11660
-12
+11
-49218
-49219
-12
+53824
+53825
+11
@@ -23236,19 +23245,19 @@
usertypesize
-1364882
+1420908
id
-1364882
+1420908
size
-1641
+1577
alignment
-97
+93
@@ -23262,7 +23271,7 @@
1
2
-1364882
+1420908
@@ -23278,7 +23287,7 @@
1
2
-1364882
+1420908
@@ -23294,52 +23303,52 @@
1
2
-474
+455
2
3
-218
+210
3
4
-72
+70
4
5
-109
+105
5
8
-121
+116
8
12
-109
+105
12
16
-133
+128
16
36
-133
+128
37
170
-133
+128
260
-86076
-133
+95028
+128
@@ -23355,17 +23364,17 @@
1
2
-1349
+1297
2
3
-194
+186
3
6
-97
+93
@@ -23381,42 +23390,42 @@
1
2
-12
+11
3
4
-12
+11
7
8
-12
+11
50
51
-12
+11
54
55
-12
+11
-1916
-1917
-12
+2246
+2247
+11
-9653
-9654
-12
+9717
+9718
+11
-100574
-100575
-12
+109526
+109527
+11
@@ -23432,37 +23441,37 @@
1
2
-24
+23
3
4
-12
+11
8
9
-12
+11
12
13
-12
+11
17
18
-12
+11
25
26
-12
+11
106
107
-12
+11
@@ -23472,26 +23481,26 @@
usertype_final
-1331
+1317
id
-1331
+1317
usertype_uuid
-4897
+4847
id
-4897
+4847
uuid
-4897
+4847
@@ -23505,7 +23514,7 @@
1
2
-4897
+4847
@@ -23521,7 +23530,7 @@
1
2
-4897
+4847
@@ -23531,15 +23540,15 @@
mangled_name
-4326447
+4333558
id
-4326447
+4333558
mangled_name
-533074
+514256
@@ -23553,7 +23562,7 @@
1
2
-4326447
+4333558
@@ -23569,32 +23578,32 @@
1
2
-326831
+310895
2
3
-67771
+66497
3
4
-35794
+35217
4
-8
-44998
+7
+39225
-8
-36
-40183
+7
+23
+38664
-36
-8470
-17495
+23
+8589
+23755
@@ -23604,59 +23613,59 @@
is_pod_class
-1041528
+1104649
id
-1041528
+1104649
is_standard_layout_class
-1118989
+1180366
id
-1118989
+1180366
is_complete
-1346048
+1398952
id
-1346048
+1398952
is_class_template
-246658
+238660
id
-246658
+238660
class_instantiation
-1109032
+1176522
to
-1107305
+1174863
from
-72525
+72363
@@ -23670,12 +23679,12 @@
1
2
-1105676
+1173297
2
4
-1629
+1565
@@ -23691,47 +23700,47 @@
1
2
-21605
+21289
2
3
-12900
+12771
3
4
-7489
+7314
4
5
-4790
+4942
5
7
-5872
+5994
7
11
-6516
+6438
11
20
-5544
+5585
20
-89
-5471
+87
+5456
-89
-2079
-2334
+87
+3629
+2570
@@ -23741,19 +23750,19 @@
class_template_argument
-2800453
+3135757
type_id
-1339823
+1428725
index
-1361
+1308
arg_type
-921439
+904899
@@ -23767,27 +23776,27 @@
1
2
-593757
+586000
2
3
-438178
+428957
3
4
-175628
+258501
4
7
-108647
+129501
7
113
-23611
+25764
@@ -23803,22 +23812,22 @@
1
2
-619740
+613658
2
3
-445764
+442173
3
4
-188030
+272382
4
113
-86288
+100511
@@ -23834,37 +23843,37 @@
1
2
-12
+11
2
3
-863
+829
3
26
-109
+105
29
64
-109
+105
69
-427
-109
+435
+105
-594
-8438
-109
+616
+9263
+105
-11951
-101094
-48
+13601
+119714
+46
@@ -23880,37 +23889,37 @@
1
2
-12
+11
2
3
-863
+829
3
14
-121
+116
14
26
-109
+105
29
-147
-109
+148
+105
-191
-3560
-109
+198
+3591
+105
-11321
-39550
-36
+11582
+41130
+35
@@ -23926,22 +23935,27 @@
1
2
-571203
+550198
2
3
-205818
+200100
3
-5
-75382
+4
+54918
-5
-5259
-69035
+4
+10
+70727
+
+
+10
+11421
+28954
@@ -23957,17 +23971,253 @@
1
2
-806055
+786767
2
3
-96027
+98069
3
22
-19356
+20062
+
+
+
+
+
+
+
+
+class_template_argument_value
+362635
+
+
+type_id
+233565
+
+
+index
+163
+
+
+arg_value
+345447
+
+
+
+
+type_id
+index
+
+
+12
+
+
+1
+2
+209845
+
+
+2
+3
+14512
+
+
+3
+14
+9207
+
+
+
+
+
+
+type_id
+arg_value
+
+
+12
+
+
+1
+2
+197436
+
+
+2
+3
+17971
+
+
+3
+22
+17527
+
+
+24
+173
+630
+
+
+
+
+
+
+index
+type_id
+
+
+12
+
+
+8
+9
+58
+
+
+20
+21
+11
+
+
+25
+26
+11
+
+
+206
+207
+11
+
+
+389
+390
+11
+
+
+552
+553
+11
+
+
+1312
+1313
+11
+
+
+5393
+5394
+11
+
+
+5423
+5424
+11
+
+
+9668
+9669
+11
+
+
+
+
+
+
+index
+arg_value
+
+
+12
+
+
+8
+9
+58
+
+
+20
+21
+11
+
+
+42
+43
+11
+
+
+311
+312
+11
+
+
+514
+515
+11
+
+
+715
+716
+11
+
+
+1585
+1586
+11
+
+
+6302
+6303
+11
+
+
+8160
+8161
+11
+
+
+11875
+11876
+11
+
+
+
+
+
+
+arg_value
+type_id
+
+
+12
+
+
+1
+2
+328387
+
+
+2
+4
+17059
+
+
+
+
+
+
+arg_value
+index
+
+
+12
+
+
+1
+2
+345447
@@ -23977,15 +24227,15 @@
is_proxy_class_for
-50080
+49017
id
-50080
+49017
templ_param_id
-50080
+49017
@@ -23999,7 +24249,7 @@
1
2
-50080
+49017
@@ -24015,7 +24265,7 @@
1
2
-50080
+49017
@@ -24025,19 +24275,19 @@
type_mentions
-1699398
+1682135
id
-1699398
+1682135
type_id
-67393
+67387
location
-1668493
+1651543
kind
@@ -24055,7 +24305,7 @@
1
2
-1699398
+1682135
@@ -24071,7 +24321,7 @@
1
2
-1699398
+1682135
@@ -24087,7 +24337,7 @@
1
2
-1699398
+1682135
@@ -24103,37 +24353,37 @@
1
2
-29850
+30154
2
3
-12338
+12337
3
4
-3697
+3646
4
7
-6156
+6080
7
13
-5213
+5160
13
-34
-5121
+35
+5206
-34
+35
9490
-5015
+4801
@@ -24149,37 +24399,37 @@
1
2
-29850
+30154
2
3
-12338
+12337
3
4
-3697
+3646
4
7
-6156
+6080
7
13
-5213
+5160
13
-34
-5121
+35
+5206
-34
+35
9490
-5015
+4801
@@ -24195,12 +24445,12 @@
1
2
-66134
+66141
2
3
-1258
+1246
@@ -24216,12 +24466,12 @@
1
2
-1637930
+1621291
2
5
-30562
+30252
@@ -24237,12 +24487,12 @@
1
2
-1637930
+1621291
2
5
-30562
+30252
@@ -24258,7 +24508,7 @@
1
2
-1668493
+1651543
@@ -24298,8 +24548,8 @@
6
-10209
-10210
+10313
+10314
6
@@ -24331,26 +24581,26 @@
is_function_template
-1058258
+1023581
id
-1058258
+1023581
function_instantiation
-739585
+719194
to
-739585
+719194
from
-136125
+134923
@@ -24364,7 +24614,7 @@
1
2
-739585
+719194
@@ -24380,37 +24630,32 @@
1
2
-63029
+64125
2
3
-32402
+31385
3
4
-8194
+7898
4
-5
-9301
+6
+12467
-5
-10
-10942
+6
+15
+10434
-10
-57
-10225
-
-
-58
+15
645
-2030
+8611
@@ -24420,19 +24665,19 @@
function_template_argument
-1911112
+1981225
function_id
-1112606
+1088560
index
-243
+233
arg_type
-369871
+359386
@@ -24446,22 +24691,22 @@
1
2
-634901
+600045
2
3
-361093
+302330
3
-5
-84659
+4
+131242
-5
+4
21
-31952
+54941
@@ -24477,22 +24722,22 @@
1
2
-658075
+618530
2
3
-332448
+293520
3
-5
-90920
+4
+116683
-5
+4
21
-31162
+59825
@@ -24508,102 +24753,102 @@
4
5
-12
+11
7
8
-12
+11
17
18
-12
+11
39
40
-12
+11
65
66
-12
+11
152
153
-12
+11
-238
-239
-12
+241
+242
+11
-325
-326
-12
+328
+329
+11
-422
-423
-12
+425
+426
+11
-520
-521
-12
+523
+524
+11
-754
-755
-12
+758
+759
+11
-1007
-1008
-12
+1011
+1012
+11
-1259
-1260
-12
+1520
+1521
+11
-1514
-1515
-12
+1701
+1702
+11
-2559
-2560
-12
+2566
+2567
+11
-2804
-2805
-12
+2917
+2918
+11
-4818
-4819
-12
+5686
+5687
+11
-13309
-13310
-12
+17602
+17603
+11
-40569
-40570
-12
+42858
+42859
+11
-84931
-84932
-12
+88286
+88287
+11
@@ -24619,102 +24864,102 @@
4
5
-12
+11
7
8
-12
+11
14
15
-12
+11
22
23
-12
+11
32
33
-12
+11
54
55
-12
+11
-57
-58
-12
+60
+61
+11
-61
-62
-12
+62
+63
+11
-75
-76
-12
+78
+79
+11
-90
-91
-12
+91
+92
+11
-137
-138
-12
+140
+141
+11
214
215
-12
+11
-238
-239
-12
+242
+243
+11
-316
-317
-12
+317
+318
+11
-517
-518
-12
+521
+522
+11
-695
-696
-12
+700
+701
+11
-1468
-1469
-12
+1491
+1492
+11
-3652
-3653
-12
+3698
+3699
+11
-8773
-8774
-12
+8798
+8799
+11
-17388
-17389
-12
+17653
+17654
+11
@@ -24730,27 +24975,27 @@
1
2
-249515
+242738
2
3
-48743
+46902
3
6
-29788
+28756
6
-19
-28000
+20
+27704
-19
-906
-13824
+20
+1989
+13285
@@ -24766,17 +25011,238 @@
1
2
-341214
+331740
2
5
-28171
+27131
5
-10
-486
+12
+514
+
+
+
+
+
+
+
+
+function_template_argument_value
+209892
+
+
+function_id
+113037
+
+
+index
+163
+
+
+arg_value
+181755
+
+
+
+
+function_id
+index
+
+
+12
+
+
+1
+2
+107207
+
+
+2
+14
+5830
+
+
+
+
+
+
+function_id
+arg_value
+
+
+12
+
+
+1
+2
+89610
+
+
+2
+3
+17106
+
+
+3
+113
+6321
+
+
+
+
+
+
+index
+function_id
+
+
+12
+
+
+3
+4
+70
+
+
+5
+6
+11
+
+
+6
+7
+11
+
+
+111
+112
+11
+
+
+441
+442
+11
+
+
+857
+858
+11
+
+
+2136
+2137
+11
+
+
+2428
+2429
+11
+
+
+4217
+4218
+11
+
+
+
+
+
+
+index
+arg_value
+
+
+12
+
+
+5
+6
+70
+
+
+7
+8
+11
+
+
+8
+9
+11
+
+
+157
+158
+11
+
+
+405
+406
+11
+
+
+973
+974
+11
+
+
+2523
+2524
+11
+
+
+4848
+4849
+11
+
+
+6604
+6605
+11
+
+
+
+
+
+
+arg_value
+function_id
+
+
+12
+
+
+1
+2
+153712
+
+
+2
+3
+27949
+
+
+3
+4
+93
+
+
+
+
+
+
+arg_value
+index
+
+
+12
+
+
+1
+2
+181755
@@ -24786,26 +25252,26 @@
is_variable_template
-19781
+19011
id
-19781
+19011
variable_instantiation
-31842
+38057
to
-31842
+38057
from
-6735
+6929
@@ -24819,7 +25285,7 @@
1
2
-31842
+38057
@@ -24840,32 +25306,32 @@
2
3
-2030
+2033
3
4
-449
+432
4
5
-693
+782
5
9
-534
+560
9
-29
-534
+21
+525
-42
-214
-109
+24
+286
+210
@@ -24875,11 +25341,11 @@
variable_template_argument
-5536
+5499
variable_id
-1047
+848
index
@@ -24887,7 +25353,7 @@
arg_type
-4257
+4214
@@ -24901,12 +25367,12 @@
1
2
-856
+685
2
3
-151
+123
3
@@ -24927,37 +25393,42 @@
1
2
-553
+371
2
3
-164
+130
3
4
-59
+65
4
6
-79
+71
6
8
-85
+71
8
-26
-79
+13
+65
-30
+13
+85
+65
+
+
+202
203
-26
+6
@@ -24981,18 +25452,18 @@
6
-12
-13
+10
+11
6
-21
-22
+20
+21
6
-155
-156
+125
+126
6
@@ -25022,8 +25493,8 @@
6
-137
-138
+139
+140
6
@@ -25045,17 +25516,17 @@
1
2
-3532
+3490
2
3
-481
+476
3
11
-243
+247
@@ -25071,12 +25542,148 @@
1
2
-4046
+3999
+
+
+2
+4
+215
+
+
+
+
+
+
+
+
+variable_template_argument_value
+137
+
+
+variable_id
+19
+
+
+index
+13
+
+
+arg_value
+137
+
+
+
+
+variable_id
+index
+
+
+12
+
+
+1
+2
+13
2
3
-210
+6
+
+
+
+
+
+
+variable_id
+arg_value
+
+
+12
+
+
+3
+4
+13
+
+
+15
+16
+6
+
+
+
+
+
+
+index
+variable_id
+
+
+12
+
+
+1
+2
+6
+
+
+3
+4
+6
+
+
+
+
+
+
+index
+arg_value
+
+
+12
+
+
+6
+7
+6
+
+
+15
+16
+6
+
+
+
+
+
+
+arg_value
+variable_id
+
+
+12
+
+
+1
+2
+137
+
+
+
+
+
+
+arg_value
+index
+
+
+12
+
+
+1
+2
+137
@@ -25086,15 +25693,15 @@
routinetypes
-444646
+436132
id
-444646
+436132
return_type
-184832
+178086
@@ -25108,7 +25715,7 @@
1
2
-444646
+436132
@@ -25124,22 +25731,22 @@
1
2
-148345
+142985
2
3
-21046
+20191
3
-18
-13945
+17
+13367
-18
-7709
-1495
+17
+8001
+1542
@@ -25149,19 +25756,19 @@
routinetypeargs
-741324
+726544
routine
-362820
+357377
index
-389
+373
type_id
-209392
+208724
@@ -25175,27 +25782,27 @@
1
2
-169440
+166448
2
3
-95370
+96574
3
4
-58153
+55888
4
6
-31356
+30298
6
33
-8498
+8167
@@ -25211,22 +25818,22 @@
1
2
-194863
+190916
2
3
-95869
+97018
3
4
-49715
+47778
4
22
-22371
+21663
@@ -25242,62 +25849,62 @@
1
2
-133
+128
2
7
-24
+23
10
16
-24
+23
19
33
-24
+23
52
74
-24
+23
93
115
-24
+23
139
199
-24
+23
269
364
-24
+23
473
700
-24
+23
1319
-3279
-24
+3293
+23
-8061
-15906
-24
+8075
+16341
+23
-29841
-29842
-12
+30585
+30586
+11
@@ -25313,62 +25920,62 @@
1
2
-133
+128
2
5
-24
+23
6
10
-24
+23
11
22
-24
+23
33
37
-24
+23
38
42
-24
+23
44
83
-24
+23
124
183
-24
+23
248
346
-24
+23
463
950
-24
+23
-2463
-5546
-24
+2464
+5642
+23
-12306
-12307
-12
+12850
+12851
+11
@@ -25384,27 +25991,27 @@
1
2
-123639
+124231
2
3
-40913
+41258
3
4
-13508
+13016
4
7
-17945
+17293
7
-1097
-13386
+1099
+12923
@@ -25420,17 +26027,17 @@
1
2
-156783
+158176
2
3
-41204
+39576
3
33
-11404
+10971
@@ -25440,19 +26047,19 @@
ptrtomembers
-12182
+13227
id
-12182
+13227
type_id
-10249
+10013
class_id
-5519
+6601
@@ -25466,7 +26073,7 @@
1
2
-12182
+13227
@@ -25482,7 +26089,7 @@
1
2
-12182
+13227
@@ -25498,12 +26105,12 @@
1
2
-9896
+9628
2
-65
-352
+100
+385
@@ -25519,12 +26126,12 @@
1
2
-9896
+9628
2
-65
-352
+100
+385
@@ -25540,22 +26147,22 @@
1
2
-4620
+5573
2
-7
-352
+3
+490
8
-9
-462
+17
+502
-16
+64
65
-85
+35
@@ -25571,22 +26178,22 @@
1
2
-4620
+5573
2
-7
-352
+3
+490
8
-9
-462
+17
+502
-16
+64
65
-85
+35
@@ -25596,15 +26203,15 @@
specifiers
-547
+525
id
-547
+525
str
-547
+525
@@ -25618,7 +26225,7 @@
1
2
-547
+525
@@ -25634,7 +26241,7 @@
1
2
-547
+525
@@ -25644,15 +26251,15 @@
typespecifiers
-1465031
+1479986
type_id
-1460350
+1472695
spec_id
-85
+81
@@ -25666,12 +26273,12 @@
1
2
-1455669
+1465403
2
3
-4680
+7291
@@ -25687,37 +26294,37 @@
102
103
-12
+11
222
223
-12
+11
518
519
-12
+11
-718
-719
-12
+957
+958
+11
2323
2324
-12
+11
-19328
-19329
-12
+20241
+20242
+11
-97284
-97285
-12
+102297
+102298
+11
@@ -25727,15 +26334,15 @@
funspecifiers
-11371716
+11412148
func_id
-3512354
+3500391
spec_id
-194
+186
@@ -25749,27 +26356,27 @@
1
2
-342673
+329999
2
3
-464124
+448167
3
4
-909123
+883458
4
5
-1670679
+1717910
5
8
-125754
+120855
@@ -25785,82 +26392,82 @@
22
23
-12
+11
169
170
-12
+11
491
492
-12
+11
-506
-507
-12
+598
+599
+11
632
633
-12
+11
5650
5651
-12
+11
9530
9531
-12
+11
10451
10452
-12
+11
-11821
-11822
-12
+11834
+11835
+11
14848
14849
-12
+11
-25327
-25328
-12
+26196
+26197
+11
-33529
-33530
-12
+33724
+33725
+11
-122450
-122451
-12
+131801
+131802
+11
-204591
-204592
-12
+214925
+214926
+11
-232286
-232287
-12
+242884
+242885
+11
-262991
-262992
-12
+272918
+272919
+11
@@ -25870,11 +26477,11 @@
varspecifiers
-1123267
+1113938
var_id
-934196
+926194
spec_id
@@ -25892,17 +26499,17 @@
1
2
-794360
+787198
2
3
-92281
+91911
3
5
-47554
+47084
@@ -25926,8 +26533,8 @@
6
-452
-453
+447
+448
6
@@ -25936,28 +26543,28 @@
6
-8407
-8408
+8411
+8412
6
-8751
-8752
+8753
+8754
6
-11153
-11154
+11295
+11296
6
-13352
-13353
+13439
+13440
6
-42323
-42324
+42412
+42413
6
@@ -26546,11 +27153,11 @@
attribute_args
-118319
+118389
id
-118319
+118389
kind
@@ -26558,7 +27165,7 @@
attribute
-116952
+117020
index
@@ -26566,7 +27173,7 @@
location
-58256
+58290
@@ -26580,7 +27187,7 @@
1
2
-118319
+118389
@@ -26596,7 +27203,7 @@
1
2
-118319
+118389
@@ -26612,7 +27219,7 @@
1
2
-118319
+118389
@@ -26628,7 +27235,7 @@
1
2
-118319
+118389
@@ -26728,12 +27335,12 @@
1
2
-116132
+116200
2
4
-819
+820
@@ -26749,7 +27356,7 @@
1
2
-116431
+116499
2
@@ -26770,12 +27377,12 @@
1
2
-116132
+116200
2
4
-819
+820
@@ -26791,7 +27398,7 @@
1
2
-116137
+116205
2
@@ -26911,22 +27518,22 @@
1
2
-28468
+28485
2
3
-7516
+7521
3
4
-19458
+19469
4
25
-2812
+2814
@@ -26942,12 +27549,12 @@
1
2
-52020
+52051
2
3
-6235
+6239
@@ -26963,22 +27570,22 @@
1
2
-28452
+28469
2
3
-7534
+7539
3
4
-19454
+19466
4
25
-2814
+2815
@@ -26994,7 +27601,7 @@
1
2
-58250
+58285
3
@@ -27009,15 +27616,15 @@
attribute_arg_value
-118292
+118362
arg
-118292
+118362
value
-2257
+2258
@@ -27031,7 +27638,7 @@
1
2
-118292
+118362
@@ -27047,7 +27654,7 @@
1
2
-1808
+1809
2
@@ -27072,15 +27679,15 @@
attribute_arg_type
-60
+58
arg
-60
+58
type_id
-36
+35
@@ -27094,7 +27701,7 @@
1
2
-60
+58
@@ -27110,12 +27717,12 @@
1
2
-12
+11
2
3
-24
+23
@@ -27178,15 +27785,15 @@
typeattributes
-12121
+12701
type_id
-10833
+11462
spec_id
-12121
+12701
@@ -27200,12 +27807,12 @@
1
2
-10298
+10948
2
34
-534
+514
@@ -27221,7 +27828,7 @@
1
2
-12121
+12701
@@ -27231,15 +27838,15 @@
funcattributes
-327062
+314529
func_id
-174935
+168330
spec_id
-327062
+314529
@@ -27253,22 +27860,22 @@
1
2
-94057
+90603
2
3
-13556
+13028
3
4
-65144
+62606
4
14
-2176
+2091
@@ -27284,7 +27891,7 @@
1
2
-327062
+314529
@@ -27400,15 +28007,15 @@
unspecifiedtype
-9371008
+9451546
type_id
-9371008
+9451546
unspecified_type_id
-5062944
+5133540
@@ -27422,7 +28029,7 @@
1
2
-9371008
+9451546
@@ -27438,17 +28045,17 @@
1
2
-2734044
+2776289
2
3
-1949727
+1978655
3
-7841
-379173
+7936
+378596
@@ -27458,19 +28065,19 @@
member
-5112818
+5066844
parent
-809898
+833190
index
-2966
+2851
child
-5068087
+5050544
@@ -27484,47 +28091,47 @@
1
2
-46955
+45126
2
3
-191689
+219789
3
4
-208371
+209109
4
5
-90300
+90638
5
7
-65047
+67210
7
9
-67224
+65539
9
15
-68233
+66497
15
-39
-62482
+45
+62758
-39
+45
245
-9593
+6520
@@ -27540,47 +28147,47 @@
1
2
-46226
+44425
2
3
-191823
+219824
3
4
-202729
+203723
4
5
-93170
+93454
5
7
-65740
+67876
7
9
-67357
+65668
9
15
-69072
+67245
15
-37
-61242
+41
+62641
-37
+41
281
-12535
+8331
@@ -27596,62 +28203,62 @@
1
2
-547
+525
2
5
-267
+257
5
9
-255
+245
9
18
-231
+222
23
100
-231
+222
122
186
-231
+222
188
293
-231
+222
296
374
-231
+222
375
542
-231
+222
572
3257
-231
+222
3427
-21553
-231
+22111
+222
-29159
-65990
-48
+30337
+70680
+46
@@ -27667,62 +28274,62 @@
1
2
-534
+514
2
5
-218
+210
5
7
-231
+222
7
13
-231
+222
13
70
-231
+222
71
160
-231
+222
160
256
-231
+222
263
345
-231
+222
346
476
-243
+233
492
1685
-231
+222
1926
-8765
-231
+8775
+222
-10213
-66835
-121
+10226
+71530
+116
@@ -27738,7 +28345,7 @@
1
2
-5068087
+5050544
@@ -27754,12 +28361,12 @@
1
2
-5043175
+5034466
2
-11
-24912
+7
+16078
@@ -27769,15 +28376,15 @@
enclosingfunction
-131736
+127164
child
-131736
+127164
parent
-74251
+71639
@@ -27791,7 +28398,7 @@
1
2
-131736
+127164
@@ -27807,27 +28414,27 @@
1
2
-39283
+37811
2
3
-21897
+21231
3
4
-7209
+6952
4
6
-5653
+5445
6
45
-206
+198
@@ -27837,27 +28444,27 @@
derivations
-370358
+392220
derivation
-370358
+392220
sub
-346612
+367484
index
-72
+70
super
-237819
+236475
location
-96367
+92613
@@ -27871,7 +28478,7 @@
1
2
-370358
+392220
@@ -27887,7 +28494,7 @@
1
2
-370358
+392220
@@ -27903,7 +28510,7 @@
1
2
-370358
+392220
@@ -27919,7 +28526,7 @@
1
2
-370358
+392220
@@ -27935,12 +28542,12 @@
1
2
-326174
+345926
2
7
-20438
+21558
@@ -27956,12 +28563,12 @@
1
2
-334989
+356161
2
7
-11623
+11322
@@ -27977,12 +28584,12 @@
1
2
-326186
+345937
2
7
-20426
+21546
@@ -27998,12 +28605,12 @@
1
2
-334977
+356150
2
7
-11635
+11334
@@ -28019,32 +28626,32 @@
1
2
-12
+11
4
5
-12
+11
44
45
-12
+11
220
221
-12
+11
-957
-958
-12
+970
+971
+11
-29235
-29236
-12
+32328
+32329
+11
@@ -28060,32 +28667,32 @@
1
2
-12
+11
4
5
-12
+11
44
45
-12
+11
220
221
-12
+11
-956
-957
-12
+969
+970
+11
-28508
-28509
-12
+31450
+31451
+11
@@ -28101,32 +28708,32 @@
1
2
-12
+11
3
4
-12
+11
29
30
-12
+11
84
85
-12
+11
-441
-442
-12
+452
+453
+11
-19046
-19047
-12
+19713
+19714
+11
@@ -28142,32 +28749,32 @@
1
2
-12
+11
4
5
-12
+11
17
18
-12
+11
51
52
-12
+11
254
255
-12
+11
7602
7603
-12
+11
@@ -28183,12 +28790,12 @@
1
2
-222390
+221343
2
-766
-15429
+1134
+15131
@@ -28204,12 +28811,12 @@
1
2
-222402
+221355
2
-766
-15416
+1134
+15120
@@ -28225,12 +28832,12 @@
1
2
-237320
+235996
2
4
-498
+479
@@ -28246,12 +28853,12 @@
1
2
-229733
+228634
2
439
-8085
+7840
@@ -28267,22 +28874,22 @@
1
2
-74677
+70786
2
3
-9325
+8985
3
8
-7307
+7606
8
-682
-5057
+698
+5234
@@ -28298,22 +28905,22 @@
1
2
-77206
+73146
2
3
-7003
+6858
3
-9
-7611
+8
+7443
-9
-682
-4547
+8
+698
+5164
@@ -28329,12 +28936,12 @@
1
2
-96343
+92589
2
4
-24
+23
@@ -28350,22 +28957,22 @@
1
2
-77680
+73882
2
3
-9045
+8833
3
-11
-7270
+10
+7326
-11
-682
-2370
+10
+698
+2570
@@ -28375,15 +28982,15 @@
derspecifiers
-372996
+394756
der_id
-370321
+392185
spec_id
-48
+46
@@ -28397,12 +29004,12 @@
1
2
-367646
+389615
2
3
-2674
+2570
@@ -28418,22 +29025,22 @@
220
221
-12
+11
242
243
-12
+11
979
980
-12
+11
-29237
-29238
-12
+32343
+32344
+11
@@ -28443,15 +29050,15 @@
direct_base_offsets
-282075
+307366
der_id
-282075
+307366
offset
-206
+210
@@ -28465,7 +29072,7 @@
1
2
-282075
+307366
@@ -28481,62 +29088,67 @@
1
2
-36
+46
2
3
-12
+11
4
5
-24
+23
5
6
-12
+11
6
7
-24
+11
7
8
-12
+23
8
9
-24
+11
+
+
+10
+11
+11
11
12
-12
+11
21
22
-12
+11
-78
-79
-12
+85
+86
+11
200
201
-12
+11
-22837
-22838
-12
+25931
+25932
+11
@@ -28546,19 +29158,19 @@
virtual_base_offsets
-7027
+6753
sub
-3890
+3739
super
-534
+514
offset
-267
+257
@@ -28572,22 +29184,22 @@
1
2
-3063
+2944
2
4
-340
+327
4
7
-279
+268
7
11
-206
+198
@@ -28603,17 +29215,17 @@
1
2
-3282
+3154
2
4
-328
+315
4
8
-279
+268
@@ -28629,52 +29241,52 @@
1
2
-85
+81
2
3
-48
+46
3
4
-60
+58
4
5
-97
+93
5
7
-36
+35
8
13
-48
+46
13
15
-48
+46
15
23
-48
+46
24
60
-48
+46
196
197
-12
+11
@@ -28690,32 +29302,32 @@
1
2
-303
+292
2
3
-85
+81
4
6
-36
+35
6
8
-48
+46
8
10
-48
+46
14
15
-12
+11
@@ -28731,57 +29343,57 @@
2
3
-36
+35
4
5
-12
+11
5
6
-24
+23
6
8
-24
+23
8
9
-36
+35
10
12
-24
+23
14
19
-24
+23
20
27
-24
+23
28
31
-24
+23
36
97
-24
+23
97
98
-12
+11
@@ -28797,37 +29409,37 @@
1
2
-85
+81
2
3
-36
+35
3
4
-48
+46
5
7
-24
+23
7
10
-24
+23
12
14
-24
+23
21
29
-24
+23
@@ -28837,23 +29449,23 @@
frienddecls
-231217
+222208
id
-231217
+222208
type_id
-19514
+18753
decl_id
-28815
+27692
location
-8097
+7782
@@ -28867,7 +29479,7 @@
1
2
-231217
+222208
@@ -28883,7 +29495,7 @@
1
2
-231217
+222208
@@ -28899,7 +29511,7 @@
1
2
-231217
+222208
@@ -28915,47 +29527,47 @@
1
2
-6857
+6590
2
3
-2468
+2371
3
4
-1191
+1145
4
6
-1714
+1647
6
10
-1556
+1495
10
16
-1471
+1413
16
34
-1787
+1717
36
59
-1471
+1413
59
129
-996
+958
@@ -28971,47 +29583,47 @@
1
2
-6857
+6590
2
3
-2468
+2371
3
4
-1191
+1145
4
6
-1714
+1647
6
10
-1556
+1495
10
16
-1471
+1413
16
34
-1787
+1717
36
59
-1471
+1413
59
129
-996
+958
@@ -29027,17 +29639,17 @@
1
2
-17751
+17059
2
5
-1544
+1483
5
31
-218
+210
@@ -29053,37 +29665,37 @@
1
2
-15818
+15201
2
3
-2261
+2173
3
5
-2480
+2383
5
9
-2188
+2103
9
20
-2030
+1951
20
28
-2468
+2371
28
390
-1568
+1507
@@ -29099,37 +29711,37 @@
1
2
-15818
+15201
2
3
-2261
+2173
3
5
-2480
+2383
5
9
-2188
+2103
9
20
-2030
+1951
20
28
-2468
+2371
28
390
-1568
+1507
@@ -29145,12 +29757,12 @@
1
2
-28219
+27120
2
46
-595
+572
@@ -29166,17 +29778,17 @@
1
2
-6918
+6648
2
3
-1045
+1004
3
18239
-133
+128
@@ -29192,12 +29804,12 @@
1
2
-7611
+7314
2
1215
-486
+467
@@ -29213,17 +29825,17 @@
1
2
-6930
+6660
2
3
-1033
+993
3
1734
-133
+128
@@ -29233,19 +29845,19 @@
comments
-1743921
+1675974
id
-1743921
+1675974
contents
-864635
+830947
location
-1743921
+1675974
@@ -29259,7 +29871,7 @@
1
2
-1743921
+1675974
@@ -29275,7 +29887,7 @@
1
2
-1743921
+1675974
@@ -29291,17 +29903,17 @@
1
2
-731646
+703139
2
3
-83163
+79923
3
10736
-49825
+47883
@@ -29317,17 +29929,17 @@
1
2
-731646
+703139
2
3
-83163
+79923
3
10736
-49825
+47883
@@ -29343,7 +29955,7 @@
1
2
-1743921
+1675974
@@ -29359,7 +29971,7 @@
1
2
-1743921
+1675974
@@ -29369,15 +29981,15 @@
commentbinding
-778395
+748230
id
-679134
+652673
element
-747391
+718434
@@ -29391,17 +30003,17 @@
1
2
-616274
+592099
2
4
-58384
+56273
4
97
-4474
+4299
@@ -29417,12 +30029,12 @@
1
2
-716387
+688638
2
3
-31004
+29796
@@ -29432,15 +30044,15 @@
exprconv
-6445331
+6446816
converted
-6445038
+6446523
conversion
-6445331
+6446816
@@ -29454,7 +30066,7 @@
1
2
-6444746
+6446231
2
@@ -29475,7 +30087,7 @@
1
2
-6445331
+6446816
@@ -29485,30 +30097,30 @@
compgenerated
-6721088
+6652484
id
-6721088
+6652484
synthetic_destructor_call
-59503
+57185
element
-47430
+45582
i
-340
+327
destructor_call
-59503
+47650
@@ -29522,17 +30134,17 @@
1
2
-38797
+37285
2
3
-6322
+6076
3
29
-2310
+2220
@@ -29548,17 +30160,17 @@
1
2
-38797
+37285
2
3
-6322
+6076
3
29
-2310
+2220
@@ -29574,27 +30186,27 @@
1
2
-255
+245
2
7
-24
+23
22
43
-24
+23
190
711
-24
+23
3901
3902
-12
+11
@@ -29610,27 +30222,27 @@
1
2
-255
+245
2
7
-24
+23
21
37
-24
+23
146
550
-24
+23
3297
3298
-12
+11
@@ -29646,7 +30258,17 @@
1
2
-59503
+42123
+
+
+2
+3
+3575
+
+
+3
+26
+1951
@@ -29662,7 +30284,7 @@
1
2
-59503
+47650
@@ -29672,15 +30294,15 @@
namespaces
-8498
+8167
id
-8498
+8167
name
-4571
+4393
@@ -29694,7 +30316,7 @@
1
2
-8498
+8167
@@ -29710,17 +30332,17 @@
1
2
-3842
+3692
2
3
-462
+444
3
139
-267
+257
@@ -29730,26 +30352,26 @@
namespace_inline
-170
+163
id
-170
+163
namespacembrs
-1562177
+1616300
parentid
-7915
+7606
memberid
-1562177
+1616300
@@ -29763,62 +30385,67 @@
1
2
-887
+829
2
3
-826
+794
3
4
-644
+385
4
-6
-680
-
-
-6
-9
+5
607
-9
-16
-668
+5
+8
+666
-16
-27
-607
+8
+14
+677
-27
-46
-620
-
-
-46
-84
+14
+22
595
-84
-166
+22
+42
595
-168
-406
-595
+42
+63
+572
-410
-28891
-583
+63
+127
+572
+
+
+127
+304
+572
+
+
+321
+1041
+572
+
+
+1101
+32701
+163
@@ -29834,7 +30461,7 @@
1
2
-1562177
+1616300
@@ -29844,19 +30471,19 @@
exprparents
-13615192
+13481241
expr_id
-13615001
+13481052
child_index
-3163
+3131
parent_id
-9643112
+9549204
@@ -29870,7 +30497,7 @@
1
2
-13614995
+13481045
2
@@ -29891,12 +30518,12 @@
1
2
-13614817
+13480869
2
4
-184
+182
@@ -29912,37 +30539,37 @@
1
2
-72
+71
2
3
-652
+645
3
4
-1674
+1657
4
46
-243
+241
46
56
-237
+234
56
3660
-237
+234
6434
-1187663
-46
+1188148
+45
@@ -29958,37 +30585,37 @@
1
2
-72
+71
2
3
-652
+645
3
4
-1674
+1657
4
31
-243
+241
31
41
-237
+234
41
3645
-237
+234
6419
-1187540
-46
+1188161
+45
@@ -30004,17 +30631,17 @@
1
2
-6839533
+6772879
2
3
-2212018
+2190858
3
1681
-591560
+585466
@@ -30030,17 +30657,17 @@
1
2
-6839816
+6772898
2
3
-2211945
+2190839
3
480
-591349
+585466
@@ -30050,22 +30677,22 @@
expr_isload
-5063230
+5014574
expr_id
-5063230
+5014574
conversionkinds
-4254113
+4255501
expr_id
-4254113
+4255501
kind
@@ -30083,7 +30710,7 @@
1
2
-4254113
+4255501
@@ -30107,8 +30734,8 @@
1
-13109
-13110
+13672
+13673
1
@@ -30122,8 +30749,8 @@
1
-4161592
-4161593
+4162417
+4162418
1
@@ -30134,15 +30761,15 @@
iscall
-2364889
+2284876
caller
-2364889
+2284876
kind
-36
+35
@@ -30156,7 +30783,7 @@
1
2
-2364889
+2284876
@@ -30172,17 +30799,17 @@
1319
1320
-12
+11
-5604
-5605
-12
+5643
+5644
+11
-187583
-187584
-12
+188582
+188583
+11
@@ -30192,15 +30819,15 @@
numtemplatearguments
-157974
+152181
expr_id
-157974
+152181
num
-48
+46
@@ -30214,7 +30841,7 @@
1
2
-157974
+152181
@@ -30230,22 +30857,22 @@
3
4
-12
+11
41
42
-12
+11
651
652
-12
+11
-12298
-12299
-12
+12329
+12330
+11
@@ -30255,15 +30882,15 @@
specialnamequalifyingelements
-12
+11
id
-12
+11
name
-12
+11
@@ -30277,7 +30904,7 @@
1
2
-12
+11
@@ -30293,7 +30920,7 @@
1
2
-12
+11
@@ -30303,23 +30930,23 @@
namequalifiers
-1125647
+1116919
id
-1125647
+1116919
qualifiableelement
-1125647
+1116919
qualifyingelement
-37002
+38961
location
-509600
+506087
@@ -30333,7 +30960,7 @@
1
2
-1125647
+1116919
@@ -30349,7 +30976,7 @@
1
2
-1125647
+1116919
@@ -30365,7 +30992,7 @@
1
2
-1125647
+1116919
@@ -30381,7 +31008,7 @@
1
2
-1125647
+1116919
@@ -30397,7 +31024,7 @@
1
2
-1125647
+1116919
@@ -30413,7 +31040,7 @@
1
2
-1125647
+1116919
@@ -30429,109 +31056,109 @@
1
2
-17110
+18965
2
3
-7935
+8142
3
4
-5081
-
-
-4
-10
-2860
-
-
-10
-86
-2781
-
-
-86
-24926
-1232
-
-
-
-
-
-
-qualifyingelement
-qualifiableelement
-
-
-12
-
-
-1
-2
-17110
-
-
-2
-3
-7935
-
-
-3
-4
-5081
-
-
-4
-10
-2860
-
-
-10
-86
-2781
-
-
-86
-24926
-1232
-
-
-
-
-
-
-qualifyingelement
-location
-
-
-12
-
-
-1
-2
-22745
-
-
-2
-3
-4818
-
-
-3
-4
-3803
+5043
4
11
-3018
+3027
+
+
+11
+129
+2935
+
+
+132
+24926
+848
+
+
+
+
+
+
+qualifyingelement
+qualifiableelement
+
+
+12
+
+
+1
+2
+18965
+
+
+2
+3
+8142
+
+
+3
+4
+5043
+
+
+4
+11
+3027
+
+
+11
+129
+2935
+
+
+132
+24926
+848
+
+
+
+
+
+
+qualifyingelement
+location
+
+
+12
+
+
+1
+2
+24850
+
+
+2
+3
+4762
+
+
+3
+4
+3770
+
+
+4
+11
+2974
11
16728
-2616
+2603
@@ -30547,22 +31174,22 @@
1
2
-387659
+384719
2
3
-57006
+56877
3
7
-39038
+38851
7
381
-25896
+25639
@@ -30578,22 +31205,22 @@
1
2
-387659
+384719
2
3
-57006
+56877
3
7
-39038
+38851
7
381
-25896
+25639
@@ -30609,17 +31236,17 @@
1
2
-452489
+449080
2
3
-44786
+44650
3
190
-12325
+12356
@@ -30629,15 +31256,15 @@
varbind
-5497468
+5445518
expr
-5497343
+5445394
var
-1547527
+1534266
@@ -30651,12 +31278,12 @@
1
2
-5497218
+5445270
2
3
-125
+123
@@ -30672,32 +31299,32 @@
1
2
-685146
+679661
2
3
-310616
+308179
3
4
-235999
+234430
4
5
-94126
+93020
5
9
-135129
+133430
9
6150
-86507
+85544
@@ -30707,15 +31334,15 @@
funbind
-2442817
+2419854
expr
-2142581
+2122577
fun
-438015
+434205
@@ -30729,17 +31356,17 @@
1
2
-1842589
+1825541
2
3
-299814
+296859
3
5
-177
+176
@@ -30755,32 +31382,32 @@
1
2
-254164
+252065
2
3
-76403
+75516
3
4
-31716
+31302
4
7
-33976
+33957
7
37
-33831
+33520
37
6664
-7922
+7841
@@ -30790,19 +31417,19 @@
expr_allocator
-27696
+26617
expr
-27696
+26617
func
-133
+128
form
-12
+11
@@ -30816,7 +31443,7 @@
1
2
-27696
+26617
@@ -30832,7 +31459,7 @@
1
2
-27696
+26617
@@ -30848,42 +31475,42 @@
1
2
-36
+35
3
4
-12
+11
4
5
-12
+11
5
6
-12
+11
7
8
-24
+23
39
40
-12
+11
973
974
-12
+11
1237
1238
-12
+11
@@ -30899,7 +31526,7 @@
1
2
-133
+128
@@ -30915,7 +31542,7 @@
2278
2279
-12
+11
@@ -30931,7 +31558,7 @@
11
12
-12
+11
@@ -30941,19 +31568,19 @@
expr_deallocator
-31004
+29796
expr
-31004
+29796
func
-145
+140
form
-24
+23
@@ -30967,7 +31594,7 @@
1
2
-31004
+29796
@@ -30983,7 +31610,7 @@
1
2
-31004
+29796
@@ -30999,42 +31626,42 @@
1
2
-36
+35
2
3
-24
+23
3
4
-12
+11
4
5
-12
+11
7
8
-24
+23
118
119
-12
+11
883
884
-12
+11
1521
1522
-12
+11
@@ -31050,7 +31677,7 @@
1
2
-145
+140
@@ -31066,12 +31693,12 @@
891
892
-12
+11
1659
1660
-12
+11
@@ -31087,12 +31714,12 @@
4
5
-12
+11
8
9
-12
+11
@@ -31102,26 +31729,26 @@
expr_cond_two_operand
-610
+611
cond
-610
+611
expr_cond_guard
-154429
+154519
cond
-154429
+154519
guard
-154429
+154519
@@ -31135,7 +31762,7 @@
1
2
-154429
+154519
@@ -31151,7 +31778,7 @@
1
2
-154429
+154519
@@ -31161,15 +31788,15 @@
expr_cond_true
-154429
+154519
cond
-154429
+154519
true
-154429
+154519
@@ -31183,7 +31810,7 @@
1
2
-154429
+154519
@@ -31199,7 +31826,7 @@
1
2
-154429
+154519
@@ -31209,15 +31836,15 @@
expr_cond_false
-154429
+154519
cond
-154429
+154519
false
-154429
+154519
@@ -31231,7 +31858,7 @@
1
2
-154429
+154519
@@ -31247,7 +31874,7 @@
1
2
-154429
+154519
@@ -31257,15 +31884,15 @@
values
-8776937
+8788031
id
-8776937
+8788031
str
-651390
+651491
@@ -31279,7 +31906,7 @@
1
2
-8776937
+8788031
@@ -31295,17 +31922,17 @@
1
2
-540103
+540120
2
3
-65463
+65472
3
-4044676
-45824
+4046841
+45899
@@ -31315,15 +31942,15 @@
valuetext
-4759522
+4766569
id
-4759522
+4766569
text
-704971
+705085
@@ -31337,7 +31964,7 @@
1
2
-4759522
+4766569
@@ -31353,22 +31980,22 @@
1
2
-528095
+528108
2
3
-102815
+102830
3
7
-56849
+56879
7
427516
-17212
+17268
@@ -31378,15 +32005,15 @@
valuebind
-9499212
+9509366
val
-8769772
+8779926
expr
-9499212
+9509366
@@ -31400,7 +32027,7 @@
1
2
-8041064
+8051218
2
@@ -31426,7 +32053,7 @@
1
2
-9499212
+9509366
@@ -31436,19 +32063,19 @@
fieldoffsets
-259546
+251575
id
-259546
+251575
byteoffset
-3915
+9022
bitoffset
-85
+52
@@ -31462,7 +32089,7 @@
1
2
-259546
+251575
@@ -31478,7 +32105,7 @@
1
2
-259546
+251575
@@ -31494,42 +32121,27 @@
1
2
-1690
+6132
2
3
-534
+782
3
-4
-194
-
-
-4
6
-340
+750
6
-11
-328
+17
+678
-11
-23
-328
-
-
-23
-84
-303
-
-
-90
-12366
-194
+17
+11685
+678
@@ -31545,12 +32157,12 @@
1
2
-3854
+8579
2
-8
-60
+9
+443
@@ -31564,29 +32176,44 @@
12
-1
-2
-36
+83
+84
+6
-2
-3
-12
+87
+88
+6
-3
-4
-12
+102
+103
+6
-4
-5
-12
+122
+123
+6
-21335
-21336
-12
+127
+128
+6
+
+
+153
+154
+6
+
+
+195
+196
+6
+
+
+37692
+37693
+6
@@ -31600,24 +32227,44 @@
12
-1
-2
-36
+40
+41
+6
-2
-3
-24
+44
+45
+6
-3
-4
-12
+45
+46
+6
-322
-323
-12
+52
+53
+6
+
+
+54
+55
+6
+
+
+58
+59
+6
+
+
+66
+67
+6
+
+
+1383
+1384
+6
@@ -31627,11 +32274,11 @@
bitfield
-13357
+13365
id
-13357
+13365
bits
@@ -31653,7 +32300,7 @@
1
2
-13357
+13365
@@ -31669,7 +32316,7 @@
1
2
-13357
+13365
@@ -31833,23 +32480,23 @@
initialisers
-1685056
+1668506
init
-1685056
+1668506
var
-649693
+643667
expr
-1685056
+1668506
location
-321913
+318643
@@ -31863,7 +32510,7 @@
1
2
-1685056
+1668506
@@ -31879,7 +32526,7 @@
1
2
-1685056
+1668506
@@ -31895,7 +32542,7 @@
1
2
-1685056
+1668506
@@ -31911,22 +32558,22 @@
1
2
-561993
+556871
2
16
-29092
+28784
16
17
-49689
+49185
17
53
-8917
+8827
@@ -31942,22 +32589,22 @@
1
2
-561993
+556871
2
16
-29092
+28784
16
17
-49689
+49185
17
53
-8917
+8827
@@ -31973,11 +32620,11 @@
1
2
-649680
+643654
2
-4
+3
13
@@ -31994,7 +32641,7 @@
1
2
-1685056
+1668506
@@ -32010,7 +32657,7 @@
1
2
-1685056
+1668506
@@ -32026,7 +32673,7 @@
1
2
-1685056
+1668506
@@ -32042,27 +32689,27 @@
1
2
-248456
+245906
2
3
-24096
+23845
3
7
-24485
+24243
7
65
-24347
+24126
67
109238
-527
+521
@@ -32078,22 +32725,22 @@
1
2
-271340
+268551
2
3
-25342
+25091
3
-24
-24156
+23
+23904
-24
+23
12632
-1074
+1096
@@ -32109,27 +32756,27 @@
1
2
-248456
+245906
2
3
-24096
+23845
3
7
-24485
+24243
7
65
-24347
+24126
67
109238
-527
+521
@@ -32139,15 +32786,15 @@
expr_ancestor
-67783
+68706
exp
-67187
+68133
ancestor
-49703
+51330
@@ -32161,12 +32808,12 @@
1
2
-66616
+67584
2
4
-571
+549
@@ -32182,17 +32829,17 @@
1
2
-37411
+39517
2
3
-10152
+9756
3
29
-2139
+2056
@@ -32202,19 +32849,19 @@
exprs
-18449753
+18460593
id
-18449753
+18460593
kind
-393
+1168
location
-6150603
+3836046
@@ -32228,7 +32875,7 @@
1
2
-18449753
+18460593
@@ -32244,7 +32891,7 @@
1
2
-18449753
+18460593
@@ -32258,69 +32905,69 @@
12
-5
-28
-30
+1
+14
+105
-71
-82
-30
+15
+44
+93
-94
-255
-30
+47
+86
+93
-271
-627
-30
+90
+223
+93
-858
-1879
-30
+296
+463
+93
-2191
-3716
-30
+483
+715
+93
-4305
-6068
-30
+788
+2129
+93
-7004
-11661
-30
+2165
+2950
+93
-12107
-20202
-30
+3030
+4378
+93
-21965
-29561
-30
+4477
+6077
+93
-32830
-41032
-30
+6490
+17249
+93
-44670
-145995
-30
+19347
+188353
+93
-447805
-725766
-24
+191022
+403956
+35
@@ -32335,68 +32982,68 @@
1
+4
+105
+
+
+6
+13
+105
+
+
+13
24
-30
+93
-27
-72
-30
+24
+38
+93
-77
-157
-30
+38
+134
+93
-171
-402
-30
+144
+259
+93
-422
-1083
-30
+269
+480
+93
-1179
-1862
-30
+481
+1074
+93
-2201
-4268
-30
+1095
+1411
+93
-4679
-6584
-30
+1422
+2045
+93
-6624
-11083
-30
+2059
+4557
+93
-11359
-12983
-30
+5888
+59687
+93
-17158
-28621
-30
-
-
-29980
-88945
-30
-
-
-128315
-425042
-24
+72153
+117893
+23
@@ -32412,22 +33059,37 @@
1
2
-4450587
+1838158
2
3
-874643
+753582
3
-6
-496205
+4
+356208
-6
-22445
-329167
+4
+5
+276250
+
+
+5
+9
+301489
+
+
+9
+72
+287759
+
+
+72
+136887
+22598
@@ -32443,22 +33105,17 @@
1
2
-4599301
+2734142
2
3
-812812
+859259
3
-5
-498909
-
-
-5
-33
-239579
+30
+242644
@@ -32468,19 +33125,19 @@
expr_types
-18622475
+18629051
id
-18449753
+18456012
typeid
-1372748
+1345343
value_category
-36
+35
@@ -32494,12 +33151,12 @@
1
2
-18277261
+18283254
2
4
-172491
+172758
@@ -32515,7 +33172,7 @@
1
2
-18449753
+18456012
@@ -32531,42 +33188,42 @@
1
2
-530582
+526443
2
3
-259315
+254107
3
4
-113571
+110864
4
5
-95346
+92110
5
8
-122885
+118483
8
14
-105669
+102381
14
-45
-102969
+47
+101797
-45
-111447
-42408
+47
+123210
+39155
@@ -32582,17 +33239,17 @@
1
2
-1207211
+1185928
2
3
-157707
+151877
3
4
-7830
+7536
@@ -32608,17 +33265,17 @@
4533
4534
-12
+11
-340829
-340830
-12
+342547
+342548
+11
-1172082
-1172083
-12
+1232420
+1232421
+11
@@ -32632,19 +33289,19 @@
12
-1340
-1341
-12
+1341
+1342
+11
-29722
-29723
-12
+29841
+29842
+11
-96102
-96103
-12
+98243
+98244
+11
@@ -32654,15 +33311,15 @@
new_allocated_type
-29532
+28382
expr
-29532
+28382
type_id
-18237
+17527
@@ -32676,7 +33333,7 @@
1
2
-29532
+28382
@@ -32692,22 +33349,22 @@
1
2
-12669
+12175
2
3
-4036
+3879
3
9
-1446
+1390
10
92
-85
+81
@@ -32717,15 +33374,15 @@
new_array_allocated_type
-5365
+5310
expr
-5365
+5310
type_id
-2306
+2283
@@ -32739,7 +33396,7 @@
1
2
-5365
+5310
@@ -32760,12 +33417,12 @@
2
3
-2043
+2022
3
7
-177
+176
8
@@ -33317,15 +33974,15 @@
condition_decl_bind
-7647
+7349
expr
-7647
+7349
decl
-7647
+7349
@@ -33339,7 +33996,7 @@
1
2
-7647
+7349
@@ -33355,7 +34012,7 @@
1
2
-7647
+7349
@@ -33365,15 +34022,15 @@
typeid_bind
-4899
+4708
expr
-4899
+4708
type_id
-2601
+2500
@@ -33387,7 +34044,7 @@
1
2
-4899
+4708
@@ -33403,22 +34060,22 @@
1
2
-1300
+1250
2
3
-996
+958
3
6
-231
+222
6
17
-72
+70
@@ -33428,15 +34085,15 @@
uuidof_bind
-856
+848
expr
-856
+848
type_id
-652
+645
@@ -33450,7 +34107,7 @@
1
2
-856
+848
@@ -33466,12 +34123,12 @@
1
2
-481
+476
2
3
-138
+137
3
@@ -33486,15 +34143,15 @@
sizeof_bind
-156997
+157089
expr
-156997
+157089
type_id
-2673
+2674
@@ -33508,7 +34165,7 @@
1
2
-156997
+157089
@@ -33524,7 +34181,7 @@
1
2
-1044
+1045
2
@@ -33622,11 +34279,11 @@
lambdas
-12641
+12513
expr
-12641
+12513
default_capture
@@ -33648,7 +34305,7 @@
1
2
-12641
+12513
@@ -33664,7 +34321,7 @@
1
2
-12641
+12513
@@ -33753,23 +34410,23 @@
lambda_capture
-21730
+21509
id
-21730
+21509
lambda
-10097
+9994
index
-112
+110
field
-20794
+21509
captured_by_reference
@@ -33781,7 +34438,7 @@
location
-14052
+13909
@@ -33795,7 +34452,7 @@
1
2
-21730
+21509
@@ -33811,7 +34468,7 @@
1
2
-21730
+21509
@@ -33827,7 +34484,7 @@
1
2
-21730
+21509
@@ -33843,7 +34500,7 @@
1
2
-21730
+21509
@@ -33859,7 +34516,7 @@
1
2
-21730
+21509
@@ -33875,7 +34532,7 @@
1
2
-21730
+21509
@@ -33891,27 +34548,27 @@
1
2
-5002
+4951
2
3
-2379
+2355
3
4
-1265
+1252
4
6
-922
+913
6
18
-527
+521
@@ -33927,27 +34584,27 @@
1
2
-5002
+4951
2
3
-2379
+2355
3
4
-1265
+1252
4
6
-922
+913
6
18
-527
+521
@@ -33963,27 +34620,27 @@
1
2
-5002
+4951
2
3
-2379
+2355
3
4
-1265
+1252
4
6
-922
+913
6
18
-527
+521
@@ -33999,12 +34656,12 @@
1
2
-9629
+9531
2
3
-467
+463
@@ -34020,7 +34677,7 @@
1
2
-10077
+9975
2
@@ -34041,27 +34698,27 @@
1
2
-5483
+5428
2
3
-2504
+2479
3
4
-1041
+1030
4
7
-823
+815
7
18
-243
+241
@@ -34327,28 +34984,28 @@
6
-138
-139
+140
+141
6
-215
-216
+220
+221
6
-399
-400
+412
+413
6
-741
-742
+773
+774
6
-1442
-1443
+1532
+1533
6
@@ -34370,7 +35027,7 @@
2
3
-85
+84
@@ -34391,7 +35048,7 @@
2
3
-46
+45
@@ -34503,12 +35160,7 @@
1
2
-20142
-
-
-2
-11
-652
+21509
@@ -34524,12 +35176,7 @@
1
2
-20142
-
-
-2
-11
-652
+21509
@@ -34545,7 +35192,7 @@
1
2
-20794
+21509
@@ -34561,7 +35208,7 @@
1
2
-20794
+21509
@@ -34577,7 +35224,7 @@
1
2
-20794
+21509
@@ -34593,12 +35240,7 @@
1
2
-20142
-
-
-2
-11
-652
+21509
@@ -34675,13 +35317,13 @@
12
-1140
-1141
+1186
+1187
6
-2015
-2016
+2111
+2112
6
@@ -34796,13 +35438,13 @@
12
-788
-789
+835
+836
6
-2367
-2368
+2462
+2463
6
@@ -34856,17 +35498,17 @@
1
2
-12668
+12539
2
6
-1061
+1050
6
68
-322
+319
@@ -34882,12 +35524,12 @@
1
2
-13116
+12982
2
68
-935
+926
@@ -34903,12 +35545,12 @@
1
2
-13511
+13374
2
8
-540
+534
@@ -34924,17 +35566,17 @@
1
2
-12668
+12539
2
6
-1061
+1050
6
68
-322
+319
@@ -34950,7 +35592,7 @@
1
2
-14032
+13889
2
@@ -34971,7 +35613,7 @@
1
2
-14052
+13909
@@ -35097,19 +35739,19 @@
stmts
-4943889
+4765273
id
-4943889
+4765273
kind
-231
+222
location
-1317792
+1266448
@@ -35123,7 +35765,7 @@
1
2
-4943889
+4765273
@@ -35139,7 +35781,7 @@
1
2
-4943889
+4765273
@@ -35155,97 +35797,97 @@
2
3
-12
+11
28
29
-12
+11
338
339
-12
+11
473
474
-12
+11
690
691
-12
+11
1560
1561
-12
+11
1757
1758
-12
+11
-2076
-2077
-12
+2080
+2081
+11
-2191
-2192
-12
+2193
+2194
+11
2707
2708
-12
+11
2748
2749
-12
+11
-2944
-2945
-12
+2946
+2947
+11
3296
3297
-12
+11
4540
4541
-12
+11
-28688
-28689
-12
+28694
+28695
+11
-53234
-53235
-12
+53369
+53370
+11
-85849
-85850
-12
+86211
+86212
+11
-98480
-98481
-12
+98824
+98825
+11
-115021
-115022
-12
+115365
+115366
+11
@@ -35261,97 +35903,97 @@
2
3
-12
+11
23
24
-12
+11
106
107
-12
+11
112
113
-12
+11
178
179
-12
+11
252
253
-12
+11
296
297
-12
+11
661
662
-12
+11
663
664
-12
+11
991
992
-12
+11
1030
1031
-12
+11
1408
1409
-12
+11
1918
1919
-12
+11
2676
2677
-12
+11
10172
10173
-12
+11
10231
10232
-12
+11
22268
22269
-12
+11
26534
26535
-12
+11
31957
31958
-12
+11
@@ -35367,32 +36009,32 @@
1
2
-780425
+747015
2
3
-178534
+173050
3
4
-120295
+116169
4
6
-106909
+103199
6
19
-102811
+99168
19
-4895
-28815
+4923
+27844
@@ -35408,12 +36050,12 @@
1
2
-1291907
+1241571
2
9
-25885
+24876
@@ -35519,15 +36161,15 @@
if_then
-524558
+524866
if_stmt
-524558
+524866
then_id
-524558
+524866
@@ -35541,7 +36183,7 @@
1
2
-524558
+524866
@@ -35557,7 +36199,7 @@
1
2
-524558
+524866
@@ -35567,15 +36209,15 @@
if_else
-148234
+148322
if_stmt
-148234
+148322
else_id
-148234
+148322
@@ -35589,7 +36231,7 @@
1
2
-148234
+148322
@@ -35605,7 +36247,7 @@
1
2
-148234
+148322
@@ -35711,15 +36353,15 @@
while_body
-32912
+31630
while_stmt
-32912
+31630
body_id
-32912
+31630
@@ -35733,7 +36375,7 @@
1
2
-32912
+31630
@@ -35749,7 +36391,7 @@
1
2
-32912
+31630
@@ -35759,15 +36401,15 @@
do_body
-149900
+149988
do_stmt
-149900
+149988
body_id
-149900
+149988
@@ -35781,7 +36423,7 @@
1
2
-149900
+149988
@@ -35797,7 +36439,7 @@
1
2
-149900
+149988
@@ -35807,11 +36449,11 @@
switch_case
-281530
+281695
switch_stmt
-55225
+55258
index
@@ -35819,7 +36461,7 @@
case_id
-281530
+281695
@@ -35833,17 +36475,17 @@
1
5
-4288
+4290
5
6
-48725
+48753
6
156
-2212
+2213
@@ -35859,17 +36501,17 @@
1
5
-4288
+4290
5
6
-48725
+48753
6
156
-2212
+2213
@@ -35987,7 +36629,7 @@
1
2
-281530
+281695
@@ -36003,7 +36645,7 @@
1
2
-281530
+281695
@@ -36013,15 +36655,15 @@
switch_body
-55225
+55258
switch_stmt
-55225
+55258
body_id
-55225
+55258
@@ -36035,7 +36677,7 @@
1
2
-55225
+55258
@@ -36051,7 +36693,7 @@
1
2
-55225
+55258
@@ -36061,15 +36703,15 @@
for_initialization
-29923
+29619
for_stmt
-29923
+29619
init_id
-29923
+29619
@@ -36083,7 +36725,7 @@
1
2
-29923
+29619
@@ -36099,7 +36741,7 @@
1
2
-29923
+29619
@@ -36109,15 +36751,15 @@
for_condition
-31782
+31459
for_stmt
-31782
+31459
condition_id
-31782
+31459
@@ -36131,7 +36773,7 @@
1
2
-31782
+31459
@@ -36147,7 +36789,7 @@
1
2
-31782
+31459
@@ -36157,15 +36799,15 @@
for_update
-29659
+29358
for_stmt
-29659
+29358
update_id
-29659
+29358
@@ -36179,7 +36821,7 @@
1
2
-29659
+29358
@@ -36195,7 +36837,7 @@
1
2
-29659
+29358
@@ -36205,15 +36847,15 @@
for_body
-32388
+32059
for_stmt
-32388
+32059
body_id
-32388
+32059
@@ -36227,7 +36869,7 @@
1
2
-32388
+32059
@@ -36243,7 +36885,7 @@
1
2
-32388
+32059
@@ -36253,19 +36895,19 @@
stmtparents
-4168362
+4131960
id
-4168362
+4131960
index
-12799
+836
parent
-1761835
+1583254
@@ -36279,7 +36921,7 @@
1
2
-4168362
+4131960
@@ -36295,7 +36937,7 @@
1
2
-4168362
+4131960
@@ -36311,52 +36953,57 @@
1
2
-4020
+182
2
3
-1094
+149
3
-4
-520
+5
+41
-4
-5
-1496
+5
+7
+72
7
-8
-1047
+9
+68
-8
-12
-830
+9
+18
+63
-12
-29
-1127
+18
+39
+64
-29
-37
-909
+39
+111
+63
-37
-74
-975
+112
+836
+63
-74
-191964
-777
+920
+136298
+63
+
+
+254134
+706946
+5
@@ -36372,52 +37019,57 @@
1
2
-4020
+182
2
3
-1094
+149
3
-4
-520
+5
+41
-4
-5
-1496
+5
+7
+72
7
-8
-1047
+9
+68
-8
-12
-830
+9
+18
+63
-12
-29
-1127
+18
+39
+64
-29
-37
-909
+39
+111
+63
-37
-74
-975
+112
+836
+63
-74
-191964
-777
+920
+136298
+63
+
+
+254134
+706946
+5
@@ -36433,32 +37085,27 @@
1
2
-1004226
+841847
2
3
-386611
+376317
3
4
-109345
+145318
4
-6
-115442
+9
+119501
-6
-17
-133389
-
-
-17
-1943
-12819
+9
+465
+100269
@@ -36474,32 +37121,27 @@
1
2
-1004226
+841847
2
3
-386611
+376317
3
4
-109345
+145318
4
-6
-115442
+9
+119501
-6
-17
-133389
-
-
-17
-1943
-12819
+9
+465
+100269
@@ -36509,26 +37151,26 @@
ishandler
-21888
+21666
block
-21888
+21666
successors
-17214104
+17224218
from
-16042874
+16052300
to
-16040997
+16050421
@@ -36542,12 +37184,12 @@
1
2
-15044821
+15053661
2
156
-998052
+998638
@@ -36563,12 +37205,12 @@
1
2
-15328885
+15337891
2
419
-712111
+712530
@@ -36578,15 +37220,15 @@
truecond
-966805
+967373
from
-966805
+967373
to
-937112
+937663
@@ -36600,7 +37242,7 @@
1
2
-966805
+967373
@@ -36616,12 +37258,12 @@
1
2
-913540
+914077
2
21
-23572
+23585
@@ -36631,15 +37273,15 @@
falsecond
-966805
+967373
from
-966805
+967373
to
-812105
+812583
@@ -36653,7 +37295,7 @@
1
2
-966805
+967373
@@ -36669,17 +37311,17 @@
1
2
-698749
+699160
2
3
-87006
+87057
3
25
-26349
+26365
@@ -36689,11 +37331,11 @@
stmt_decl_bind
-538245
+532777
stmt
-531291
+525894
num
@@ -36701,7 +37343,7 @@
decl
-538245
+532777
@@ -36715,12 +37357,12 @@
1
2
-525788
+520447
2
9
-5503
+5447
@@ -36736,12 +37378,12 @@
1
2
-525768
+520427
2
9
-5523
+5467
@@ -36859,7 +37501,7 @@
1
2
-538245
+532777
@@ -36875,7 +37517,7 @@
1
2
-538245
+532777
@@ -36885,19 +37527,19 @@
stmt_decl_entry_bind
-519726
+520031
stmt
-474524
+474803
num
-491
+492
decl_entry
-495657
+495948
@@ -36911,12 +37553,12 @@
1
2
-441842
+442101
2
274
-32682
+32702
@@ -36932,12 +37574,12 @@
1
2
-441842
+442101
2
15
-32682
+32702
@@ -37020,12 +37662,12 @@
1
2
-483634
+483918
2
85
-12022
+12029
@@ -37041,7 +37683,7 @@
1
2
-495588
+495879
2
@@ -37056,15 +37698,15 @@
blockscope
-1398451
+1347984
block
-1398451
+1347984
enclosing
-1254386
+1209602
@@ -37078,7 +37720,7 @@
1
2
-1398451
+1347984
@@ -37094,12 +37736,12 @@
1
2
-1171648
+1130157
2
509
-82738
+79444
@@ -37109,19 +37751,19 @@
jumpinfo
-366503
+366719
id
-366503
+366719
str
-6361
+6365
target
-85508
+85558
@@ -37135,7 +37777,7 @@
1
2
-366503
+366719
@@ -37151,7 +37793,7 @@
1
2
-366503
+366719
@@ -37172,12 +37814,12 @@
2
3
-3507
+3509
3
4
-835
+836
4
@@ -37187,12 +37829,12 @@
5
7
-491
+492
7
15
-500
+501
15
@@ -37213,12 +37855,12 @@
1
2
-5095
+5098
2
3
-700
+701
3
@@ -37249,32 +37891,32 @@
2
3
-21251
+21263
3
4
-7559
+7564
4
5
-3816
+3818
5
6
-39695
+39718
6
7
-11019
+11025
7
162
-1880
+1882
@@ -37290,7 +37932,7 @@
1
2
-85508
+85558
@@ -37300,19 +37942,19 @@
preprocdirects
-1456362
+1399618
id
-1456362
+1399618
kind
-158
+151
location
-1449371
+1392900
@@ -37326,7 +37968,7 @@
1
2
-1456362
+1399618
@@ -37342,7 +37984,7 @@
1
2
-1456362
+1399618
@@ -37358,67 +38000,67 @@
4
5
-12
+11
8
9
-12
+11
500
501
-12
+11
929
930
-12
+11
1740
1741
-12
+11
1873
1874
-12
+11
5235
5236
-12
+11
5497
5498
-12
+11
7551
7552
-12
+11
14073
14074
-12
+11
26464
26465
-12
+11
27121
27122
-12
+11
28787
28788
-12
+11
@@ -37434,67 +38076,67 @@
4
5
-12
+11
7
8
-12
+11
500
501
-12
+11
929
930
-12
+11
1740
1741
-12
+11
1873
1874
-12
+11
5235
5236
-12
+11
5497
5498
-12
+11
7551
7552
-12
+11
14073
14074
-12
+11
26122
26123
-12
+11
27121
27122
-12
+11
28555
28556
-12
+11
@@ -37510,12 +38152,12 @@
1
2
-1449006
+1392549
2
234
-364
+350
@@ -37531,7 +38173,7 @@
1
2
-1449371
+1392900
@@ -37541,15 +38183,15 @@
preprocpair
-416171
+399956
begin
-329749
+316901
elseelifend
-416171
+399956
@@ -37563,17 +38205,17 @@
1
2
-261442
+251256
2
3
-60257
+57909
3
53
-8048
+7735
@@ -37589,7 +38231,7 @@
1
2
-416171
+399956
@@ -37599,41 +38241,41 @@
preproctrue
-183142
+176006
branch
-183142
+176006
preprocfalse
-130022
+124956
branch
-130022
+124956
preproctext
-1062951
+1021536
id
-1062951
+1021536
head
-510168
+490290
body
-192990
+185471
@@ -37647,7 +38289,7 @@
1
2
-1062951
+1021536
@@ -37663,7 +38305,7 @@
1
2
-1062951
+1021536
@@ -37679,22 +38321,22 @@
1
2
-380522
+365696
2
3
-86130
+82774
3
19
-38420
+36923
19
752
-5094
+4895
@@ -37710,12 +38352,12 @@
1
2
-486288
+467341
2
38
-23879
+22948
@@ -37731,12 +38373,12 @@
1
2
-181282
+174219
2
64395
-11708
+11252
@@ -37752,12 +38394,12 @@
1
2
-182923
+175796
2
21671
-10067
+9674
@@ -37767,15 +38409,15 @@
includes
-321858
+309317
id
-321858
+309317
included
-60172
+57827
@@ -37789,7 +38431,7 @@
1
2
-321858
+309317
@@ -37805,37 +38447,37 @@
1
2
-29508
+28358
2
3
-9896
+9511
3
4
-5118
+4919
4
6
-5349
+5141
6
11
-4644
+4463
11
41
-4559
+4381
41
763
-1094
+1051
@@ -37845,15 +38487,15 @@
link_targets
-644
+619
id
-644
+619
binary
-644
+619
@@ -37867,7 +38509,7 @@
1
2
-644
+619
@@ -37883,7 +38525,7 @@
1
2
-644
+619
@@ -37893,15 +38535,15 @@
link_parent
-19146066
+19072521
element
-5077012
+5117392
link_target
-644
+619
@@ -37915,32 +38557,32 @@
1
2
-1439595
+1455425
2
3
-1904753
+1924496
3
4
-772523
+779113
4
6
-426238
+434215
6
-27
-383161
+28
+394382
-27
+28
45
-150740
+129758
@@ -37956,67 +38598,67 @@
2
3
-97
+93
5
557
-48
+46
-2555
-5912
-48
+2662
+6059
+46
-6322
-8240
-48
+6479
+8402
+46
-9873
-12705
-48
+10060
+12929
+46
-12725
-19568
-48
+13044
+20082
+46
-24612
-26319
-48
+24978
+26985
+46
-26410
-31881
-48
+27088
+32537
+46
-32502
-37729
-48
+33104
+38465
+46
-39858
-40615
-48
+41123
+42511
+46
-43096
-52497
-48
+44410
+55889
+46
-53360
-126529
-48
+55937
+132605
+46
-345632
-345633
-12
+362472
+362473
+11
diff --git a/cpp/ql/test/duplication-tests/constants/constants.cpp b/cpp/ql/test/duplication-tests/constants/constants.cpp
new file mode 100644
index 00000000000..c42a5981387
--- /dev/null
+++ b/cpp/ql/test/duplication-tests/constants/constants.cpp
@@ -0,0 +1,12 @@
+
+int x = int();
+float y = float();
+double z = double();
+
+/* This produces a getValueText() of 0 for R() in line 9, which is debatable. */
+struct R {};
+struct S {
+ S() : S(R()) { }
+ S(R) { }
+};
+S s;
diff --git a/cpp/ql/test/duplication-tests/constants/expr.expected b/cpp/ql/test/duplication-tests/constants/expr.expected
new file mode 100644
index 00000000000..e860f867a10
--- /dev/null
+++ b/cpp/ql/test/duplication-tests/constants/expr.expected
@@ -0,0 +1,4 @@
+| constants.cpp:2:9:2:13 | 0 | int() |
+| constants.cpp:3:11:3:17 | 0.0 | float() |
+| constants.cpp:4:12:4:19 | 0.0 | double() |
+| constants.cpp:9:11:9:13 | 0 | 0 |
diff --git a/cpp/ql/test/duplication-tests/constants/expr.ql b/cpp/ql/test/duplication-tests/constants/expr.ql
new file mode 100644
index 00000000000..09d745df82e
--- /dev/null
+++ b/cpp/ql/test/duplication-tests/constants/expr.ql
@@ -0,0 +1,4 @@
+import cpp
+
+from Expr e
+select e, e.getValueText()
diff --git a/cpp/ql/test/examples/expressions/PrintAST.expected b/cpp/ql/test/examples/expressions/PrintAST.expected
index da71c17f669..9a782825164 100644
--- a/cpp/ql/test/examples/expressions/PrintAST.expected
+++ b/cpp/ql/test/examples/expressions/PrintAST.expected
@@ -917,7 +917,7 @@ Varargs.c:
# 8| body: [Block] { ... }
# 9| 0: [DeclStmt] declaration
# 9| 0: [VariableDeclarationEntry] definition of args
-# 9| Type = [TypedefType] va_list
+# 9| Type = [CTypedefType] va_list
# 10| 1: [ExprStmt] ExprStmt
# 10| 0: [BuiltInVarArgsStart] __builtin_va_start
# 10| Type = [VoidType] void
@@ -926,7 +926,7 @@ Varargs.c:
# 10| Type = [PointerType] __va_list_tag *
# 10| ValueCategory = prvalue
# 10| expr: [VariableAccess] args
-# 10| Type = [TypedefType] va_list
+# 10| Type = [CTypedefType] va_list
# 10| ValueCategory = lvalue
# 10| 1: [VariableAccess] text
# 10| Type = [PointerType] const char *
@@ -939,7 +939,7 @@ Varargs.c:
# 11| Type = [PointerType] __va_list_tag *
# 11| ValueCategory = prvalue
# 11| expr: [VariableAccess] args
-# 11| Type = [TypedefType] va_list
+# 11| Type = [CTypedefType] va_list
# 11| ValueCategory = lvalue
# 12| 3: [ReturnStmt] return ...
macro_etc.c:
diff --git a/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.cpp b/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.cpp
index 50c02ab889e..8afad9d11db 100644
--- a/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.cpp
+++ b/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.cpp
@@ -84,3 +84,34 @@ int test_val00(S s) {
int test_val01(U u) {
return u.x;
}
+
+class MyClass {
+public:
+ void myMethod(MyClass a, MyClass &b, MyClass *c) {
+ a.x = b.y; // val, ref
+ c->x = y; // ptr, ptr
+ c->x = this->y; // ptr, ptr
+ (&b)->y = (*c).y; // ptr, val
+ }
+
+ int x, y;
+};
+
+class MyHasDestructor1 {
+public:
+ ~MyHasDestructor1() {
+ // ...
+ }
+};
+
+class MyHasDestructor2 {
+public:
+ int x;
+ MyHasDestructor1 v;
+
+ ~MyHasDestructor2() {
+ x++; // PointerFieldAccess, the `this->` is generated rather than implicit.
+
+ // ImplicitThisFieldAccess on call `v`s destructor.
+ }
+};
diff --git a/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.expected b/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.expected
index 659bb032e43..18e12fe46f6 100644
--- a/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.expected
+++ b/cpp/ql/test/library-tests/access/FieldAccess/FieldAccess.expected
@@ -16,3 +16,13 @@
| FieldAccess.cpp:77:12:77:13 | x1 | ref |
| FieldAccess.cpp:81:12:81:13 | x1 | val |
| FieldAccess.cpp:85:12:85:12 | x | val |
+| FieldAccess.cpp:91:7:91:7 | x | val |
+| FieldAccess.cpp:91:13:91:13 | y | ref |
+| FieldAccess.cpp:92:8:92:8 | x | ptr |
+| FieldAccess.cpp:92:12:92:12 | y | ptr |
+| FieldAccess.cpp:93:8:93:8 | x | ptr |
+| FieldAccess.cpp:93:18:93:18 | y | ptr |
+| FieldAccess.cpp:94:11:94:11 | y | ptr |
+| FieldAccess.cpp:94:20:94:20 | y | val |
+| FieldAccess.cpp:113:5:113:5 | x | ptr |
+| FieldAccess.cpp:116:3:116:3 | v | this |
diff --git a/cpp/ql/test/library-tests/builtins/complex/builtin.expected b/cpp/ql/test/library-tests/builtins/complex/builtin.expected
new file mode 100644
index 00000000000..c1b9b18a412
--- /dev/null
+++ b/cpp/ql/test/library-tests/builtins/complex/builtin.expected
@@ -0,0 +1,4 @@
+| complex.c:3:23:3:51 | __builtin_complex | file://:0:0:0:0 | _Complex double | complex.c:3:41:3:44 | real | file://:0:0:0:0 | double | complex.c:3:47:3:50 | imag | file://:0:0:0:0 | double |
+| complex.c:4:23:4:57 | __builtin_complex | file://:0:0:0:0 | _Complex double | complex.c:4:41:4:47 | 2.71828000000000003 | file://:0:0:0:0 | double | complex.c:4:50:4:56 | 3.141589999999999883 | file://:0:0:0:0 | double |
+| complex.c:8:22:8:52 | __builtin_complex | file://:0:0:0:0 | _Complex float | complex.c:8:40:8:44 | realf | file://:0:0:0:0 | float | complex.c:8:47:8:51 | imagf | file://:0:0:0:0 | float |
+| complex.c:9:22:9:52 | __builtin_complex | file://:0:0:0:0 | _Complex float | complex.c:9:40:9:44 | 1.230000019 | file://:0:0:0:0 | float | complex.c:9:47:9:51 | 4.559999943 | file://:0:0:0:0 | float |
diff --git a/cpp/ql/test/library-tests/builtins/complex/builtin.ql b/cpp/ql/test/library-tests/builtins/complex/builtin.ql
new file mode 100644
index 00000000000..40aa64674cf
--- /dev/null
+++ b/cpp/ql/test/library-tests/builtins/complex/builtin.ql
@@ -0,0 +1,7 @@
+import cpp
+
+from BuiltInComplexOperation bico, Expr real, Expr imag
+where
+ real = bico.getRealOperand() and
+ imag = bico.getImaginaryOperand()
+select bico, bico.getType(), real, real.getType(), imag, imag.getType()
diff --git a/cpp/ql/test/library-tests/builtins/complex/complex.c b/cpp/ql/test/library-tests/builtins/complex/complex.c
new file mode 100644
index 00000000000..e6a8554354c
--- /dev/null
+++ b/cpp/ql/test/library-tests/builtins/complex/complex.c
@@ -0,0 +1,10 @@
+
+void builtin_double(double real, double imag) {
+ _Complex double a = __builtin_complex(real, imag);
+ _Complex double b = __builtin_complex(2.71828, 3.14159);
+}
+
+void builtin_float(float realf, float imagf) {
+ _Complex float c = __builtin_complex(realf, imagf);
+ _Complex float d = __builtin_complex(1.23f, 4.56f);
+}
diff --git a/cpp/ql/test/library-tests/builtins/edg/expr.expected b/cpp/ql/test/library-tests/builtins/edg/expr.expected
index 8bae66651e7..7b4a51f022b 100644
--- a/cpp/ql/test/library-tests/builtins/edg/expr.expected
+++ b/cpp/ql/test/library-tests/builtins/edg/expr.expected
@@ -1,5 +1,5 @@
| edg.c:12:14:12:51 | (int)... | 0 | 0 |
-| edg.c:12:14:12:51 | __offsetof | 1 | 1 |
+| edg.c:12:14:12:51 | __builtin_offsetof | 1 | 1 |
| edg.c:12:49:12:50 | f2 | 0 | 0 |
| edg.c:13:14:13:45 | 0 | 0 | 0 |
| edg.c:13:14:13:45 | & ... | 0 | 0 |
diff --git a/cpp/ql/test/library-tests/complex_numbers/expr.expected b/cpp/ql/test/library-tests/complex_numbers/expr.expected
index 230793f402b..c48f9edc749 100644
--- a/cpp/ql/test/library-tests/complex_numbers/expr.expected
+++ b/cpp/ql/test/library-tests/complex_numbers/expr.expected
@@ -1,177 +1,35 @@
-| conjugation.c:3:5:3:5 | x | AnalysedExpr |
-| conjugation.c:3:5:3:5 | x | CompileTimeVariableExpr |
-| conjugation.c:3:5:3:5 | x | DefOrUse |
| conjugation.c:3:5:3:5 | x | VariableAccess |
-| conjugation.c:3:5:3:10 | ... = ... | AnalysedExpr |
| conjugation.c:3:5:3:10 | ... = ... | AssignExpr |
-| conjugation.c:3:5:3:10 | ... = ... | CompileTimeVariableExpr |
-| conjugation.c:3:5:3:10 | ... = ... | Def |
-| conjugation.c:3:5:3:10 | ... = ... | ExprInVoidContext |
-| conjugation.c:3:5:3:10 | ... = ... | NameQualifiableElement |
-| conjugation.c:3:5:3:10 | ... = ... | RangeSsaDefinition |
-| conjugation.c:3:5:3:10 | ... = ... | SsaDefinition |
-| conjugation.c:3:9:3:10 | ~ ... | AnalysedExpr |
-| conjugation.c:3:9:3:10 | ~ ... | CompileTimeVariableExpr |
| conjugation.c:3:9:3:10 | ~ ... | ConjugationExpr |
-| conjugation.c:3:9:3:10 | ~ ... | DefOrUse |
-| conjugation.c:3:9:3:10 | ~ ... | NameQualifiableElement |
-| conjugation.c:3:10:3:10 | x | AnalysedExpr |
-| conjugation.c:3:10:3:10 | x | CompileTimeVariableExpr |
-| conjugation.c:3:10:3:10 | x | Use |
| conjugation.c:3:10:3:10 | x | VariableAccess |
-| test.c:5:5:5:5 | z | AnalysedExpr |
-| test.c:5:5:5:5 | z | CompileTimeVariableExpr |
-| test.c:5:5:5:5 | z | DefOrUse |
| test.c:5:5:5:5 | z | VariableAccess |
-| test.c:5:5:5:13 | ... = ... | AnalysedExpr |
| test.c:5:5:5:13 | ... = ... | AssignExpr |
-| test.c:5:5:5:13 | ... = ... | CompileTimeVariableExpr |
-| test.c:5:5:5:13 | ... = ... | Def |
-| test.c:5:5:5:13 | ... = ... | ExprInVoidContext |
-| test.c:5:5:5:13 | ... = ... | NameQualifiableElement |
-| test.c:5:5:5:13 | ... = ... | RangeSsaDefinition |
-| test.c:5:5:5:13 | ... = ... | SsaDefinition |
-| test.c:5:9:5:9 | x | AnalysedExpr |
-| test.c:5:9:5:9 | x | CompileTimeVariableExpr |
-| test.c:5:9:5:9 | x | Use |
| test.c:5:9:5:9 | x | VariableAccess |
-| test.c:5:9:5:13 | ... * ... | AnalysedExpr |
-| test.c:5:9:5:13 | ... * ... | CompileTimeVariableExpr |
-| test.c:5:9:5:13 | ... * ... | DefOrUse |
| test.c:5:9:5:13 | ... * ... | ImaginaryMulExpr |
-| test.c:5:9:5:13 | ... * ... | NameQualifiableElement |
-| test.c:5:13:5:13 | y | AnalysedExpr |
-| test.c:5:13:5:13 | y | CompileTimeVariableExpr |
-| test.c:5:13:5:13 | y | Use |
| test.c:5:13:5:13 | y | VariableAccess |
-| test.c:6:5:6:5 | z | AnalysedExpr |
-| test.c:6:5:6:5 | z | CompileTimeVariableExpr |
-| test.c:6:5:6:5 | z | DefOrUse |
| test.c:6:5:6:5 | z | VariableAccess |
-| test.c:6:5:6:13 | ... = ... | AnalysedExpr |
| test.c:6:5:6:13 | ... = ... | AssignExpr |
-| test.c:6:5:6:13 | ... = ... | CompileTimeVariableExpr |
-| test.c:6:5:6:13 | ... = ... | Def |
-| test.c:6:5:6:13 | ... = ... | ExprInVoidContext |
-| test.c:6:5:6:13 | ... = ... | NameQualifiableElement |
-| test.c:6:5:6:13 | ... = ... | RangeSsaDefinition |
-| test.c:6:5:6:13 | ... = ... | SsaDefinition |
-| test.c:6:9:6:9 | z | AnalysedExpr |
-| test.c:6:9:6:9 | z | CompileTimeVariableExpr |
-| test.c:6:9:6:9 | z | Use |
| test.c:6:9:6:9 | z | VariableAccess |
-| test.c:6:9:6:13 | (double)... | AnalysedExpr |
| test.c:6:9:6:13 | (double)... | CStyleCast |
-| test.c:6:9:6:13 | (double)... | CompileTimeVariableExpr |
-| test.c:6:9:6:13 | (double)... | DefOrUse |
-| test.c:6:9:6:13 | (double)... | FloatingPointConversion |
-| test.c:6:9:6:13 | (double)... | NameQualifiableElement |
-| test.c:6:9:6:13 | ... / ... | AnalysedExpr |
-| test.c:6:9:6:13 | ... / ... | CompileTimeVariableExpr |
-| test.c:6:9:6:13 | ... / ... | DefOrUse |
| test.c:6:9:6:13 | ... / ... | ImaginaryDivExpr |
-| test.c:6:9:6:13 | ... / ... | NameQualifiableElement |
-| test.c:6:13:6:13 | y | AnalysedExpr |
-| test.c:6:13:6:13 | y | CompileTimeVariableExpr |
-| test.c:6:13:6:13 | y | Use |
| test.c:6:13:6:13 | y | VariableAccess |
-| test.c:7:5:7:5 | w | AnalysedExpr |
-| test.c:7:5:7:5 | w | CompileTimeVariableExpr |
-| test.c:7:5:7:5 | w | DefOrUse |
| test.c:7:5:7:5 | w | VariableAccess |
-| test.c:7:5:7:13 | ... = ... | AnalysedExpr |
| test.c:7:5:7:13 | ... = ... | AssignExpr |
-| test.c:7:5:7:13 | ... = ... | CompileTimeVariableExpr |
-| test.c:7:5:7:13 | ... = ... | Def |
-| test.c:7:5:7:13 | ... = ... | ExprInVoidContext |
-| test.c:7:5:7:13 | ... = ... | NameQualifiableElement |
-| test.c:7:5:7:13 | ... = ... | RangeSsaDefinition |
-| test.c:7:5:7:13 | ... = ... | SsaDefinition |
-| test.c:7:9:7:9 | z | AnalysedExpr |
-| test.c:7:9:7:9 | z | CompileTimeVariableExpr |
-| test.c:7:9:7:9 | z | Use |
| test.c:7:9:7:9 | z | VariableAccess |
-| test.c:7:9:7:13 | ... + ... | AnalysedExpr |
-| test.c:7:9:7:13 | ... + ... | CompileTimeVariableExpr |
-| test.c:7:9:7:13 | ... + ... | DefOrUse |
-| test.c:7:9:7:13 | ... + ... | NameQualifiableElement |
| test.c:7:9:7:13 | ... + ... | RealImaginaryAddExpr |
-| test.c:7:13:7:13 | x | AnalysedExpr |
-| test.c:7:13:7:13 | x | CompileTimeVariableExpr |
-| test.c:7:13:7:13 | x | Use |
| test.c:7:13:7:13 | x | VariableAccess |
-| test.c:8:5:8:5 | w | AnalysedExpr |
-| test.c:8:5:8:5 | w | CompileTimeVariableExpr |
-| test.c:8:5:8:5 | w | DefOrUse |
| test.c:8:5:8:5 | w | VariableAccess |
-| test.c:8:5:8:13 | ... = ... | AnalysedExpr |
| test.c:8:5:8:13 | ... = ... | AssignExpr |
-| test.c:8:5:8:13 | ... = ... | CompileTimeVariableExpr |
-| test.c:8:5:8:13 | ... = ... | Def |
-| test.c:8:5:8:13 | ... = ... | ExprInVoidContext |
-| test.c:8:5:8:13 | ... = ... | NameQualifiableElement |
-| test.c:8:5:8:13 | ... = ... | RangeSsaDefinition |
-| test.c:8:5:8:13 | ... = ... | SsaDefinition |
-| test.c:8:9:8:9 | x | AnalysedExpr |
-| test.c:8:9:8:9 | x | CompileTimeVariableExpr |
-| test.c:8:9:8:9 | x | Use |
| test.c:8:9:8:9 | x | VariableAccess |
-| test.c:8:9:8:13 | ... + ... | AnalysedExpr |
-| test.c:8:9:8:13 | ... + ... | CompileTimeVariableExpr |
-| test.c:8:9:8:13 | ... + ... | DefOrUse |
| test.c:8:9:8:13 | ... + ... | ImaginaryRealAddExpr |
-| test.c:8:9:8:13 | ... + ... | NameQualifiableElement |
-| test.c:8:13:8:13 | z | AnalysedExpr |
-| test.c:8:13:8:13 | z | CompileTimeVariableExpr |
-| test.c:8:13:8:13 | z | Use |
| test.c:8:13:8:13 | z | VariableAccess |
-| test.c:9:5:9:5 | w | AnalysedExpr |
-| test.c:9:5:9:5 | w | CompileTimeVariableExpr |
-| test.c:9:5:9:5 | w | DefOrUse |
| test.c:9:5:9:5 | w | VariableAccess |
-| test.c:9:5:9:13 | ... = ... | AnalysedExpr |
| test.c:9:5:9:13 | ... = ... | AssignExpr |
-| test.c:9:5:9:13 | ... = ... | CompileTimeVariableExpr |
-| test.c:9:5:9:13 | ... = ... | Def |
-| test.c:9:5:9:13 | ... = ... | ExprInVoidContext |
-| test.c:9:5:9:13 | ... = ... | NameQualifiableElement |
-| test.c:9:5:9:13 | ... = ... | RangeSsaDefinition |
-| test.c:9:5:9:13 | ... = ... | SsaDefinition |
-| test.c:9:9:9:9 | z | AnalysedExpr |
-| test.c:9:9:9:9 | z | CompileTimeVariableExpr |
-| test.c:9:9:9:9 | z | Use |
| test.c:9:9:9:9 | z | VariableAccess |
-| test.c:9:9:9:13 | ... - ... | AnalysedExpr |
-| test.c:9:9:9:13 | ... - ... | CompileTimeVariableExpr |
-| test.c:9:9:9:13 | ... - ... | DefOrUse |
-| test.c:9:9:9:13 | ... - ... | NameQualifiableElement |
| test.c:9:9:9:13 | ... - ... | RealImaginarySubExpr |
-| test.c:9:13:9:13 | x | AnalysedExpr |
-| test.c:9:13:9:13 | x | CompileTimeVariableExpr |
-| test.c:9:13:9:13 | x | Use |
| test.c:9:13:9:13 | x | VariableAccess |
-| test.c:10:5:10:5 | w | AnalysedExpr |
-| test.c:10:5:10:5 | w | CompileTimeVariableExpr |
-| test.c:10:5:10:5 | w | DefOrUse |
| test.c:10:5:10:5 | w | VariableAccess |
-| test.c:10:5:10:13 | ... = ... | AnalysedExpr |
| test.c:10:5:10:13 | ... = ... | AssignExpr |
-| test.c:10:5:10:13 | ... = ... | CompileTimeVariableExpr |
-| test.c:10:5:10:13 | ... = ... | Def |
-| test.c:10:5:10:13 | ... = ... | ExprInVoidContext |
-| test.c:10:5:10:13 | ... = ... | NameQualifiableElement |
-| test.c:10:5:10:13 | ... = ... | RangeSsaDefinition |
-| test.c:10:5:10:13 | ... = ... | SsaDefinition |
-| test.c:10:9:10:9 | x | AnalysedExpr |
-| test.c:10:9:10:9 | x | CompileTimeVariableExpr |
-| test.c:10:9:10:9 | x | Use |
| test.c:10:9:10:9 | x | VariableAccess |
-| test.c:10:9:10:13 | ... - ... | AnalysedExpr |
-| test.c:10:9:10:13 | ... - ... | CompileTimeVariableExpr |
-| test.c:10:9:10:13 | ... - ... | DefOrUse |
| test.c:10:9:10:13 | ... - ... | ImaginaryRealSubExpr |
-| test.c:10:9:10:13 | ... - ... | NameQualifiableElement |
-| test.c:10:13:10:13 | z | AnalysedExpr |
-| test.c:10:13:10:13 | z | CompileTimeVariableExpr |
-| test.c:10:13:10:13 | z | Use |
| test.c:10:13:10:13 | z | VariableAccess |
diff --git a/cpp/ql/test/library-tests/complex_numbers/expr.ql b/cpp/ql/test/library-tests/complex_numbers/expr.ql
index 903359f5f72..0f2e6f14d4e 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.getAQlClass()
+select e, e.getCanonicalQLClass()
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp
new file mode 100644
index 00000000000..9c3c8bc4569
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp
@@ -0,0 +1,68 @@
+int source();
+void sink(int);
+bool guarded(int);
+
+void bg_basic(int source) {
+ if (guarded(source)) {
+ sink(source); // no flow
+ } else {
+ sink(source); // flow
+ }
+}
+
+void bg_not(int source) {
+ if (!guarded(source)) {
+ sink(source); // flow
+ } else {
+ sink(source); // no flow
+ }
+}
+
+void bg_and(int source, bool arbitrary) {
+ if (guarded(source) && arbitrary) {
+ sink(source); // no flow
+ } else {
+ sink(source); // flow
+ }
+}
+
+void bg_or(int source, bool arbitrary) {
+ if (guarded(source) || arbitrary) {
+ sink(source); // flow
+ } else {
+ sink(source); // flow
+ }
+}
+
+void bg_return(int source) {
+ if (!guarded(source)) {
+ return;
+ }
+ sink(source); // no flow
+}
+
+struct XY {
+ int x, y;
+};
+
+void bg_stackstruct(XY s1, XY s2) {
+ s1.x = source();
+ if (guarded(s1.x)) {
+ sink(s1.x); // no flow
+ } else if (guarded(s1.y)) {
+ sink(s1.x); // flow
+ } else if (guarded(s2.y)) {
+ sink(s1.x); // flow
+ }
+}
+
+void bg_structptr(XY *p1, XY *p2) {
+ p1->x = source();
+ if (guarded(p1->x)) {
+ sink(p1->x); // no flow [FALSE POSITIVE in AST]
+ } else if (guarded(p1->y)) {
+ sink(p1->x); // flow [NOT DETECTED in IR]
+ } else if (guarded(p2->x)) {
+ sink(p1->x); // flow [NOT DETECTED in IR]
+ }
+}
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll b/cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll
index 02ee4d45380..a81394640ee 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll
@@ -1,6 +1,20 @@
import cpp
import semmle.code.cpp.dataflow.DataFlow
+/**
+ * A `BarrierGuard` that stops flow to all occurrences of `x` within statement
+ * S in `if (guarded(x)) S`.
+ */
+// This is tested in `BarrierGuard.cpp`.
+class TestBarrierGuard extends DataFlow::BarrierGuard {
+ TestBarrierGuard() { this.(FunctionCall).getTarget().getName() = "guarded" }
+
+ override predicate checks(Expr checked, boolean isTrue) {
+ checked = this.(FunctionCall).getArgument(0) and
+ isTrue = true
+ }
+}
+
/** Common data flow configuration to be used by tests. */
class TestAllocationConfig extends DataFlow::Configuration {
TestAllocationConfig() { this = "TestAllocationConfig" }
@@ -26,4 +40,6 @@ class TestAllocationConfig extends DataFlow::Configuration {
override predicate isBarrier(DataFlow::Node barrier) {
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier")
}
+
+ override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard }
}
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 eb5fa14e2e0..490f7e4290a 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll
@@ -1,5 +1,20 @@
import cpp
import semmle.code.cpp.ir.dataflow.DataFlow
+import semmle.code.cpp.ir.IR
+
+/**
+ * A `BarrierGuard` that stops flow to all occurrences of `x` within statement
+ * S in `if (guarded(x)) S`.
+ */
+// This is tested in `BarrierGuard.cpp`.
+class TestBarrierGuard extends DataFlow::BarrierGuard {
+ TestBarrierGuard() { this.(CallInstruction).getStaticCallTarget().getName() = "guarded" }
+
+ override predicate checks(Instruction checked, boolean isTrue) {
+ checked = this.(CallInstruction).getPositionalArgument(0) and
+ isTrue = true
+ }
+}
/** Common data flow configuration to be used by tests. */
class TestAllocationConfig extends DataFlow::Configuration {
@@ -24,4 +39,6 @@ class TestAllocationConfig extends DataFlow::Configuration {
override predicate isBarrier(DataFlow::Node barrier) {
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier")
}
+
+ override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard }
}
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 1ba5dada567..2fc2b9536f8 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/clang.cpp
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/clang.cpp
@@ -19,7 +19,7 @@ void following_pointers(
sink(sourceArray1[0]); // no flow
sink(*sourceArray1); // no flow
- sink(&sourceArray1); // no flow (since sourceArray1 is really a pointer)
+ sink(&sourceArray1); // flow (should probably be taint only)
sink(sourceStruct1.m1); // no flow
sink(sourceStruct1_ptr->m1); // no flow
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dispatch.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dispatch.cpp
new file mode 100644
index 00000000000..5e4f2f97f46
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dispatch.cpp
@@ -0,0 +1,46 @@
+int source();
+void sink(int);
+
+// This class has the opposite behavior of what the member function names suggest.
+struct Top {
+ virtual int isSource1() { return 0; }
+ virtual int isSource2() { return 0; }
+ virtual void isSink(int x) { }
+ virtual int notSource1() { return source(); }
+ virtual int notSource2() { return source(); }
+ virtual void notSink(int x) { sink(x); }
+};
+
+// This class has the correct behavior for just the functions ending in 2.
+struct Middle : Top {
+ int isSource2() override { return source(); }
+ int notSource2() override { return 0; }
+};
+
+// This class has all the behavior suggested by the function names.
+struct Bottom : Middle {
+ int isSource1() override { return source(); }
+ void isSink(int x) override { sink(x); }
+ int notSource1() override { return 0; }
+ void notSink(int x) override { }
+};
+
+void VirtualDispatch(Bottom *bottomPtr, Bottom &bottomRef) {
+ Top *topPtr = bottomPtr, &topRef = bottomRef;
+
+ sink(topPtr->isSource1()); // flow [NOT DETECTED by AST]
+ sink(topPtr->isSource2()); // flow [NOT DETECTED by AST]
+ topPtr->isSink(source()); // flow [NOT DETECTED by AST]
+
+ sink(topPtr->notSource1()); // no flow [FALSE POSITIVE]
+ sink(topPtr->notSource2()); // no flow [FALSE POSITIVE]
+ topPtr->notSink(source()); // no flow [FALSE POSITIVE]
+
+ sink(topRef.isSource1()); // flow [NOT DETECTED by AST]
+ sink(topRef.isSource2()); // flow [NOT DETECTED by AST]
+ topRef.isSink(source()); // flow [NOT DETECTED by AST]
+
+ sink(topRef.notSource1()); // no flow [FALSE POSITIVE]
+ sink(topRef.notSource2()); // no flow [FALSE POSITIVE]
+ topRef.notSink(source()); // no flow [FALSE POSITIVE]
+}
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/lambdas.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/lambdas.cpp
index 480b9016fa3..4b4f95347ef 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/lambdas.cpp
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/lambdas.cpp
@@ -43,5 +43,5 @@ void test_lambdas()
c = source();
};
e(t, u, w);
- sink(w); // flow from source() [NOT DETECTED]
+ sink(w); // flow from source()
}
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 31217a9c75c..c88a62fc2f1 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected
@@ -14,6 +14,8 @@
| example.c:24:24:24:30 | ... + ... | example.c:24:13:24:30 | ... = ... |
| 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:19:26:24 | coords | example.c:26:18:26:24 | & ... |
+| example.c:28:23:28:25 | pos | example.c:28:22:28:25 | & ... |
| test.cpp:6:12:6:17 | call to source | test.cpp:7:8:7:9 | t1 |
| test.cpp:6:12:6:17 | call to source | test.cpp:8:8:8:9 | t1 |
| test.cpp:6:12:6:17 | call to source | test.cpp:9:8:9:9 | t1 |
@@ -45,7 +47,9 @@
| test.cpp:384:10:384:13 | ref arg & ... | test.cpp:384:3:384:8 | call to memcpy |
| test.cpp:384:10:384:13 | ref arg & ... | test.cpp:384:33:384:35 | tmp |
| test.cpp:384:10:384:13 | ref arg & ... | test.cpp:385:8:385:10 | tmp |
+| test.cpp:384:11:384:13 | tmp | test.cpp:384:10:384:13 | & ... |
| test.cpp:384:17:384:23 | source1 | test.cpp:384:10:384:13 | ref arg & ... |
+| test.cpp:384:17:384:23 | source1 | test.cpp:384:16:384:23 | & ... |
| test.cpp:388:53:388:59 | source1 | test.cpp:391:17:391:23 | source1 |
| test.cpp:388:66:388:66 | b | test.cpp:393:7:393:7 | b |
| test.cpp:389:12:389:13 | 0 | test.cpp:390:19:390:21 | tmp |
@@ -53,9 +57,12 @@
| test.cpp:389:12:389:13 | 0 | test.cpp:391:33:391:35 | tmp |
| test.cpp:389:12:389:13 | 0 | test.cpp:392:8:392:10 | tmp |
| test.cpp:389:12:389:13 | 0 | test.cpp:394:10:394:12 | tmp |
+| test.cpp:390:19:390:21 | tmp | test.cpp:390:18:390:21 | & ... |
| test.cpp:391:10:391:13 | & ... | test.cpp:391:3:391:8 | call to memcpy |
| test.cpp:391:10:391:13 | ref arg & ... | test.cpp:391:3:391:8 | call to memcpy |
| test.cpp:391:10:391:13 | ref arg & ... | test.cpp:391:33:391:35 | tmp |
| test.cpp:391:10:391:13 | ref arg & ... | test.cpp:392:8:392:10 | tmp |
| test.cpp:391:10:391:13 | ref arg & ... | test.cpp:394:10:394:12 | tmp |
+| test.cpp:391:11:391:13 | tmp | test.cpp:391:10:391:13 | & ... |
| test.cpp:391:17:391:23 | source1 | test.cpp:391:10:391:13 | ref arg & ... |
+| test.cpp:391:17:391:23 | source1 | test.cpp:391:16:391:23 | & ... |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/ref.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/ref.cpp
new file mode 100644
index 00000000000..8e1ac7657ea
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/ref.cpp
@@ -0,0 +1,134 @@
+int source();
+
+template
+void sink(T);
+
+extern int arbitrary;
+
+namespace withoutFields {
+ template
+ void assign(T &lhs, T rhs) {
+ lhs = rhs;
+ }
+
+ template
+ void assignWrapper(T &lhs, T rhs) {
+ assign(lhs, rhs);
+ }
+
+ void notAssign(int &lhs, int rhs) {
+ lhs = rhs;
+ if (arbitrary) {
+ lhs = 1;
+ } else {
+ lhs = 2;
+ }
+ }
+
+ void sourceToParam(int &out) {
+ out = source();
+ if (arbitrary) {
+ out = 1;
+ }
+ }
+
+ void sourceToParamWrapper(int &out) {
+ if (arbitrary) {
+ sourceToParam(out);
+ } else {
+ out = 1;
+ }
+ }
+
+ void notSource(int &out) {
+ out = source();
+ if (arbitrary) {
+ out = 1;
+ } else {
+ out = 2;
+ }
+ }
+
+ void testRefs() {
+ int x1, x2, x3, x4;
+
+ assignWrapper(x1, source());
+ sink(x1); // flow [FALSE POSITIVE from uninitialized]
+
+ notAssign(x2, source());
+ sink(x2); // no flow [FALSE POSITIVE from uninitialized]
+
+ sourceToParamWrapper(x3);
+ sink(x3); // flow [FALSE POSITIVE from uninitialized]
+
+ notSource(x4);
+ sink(x4); // no flow [FALSE POSITIVE from uninitialized]
+ }
+}
+
+namespace withFields {
+ struct Int {
+ int val;
+ };
+
+ void assign(Int &lhs, int rhs) {
+ lhs.val = rhs;
+ }
+
+ void assignWrapper(Int &lhs, int rhs) {
+ assign(lhs, rhs);
+ }
+
+ void notAssign(Int &lhs, int rhs) {
+ lhs.val = rhs;
+ // Field flow ignores that the field is subsequently overwritten, leading
+ // to false flow here.
+ if (arbitrary) {
+ lhs.val = 1;
+ } else {
+ lhs.val = 2;
+ }
+ }
+
+ void sourceToParam(Int &out) {
+ out.val = source();
+ if (arbitrary) {
+ out.val = 1;
+ }
+ }
+
+ void sourceToParamWrapper(Int &out) {
+ if (arbitrary) {
+ sourceToParam(out);
+ } else {
+ out.val = 1;
+ }
+ }
+
+ void notSource(Int &out) {
+ out.val = source();
+ // Field flow ignores that the field is subsequently overwritten, leading
+ // to false flow here.
+ if (arbitrary) {
+ out.val = 1;
+ } else {
+ out.val = 2;
+ }
+ }
+
+ void testRefs() {
+ Int x1, x2, x3, x4;
+
+ assignWrapper(x1, source());
+ sink(x1.val); // flow
+
+ notAssign(x2, source());
+ sink(x2.val); // no flow [FALSE POSITIVE]
+
+ sourceToParamWrapper(x3);
+ sink(x3.val); // flow
+
+ notSource(x4);
+ sink(x4.val); // no flow [FALSE POSITIVE]
+ }
+}
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 60410aa3c0a..24fa6fdb5bd 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected
@@ -1,15 +1,43 @@
+| BarrierGuard.cpp:9:10:9:15 | source | BarrierGuard.cpp:5:19:5:24 | source |
+| BarrierGuard.cpp:15:10:15:15 | source | BarrierGuard.cpp:13:17:13:22 | source |
+| BarrierGuard.cpp:25:10:25:15 | source | BarrierGuard.cpp:21:17:21:22 | source |
+| BarrierGuard.cpp:31:10:31:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
+| BarrierGuard.cpp:33:10:33:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
+| BarrierGuard.cpp:53:13:53:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
+| BarrierGuard.cpp:55:13:55:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
+| BarrierGuard.cpp:62:14:62:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source |
+| BarrierGuard.cpp:64:14:64:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source |
+| BarrierGuard.cpp:66:14:66:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source |
| acrossLinkTargets.cpp:12:8:12:8 | x | acrossLinkTargets.cpp:19:27:19:32 | call to source |
| clang.cpp:18:8:18:19 | sourceArray1 | clang.cpp:12:9:12:20 | sourceArray1 |
+| clang.cpp:22:8:22:20 | & ... | clang.cpp:12:9:12:20 | sourceArray1 |
| clang.cpp:29:27:29:28 | m1 | clang.cpp:28:27:28:32 | call to source |
| 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 |
+| 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 |
+| dispatch.cpp:36:16:36:25 | call to notSource2 | dispatch.cpp:10:37:10:42 | call to source |
+| dispatch.cpp:43:15:43:24 | call to notSource1 | dispatch.cpp:9:37:9:42 | call to source |
+| dispatch.cpp:44:15:44:24 | call to notSource2 | dispatch.cpp:10:37:10:42 | call to source |
| lambdas.cpp:14:3:14:6 | t | lambdas.cpp:8:10:8:15 | call to source |
| lambdas.cpp:18:8:18:8 | call to operator() | lambdas.cpp:8:10:8:15 | call to source |
| lambdas.cpp:21:3:21:6 | t | lambdas.cpp:8:10:8:15 | call to source |
| 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 | a | 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:53:9:53:10 | x1 |
+| 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:53:13:53:14 | x2 |
+| ref.cpp:62:10:62:11 | x3 | ref.cpp:29:11:29:16 | call to source |
+| ref.cpp:62:10:62:11 | x3 | ref.cpp:53:17:53:18 | x3 |
+| ref.cpp:65:10:65:11 | x4 | ref.cpp:53:21:53:22 | x4 |
+| 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 |
+| ref.cpp:132:13:132:15 | val | ref.cpp:109:15:109:20 | call to source |
| test.cpp:7:8:7:9 | t1 | test.cpp:6:12:6:17 | call to source |
| test.cpp:9:8:9:9 | t1 | test.cpp:6:12:6:17 | call to source |
| test.cpp:10:8:10:9 | t2 | test.cpp:6:12:6:17 | 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 b89b98f42d5..9b8be3abd1e 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
@@ -1,12 +1,33 @@
+| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:62:14:62:14 | AST only |
+| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:64:14:64:14 | AST only |
+| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:66:14:66:14 | AST only |
+| clang.cpp:12:9:12:20 | clang.cpp:22:8:22:20 | AST only |
| clang.cpp:28:27:28:32 | clang.cpp:29:27:29:28 | AST only |
| clang.cpp:28:27:28:32 | clang.cpp:30:27:30:34 | AST only |
| clang.cpp:39:42:39:47 | clang.cpp:41:18:41:19 | IR 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 |
+| dispatch.cpp:22:37:22:42 | dispatch.cpp:39:15:39:23 | IR only |
+| dispatch.cpp:33:18:33:23 | dispatch.cpp:23:38:23:38 | IR only |
+| dispatch.cpp:41:17:41:22 | dispatch.cpp:23:38:23:38 | IR only |
| lambdas.cpp:8:10:8:15 | lambdas.cpp:14:3:14:6 | AST only |
| lambdas.cpp:8:10:8:15 | lambdas.cpp:18:8:18:8 | AST only |
| lambdas.cpp:8:10:8:15 | lambdas.cpp:21:3:21:6 | AST only |
| lambdas.cpp:8:10:8:15 | lambdas.cpp:29:3:29:6 | AST only |
| lambdas.cpp:8:10:8:15 | lambdas.cpp:35:8:35:8 | AST only |
| lambdas.cpp:8:10:8:15 | lambdas.cpp:41:8:41:8 | 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: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:94:15:94:20 | ref.cpp:129:13:129:15 | AST only |
+| ref.cpp:109:15:109:20 | ref.cpp:132:13:132:15 | AST only |
+| ref.cpp:122:23:122:28 | ref.cpp:123:13:123:15 | AST only |
+| ref.cpp:125:19:125:24 | ref.cpp:126:13:126:15 | AST only |
| test.cpp:89:28:89:34 | test.cpp:92:8:92:14 | IR only |
| test.cpp:100:13:100:18 | test.cpp:103:10:103:12 | AST only |
| test.cpp:109:9:109:14 | test.cpp:110:10:110:12 | 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 8ec0cf4f397..00a819948bc 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
@@ -1,3 +1,10 @@
+| BarrierGuard.cpp:9:10:9:15 | source | BarrierGuard.cpp:5:19:5:24 | source |
+| BarrierGuard.cpp:15:10:15:15 | source | BarrierGuard.cpp:13:17:13:22 | source |
+| BarrierGuard.cpp:25:10:25:15 | source | BarrierGuard.cpp:21:17:21:22 | source |
+| BarrierGuard.cpp:31:10:31:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
+| BarrierGuard.cpp:33:10:33:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
+| BarrierGuard.cpp:53:13:53:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
+| BarrierGuard.cpp:55:13:55:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
| acrossLinkTargets.cpp:12:8:12:8 | (int)... | acrossLinkTargets.cpp:19:27:19:32 | call to source |
| acrossLinkTargets.cpp:12:8:12:8 | x | acrossLinkTargets.cpp:19:27:19:32 | call to source |
| clang.cpp:18:8:18:19 | (const int *)... | clang.cpp:12:9:12:20 | sourceArray1 |
@@ -5,6 +12,18 @@
| clang.cpp:37:10:37:11 | m2 | clang.cpp:34:32:34:37 | call to source |
| clang.cpp:41:18:41:19 | m2 | clang.cpp:39:42:39:47 | call to source |
| clang.cpp:45:17:45:18 | m2 | clang.cpp:43:35:43:40 | 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:23:38:23:38 | x | dispatch.cpp:33:18:33:23 | call to source |
+| dispatch.cpp:23:38:23:38 | x | dispatch.cpp:41:17:41:22 | call to source |
+| dispatch.cpp:31:16:31:24 | call to isSource1 | dispatch.cpp:22:37:22:42 | call to source |
+| dispatch.cpp:32:16:32:24 | call to isSource2 | dispatch.cpp:16:37:16:42 | call to source |
+| dispatch.cpp:35:16:35:25 | call to notSource1 | dispatch.cpp:9:37:9:42 | call to source |
+| dispatch.cpp:36:16:36:25 | call to notSource2 | dispatch.cpp:10:37:10:42 | call to source |
+| dispatch.cpp:39:15:39:23 | call to isSource1 | dispatch.cpp:22:37:22:42 | call to source |
+| dispatch.cpp:40:15:40:23 | call to isSource2 | dispatch.cpp:16:37:16:42 | call to source |
+| dispatch.cpp:43:15:43:24 | call to notSource1 | dispatch.cpp:9:37:9:42 | call to source |
+| dispatch.cpp:44:15:44:24 | call to notSource2 | dispatch.cpp:10:37:10:42 | call to source |
| test.cpp:7:8:7:9 | t1 | test.cpp:6:12:6:17 | call to source |
| test.cpp:9:8:9:9 | t1 | test.cpp:6:12:6:17 | call to source |
| test.cpp:10:8:10:9 | t2 | test.cpp:6:12:6:17 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected
index 9cffdb99e60..b66b5704719 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/uninitialized.expected
@@ -1,3 +1,19 @@
+| ref.cpp:53:9:53:10 | x1 | ref.cpp:55:19:55:20 | x1 |
+| ref.cpp:53:9:53:10 | x1 | ref.cpp:56:10:56:11 | x1 |
+| ref.cpp:53:13:53:14 | x2 | ref.cpp:58:15:58:16 | x2 |
+| ref.cpp:53:13:53:14 | x2 | ref.cpp:59:10:59:11 | x2 |
+| ref.cpp:53:17:53:18 | x3 | ref.cpp:61:26:61:27 | x3 |
+| ref.cpp:53:17:53:18 | x3 | ref.cpp:62:10:62:11 | x3 |
+| ref.cpp:53:21:53:22 | x4 | ref.cpp:64:15:64:16 | x4 |
+| ref.cpp:53:21:53:22 | x4 | ref.cpp:65:10:65:11 | x4 |
+| ref.cpp:120:9:120:10 | x1 | ref.cpp:122:19:122:20 | x1 |
+| ref.cpp:120:9:120:10 | x1 | ref.cpp:123:10:123:11 | x1 |
+| ref.cpp:120:13:120:14 | x2 | ref.cpp:125:15:125:16 | x2 |
+| ref.cpp:120:13:120:14 | x2 | ref.cpp:126:10:126:11 | x2 |
+| ref.cpp:120:17:120:18 | x3 | ref.cpp:128:26:128:27 | x3 |
+| ref.cpp:120:17:120:18 | x3 | ref.cpp:129:10:129:11 | x3 |
+| ref.cpp:120:21:120:22 | x4 | ref.cpp:131:15:131:16 | x4 |
+| ref.cpp:120:21:120:22 | x4 | ref.cpp:132:10:132:11 | x4 |
| test.cpp:75:7:75:8 | u1 | test.cpp:76:8:76:9 | u1 |
| test.cpp:83:7:83:8 | u2 | test.cpp:84:13:84:14 | u2 |
| test.cpp:83:7:83:8 | u2 | test.cpp:85:8:85:9 | u2 |
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 e8fd28ff775..f0e099a06e6 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp
+++ b/cpp/ql/test/library-tests/dataflow/fields/by_reference.cpp
@@ -66,5 +66,5 @@ void test_setThroughNonMember() {
void test_nonMemberSetA() {
S s;
nonMemberSetA(&s, user_input());
- sink(nonMemberGetA(&s)); // flow [NOT DETECTED due to lack of flow through &]
+ sink(nonMemberGetA(&s)); // flow
}
diff --git a/cpp/ql/test/library-tests/dataflow/fields/flow.expected b/cpp/ql/test/library-tests/dataflow/fields/flow.expected
index d9bc55253f9..3b928fe961d 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/flow.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/flow.expected
@@ -125,6 +125,8 @@ edges
| aliasing.cpp:9:3:9:3 | s [post update] [m1] | aliasing.cpp:25:17:25:19 | ref arg & ... [m1] |
| aliasing.cpp:9:3:9:22 | ... = ... | aliasing.cpp:9:3:9:3 | s [post update] [m1] |
| aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:3:9:22 | ... = ... |
+| aliasing.cpp:12:25:12:25 | s [m1] | aliasing.cpp:26:19:26:20 | ref arg s2 [m1] |
+| aliasing.cpp:13:3:13:3 | s [post update] [m1] | aliasing.cpp:12:25:12:25 | s [m1] |
| aliasing.cpp:13:3:13:3 | s [post update] [m1] | aliasing.cpp:26:19:26:20 | ref arg s2 [m1] |
| aliasing.cpp:13:3:13:21 | ... = ... | aliasing.cpp:13:3:13:3 | s [post update] [m1] |
| aliasing.cpp:13:10:13:19 | call to user_input | aliasing.cpp:13:3:13:21 | ... = ... |
@@ -151,6 +153,9 @@ edges
| by_reference.cpp:62:3:62:3 | s [post update] [a] | by_reference.cpp:63:8:63:8 | s [a] |
| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:3:62:3 | s [post update] [a] |
| by_reference.cpp:63:8:63:8 | s [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
+| by_reference.cpp:68:17:68:18 | ref arg & ... [a] | by_reference.cpp:69:22:69:23 | & ... [a] |
+| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:17:68:18 | ref arg & ... [a] |
+| by_reference.cpp:69:22:69:23 | & ... [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
| complex.cpp:34:15:34:15 | b [f, a_] | complex.cpp:44:8:44:8 | b [f, a_] |
| complex.cpp:34:15:34:15 | b [f, b_] | complex.cpp:45:8:45:8 | b [f, b_] |
| complex.cpp:44:8:44:8 | b [f, a_] | complex.cpp:44:10:44:10 | f [a_] |
@@ -205,14 +210,33 @@ edges
| simple.cpp:48:9:48:9 | g [b_] | simple.cpp:26:15:26:15 | f [b_] |
| simple.cpp:51:9:51:9 | h [a_] | simple.cpp:26:15:26:15 | f [a_] |
| simple.cpp:51:9:51:9 | h [b_] | simple.cpp:26:15:26:15 | f [b_] |
+| 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] |
+| struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:24:10:24:12 | & ... [a] |
+| struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:28:5:28:7 | & ... [a] |
| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:17:20:36 | {...} [a] |
| struct_init.c:22:8:22:9 | ab [a] | struct_init.c:22:11:22:11 | a |
+| struct_init.c:24:10:24:12 | & ... [a] | struct_init.c:14:24:14:25 | ab [a] |
| struct_init.c:26:23:29:3 | {...} [nestedAB, a] | struct_init.c:31:8:31:12 | outer [nestedAB, a] |
+| struct_init.c:26:23:29:3 | {...} [nestedAB, a] | struct_init.c:36:11:36:15 | outer [nestedAB, a] |
+| struct_init.c:26:23:29:3 | {...} [pointerAB, a] | struct_init.c:33:8:33:12 | outer [pointerAB, a] |
| struct_init.c:27:5:27:23 | {...} [a] | struct_init.c:26:23:29:3 | {...} [nestedAB, a] |
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:5:27:23 | {...} [a] |
+| struct_init.c:28:5:28:7 | & ... [a] | struct_init.c:26:23:29:3 | {...} [pointerAB, a] |
| struct_init.c:31:8:31:12 | outer [nestedAB, a] | struct_init.c:31:14:31:21 | nestedAB [a] |
| struct_init.c:31:14:31:21 | nestedAB [a] | struct_init.c:31:23:31:23 | a |
+| struct_init.c:33:8:33:12 | outer [pointerAB, a] | struct_init.c:33:14:33:22 | pointerAB [a] |
+| struct_init.c:33:14:33:22 | pointerAB [a] | struct_init.c:33:25:33:25 | a |
+| struct_init.c:36:10:36:24 | & ... [a] | struct_init.c:14:24:14:25 | ab [a] |
+| struct_init.c:36:11:36:15 | outer [nestedAB, a] | struct_init.c:36:17:36:24 | nestedAB [a] |
+| struct_init.c:36:17:36:24 | nestedAB [a] | struct_init.c:36:10:36:24 | & ... [a] |
+| struct_init.c:40:17:40:36 | {...} [a] | struct_init.c:43:5:43:7 | & ... [a] |
+| struct_init.c:40:20:40:29 | call to user_input | struct_init.c:40:17:40:36 | {...} [a] |
+| struct_init.c:41:23:44:3 | {...} [pointerAB, a] | struct_init.c:46:10:46:14 | outer [pointerAB, a] |
+| struct_init.c:43:5:43:7 | & ... [a] | struct_init.c:41:23:44:3 | {...} [pointerAB, a] |
+| struct_init.c:46:10:46:14 | outer [pointerAB, a] | struct_init.c:46:16:46:24 | pointerAB [a] |
+| struct_init.c:46:16:46:24 | pointerAB [a] | struct_init.c:14:24:14:25 | ab [a] |
nodes
| A.cpp:41:15:41:21 | new | semmle.label | new |
| A.cpp:43:10:43:12 | & ... | semmle.label | & ... |
@@ -357,6 +381,7 @@ nodes
| aliasing.cpp:9:3:9:3 | s [post update] [m1] | semmle.label | s [post update] [m1] |
| aliasing.cpp:9:3:9:22 | ... = ... | semmle.label | ... = ... |
| aliasing.cpp:9:11:9:20 | call to user_input | semmle.label | call to user_input |
+| aliasing.cpp:12:25:12:25 | s [m1] | semmle.label | s [m1] |
| aliasing.cpp:13:3:13:3 | s [post update] [m1] | semmle.label | s [post update] [m1] |
| aliasing.cpp:13:3:13:21 | ... = ... | semmle.label | ... = ... |
| aliasing.cpp:13:10:13:19 | call to user_input | semmle.label | call to user_input |
@@ -390,6 +415,10 @@ nodes
| by_reference.cpp:62:25:62:34 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:63:8:63:8 | s [a] | semmle.label | s [a] |
| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | semmle.label | call to getThroughNonMember |
+| by_reference.cpp:68:17:68:18 | ref arg & ... [a] | semmle.label | ref arg & ... [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 | & ... [a] | semmle.label | & ... [a] |
| complex.cpp:34:15:34:15 | b [f, a_] | semmle.label | b [f, a_] |
| complex.cpp:34:15:34:15 | b [f, b_] | semmle.label | b [f, b_] |
| complex.cpp:44:8:44:8 | b [f, a_] | semmle.label | b [f, a_] |
@@ -450,16 +479,34 @@ nodes
| simple.cpp:48:9:48:9 | g [b_] | semmle.label | g [b_] |
| simple.cpp:51:9:51:9 | h [a_] | semmle.label | h [a_] |
| simple.cpp:51:9:51:9 | h [b_] | semmle.label | h [b_] |
+| 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 |
| struct_init.c:20:17:20:36 | {...} [a] | semmle.label | {...} [a] |
| struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input |
| struct_init.c:22:8:22:9 | ab [a] | semmle.label | ab [a] |
| struct_init.c:22:11:22:11 | a | semmle.label | a |
+| struct_init.c:24:10:24:12 | & ... [a] | semmle.label | & ... [a] |
| struct_init.c:26:23:29:3 | {...} [nestedAB, a] | semmle.label | {...} [nestedAB, a] |
+| struct_init.c:26:23:29:3 | {...} [pointerAB, a] | semmle.label | {...} [pointerAB, a] |
| struct_init.c:27:5:27:23 | {...} [a] | semmle.label | {...} [a] |
| struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input |
+| struct_init.c:28:5:28:7 | & ... [a] | semmle.label | & ... [a] |
| struct_init.c:31:8:31:12 | outer [nestedAB, a] | semmle.label | outer [nestedAB, a] |
| struct_init.c:31:14:31:21 | nestedAB [a] | semmle.label | nestedAB [a] |
| struct_init.c:31:23:31:23 | a | semmle.label | a |
+| struct_init.c:33:8:33:12 | outer [pointerAB, a] | semmle.label | outer [pointerAB, a] |
+| struct_init.c:33:14:33:22 | pointerAB [a] | semmle.label | pointerAB [a] |
+| struct_init.c:33:25:33:25 | a | semmle.label | a |
+| struct_init.c:36:10:36:24 | & ... [a] | semmle.label | & ... [a] |
+| struct_init.c:36:11:36:15 | outer [nestedAB, a] | semmle.label | outer [nestedAB, a] |
+| struct_init.c:36:17:36:24 | nestedAB [a] | semmle.label | nestedAB [a] |
+| struct_init.c:40:17:40:36 | {...} [a] | semmle.label | {...} [a] |
+| struct_init.c:40:20:40:29 | call to user_input | semmle.label | call to user_input |
+| struct_init.c:41:23:44:3 | {...} [pointerAB, a] | semmle.label | {...} [pointerAB, a] |
+| struct_init.c:43:5:43:7 | & ... [a] | semmle.label | & ... [a] |
+| struct_init.c:46:10:46:14 | outer [pointerAB, a] | semmle.label | outer [pointerAB, a] |
+| struct_init.c:46:16:46:24 | pointerAB [a] | semmle.label | pointerAB [a] |
#select
| A.cpp:43:10:43:12 | & ... | A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | & ... | & ... flows from $@ | A.cpp:41:15:41:21 | new | new |
| A.cpp:49:13:49:13 | c | A.cpp:47:12:47:18 | new | A.cpp:49:13:49:13 | c | c flows from $@ | A.cpp:47:12:47:18 | new | new |
@@ -492,6 +539,7 @@ nodes
| 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 |
| complex.cpp:44:12:44:12 | call to a | complex.cpp:55:13:55:22 | call to user_input | complex.cpp:44:12:44:12 | call to a | call to a flows from $@ | complex.cpp:55:13:55:22 | call to user_input | call to user_input |
| complex.cpp:44:12:44:12 | call to a | complex.cpp:57:13:57:22 | call to user_input | complex.cpp:44:12:44:12 | call to a | call to a flows from $@ | complex.cpp:57:13:57:22 | call to user_input | call to user_input |
| complex.cpp:45:12:45:12 | call to b | complex.cpp:56:13:56:22 | call to user_input | complex.cpp:45:12:45:12 | call to b | call to b flows from $@ | complex.cpp:56:13:56:22 | call to user_input | call to user_input |
@@ -504,5 +552,9 @@ nodes
| 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 |
+| 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 |
| 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 |
| struct_init.c:31:23:31:23 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:31:23:31:23 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input |
+| struct_init.c:33:25:33:25 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:33:25:33:25 | 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/struct_init.c b/cpp/ql/test/library-tests/dataflow/fields/struct_init.c
index 2fc4146309d..2d044cf79d8 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/struct_init.c
+++ b/cpp/ql/test/library-tests/dataflow/fields/struct_init.c
@@ -12,7 +12,7 @@ struct Outer {
};
void absink(struct AB *ab) {
- sink(ab->a); // flow x3 [NOT DETECTED]
+ sink(ab->a); // flow (three sources)
sink(ab->b); // no flow
}
@@ -30,9 +30,18 @@ int struct_init(void) {
sink(outer.nestedAB.a); // flow
sink(outer.nestedAB.b); // no flow
- sink(outer.pointerAB->a); // flow [NOT DETECTED]
+ sink(outer.pointerAB->a); // flow
sink(outer.pointerAB->b); // no flow
absink(&outer.nestedAB);
+}
+
+int struct_init2(void) {
+ struct AB ab = { user_input(), 0 };
+ struct Outer outer = {
+ { user_input(), 0 },
+ &ab,
+ };
+
absink(outer.pointerAB);
}
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 efad80dd9f9..c6f9a8a25da 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,8 @@
+| 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 | |
| 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 | |
@@ -102,12 +107,12 @@
| taint.cpp:121:10:121:11 | 1 | taint.cpp:124:13:124:14 | t2 | |
| taint.cpp:122:10:122:11 | 1 | taint.cpp:125:13:125:14 | t3 | |
| taint.cpp:123:12:123:14 | & ... | taint.cpp:129:8:129:9 | p1 | |
-| taint.cpp:123:13:123:14 | t1 | taint.cpp:123:12:123:14 | & ... | TAINT |
+| taint.cpp:123:13:123:14 | t1 | taint.cpp:123:12:123:14 | & ... | |
| taint.cpp:124:12:124:14 | & ... | taint.cpp:127:3:127:4 | p2 | |
| taint.cpp:124:12:124:14 | & ... | taint.cpp:130:8:130:9 | p2 | |
-| taint.cpp:124:13:124:14 | t2 | taint.cpp:124:12:124:14 | & ... | TAINT |
+| 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 |
+| taint.cpp:125:13:125:14 | t3 | taint.cpp:125:12:125:14 | & ... | |
| 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 |
@@ -117,7 +122,7 @@
| taint.cpp:133:7:133:9 | & ... | taint.cpp:134:8:134:9 | p3 | |
| taint.cpp:133:7:133:9 | & ... | taint.cpp:136:3:136:4 | p3 | |
| 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 |
+| 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:3:136:4 | p3 | taint.cpp:136:2:136:4 | * ... | TAINT |
| taint.cpp:136:8:136:8 | 0 | taint.cpp:136:2:136:8 | ... = ... | |
@@ -150,14 +155,14 @@
| taint.cpp:180:19:180:19 | p | taint.cpp:181:9:181:9 | p | |
| taint.cpp:181:9:181:9 | p | taint.cpp:181:8:181:9 | * ... | TAINT |
| taint.cpp:185:11:185:16 | call to source | taint.cpp:186:11:186:11 | x | |
-| taint.cpp:186:11:186:11 | x | taint.cpp:186:10:186:11 | & ... | TAINT |
+| taint.cpp:186:11:186:11 | x | taint.cpp:186:10:186:11 | & ... | |
| taint.cpp:192:23:192:28 | source | taint.cpp:194:13:194:18 | source | |
| taint.cpp:193:6:193:6 | x | taint.cpp:194:10:194:10 | x | |
| taint.cpp:193:6:193:6 | x | taint.cpp:195:7:195:7 | x | |
| taint.cpp:194:9:194:10 | & ... | taint.cpp:194:2:194:7 | call to memcpy | |
| taint.cpp:194:9:194:10 | ref arg & ... | taint.cpp:194:2:194:7 | call to memcpy | |
| taint.cpp:194:9:194:10 | ref arg & ... | taint.cpp:195:7:195:7 | x | |
-| taint.cpp:194:10:194:10 | x | taint.cpp:194:9:194:10 | & ... | TAINT |
+| taint.cpp:194:10:194:10 | x | taint.cpp:194:9:194:10 | & ... | |
| taint.cpp:194:13:194:18 | source | taint.cpp:194:9:194:10 | ref arg & ... | TAINT |
| taint.cpp:194:21:194:31 | sizeof(int) | taint.cpp:194:9:194:10 | ref arg & ... | TAINT |
| taint.cpp:207:6:207:11 | call to source | taint.cpp:207:2:207:13 | ... = ... | |
@@ -173,29 +178,31 @@
| taint.cpp:213:15:213:15 | ref arg y | taint.cpp:216:7:216:7 | y | |
| taint.cpp:213:15:213:15 | y | taint.cpp:213:12:213:12 | ref arg x | |
| taint.cpp:223:10:223:15 | call to source | taint.cpp:228:12:228:12 | t | |
-| taint.cpp:223:10:223:15 | call to source | taint.cpp:235:11:239:2 | t | |
-| taint.cpp:223:10:223:15 | call to source | taint.cpp:243:11:246:2 | t | |
+| taint.cpp:223:10:223:15 | call to source | taint.cpp:235:10:239:2 | t | |
+| taint.cpp:223:10:223:15 | call to source | taint.cpp:243:10:246:2 | t | |
| taint.cpp:223:10:223:15 | call to source | taint.cpp:253:4:253:4 | t | |
| taint.cpp:223:10:223:15 | call to source | taint.cpp:260:4:260:4 | t | |
| taint.cpp:224:9:224:10 | 0 | taint.cpp:228:15:228:15 | u | |
-| taint.cpp:224:9:224:10 | 0 | taint.cpp:235:11:239:2 | u | |
-| taint.cpp:224:9:224:10 | 0 | taint.cpp:243:11:246:2 | u | |
+| taint.cpp:224:9:224:10 | 0 | taint.cpp:235:10:239:2 | u | |
+| taint.cpp:224:9:224:10 | 0 | taint.cpp:243:10:246:2 | u | |
| taint.cpp:224:9:224:10 | 0 | taint.cpp:253:7:253:7 | u | |
| taint.cpp:224:9:224:10 | 0 | taint.cpp:260:7:260:7 | u | |
-| taint.cpp:225:9:225:10 | 0 | taint.cpp:235:11:239:2 | v | |
+| taint.cpp:225:9:225:10 | 0 | taint.cpp:235:10:239:2 | v | |
| taint.cpp:225:9:225:10 | 0 | taint.cpp:241:7:241:7 | v | |
| taint.cpp:226:9:226:10 | 0 | taint.cpp:260:10:260:10 | w | |
| taint.cpp:226:9:226:10 | 0 | taint.cpp:261:7:261:7 | w | |
+| taint.cpp:228:10:232:2 | [...](...){...} | taint.cpp:233:7:233:7 | a | |
+| taint.cpp:228:10:232:2 | {...} | taint.cpp:228:10:232:2 | [...](...){...} | |
| taint.cpp:228:11:228:11 | Unknown literal | taint.cpp:228:11:228:11 | constructor init of field t | TAINT |
| taint.cpp:228:11:228:11 | Unknown literal | taint.cpp:228:11:228:11 | constructor init of field u | TAINT |
| taint.cpp:228:11:228:11 | constructor init of field t [post-this] | taint.cpp:228:11:228:11 | constructor init of field u [pre-this] | |
| taint.cpp:228:11:228:11 | constructor init of field t [pre-this] | taint.cpp:228:11:228:11 | constructor init of field u [pre-this] | |
| taint.cpp:228:11:228:11 | this | taint.cpp:228:11:228:11 | constructor init of field t [pre-this] | |
-| taint.cpp:228:11:232:2 | [...](...){...} | taint.cpp:233:7:233:7 | a | |
-| taint.cpp:228:11:232:2 | {...} | taint.cpp:228:11:232:2 | [...](...){...} | |
| taint.cpp:228:17:228:17 | this | taint.cpp:229:3:229:6 | this | |
| taint.cpp:229:3:229:6 | this | taint.cpp:230:3:230:6 | this | |
| taint.cpp:230:3:230:6 | this | taint.cpp:231:3:231:11 | this | |
+| taint.cpp:235:10:239:2 | [...](...){...} | taint.cpp:240:2:240:2 | b | |
+| taint.cpp:235:10:239:2 | {...} | taint.cpp:235:10:239:2 | [...](...){...} | |
| taint.cpp:235:11:235:11 | Unknown literal | taint.cpp:235:11:235:11 | constructor init of field t | TAINT |
| taint.cpp:235:11:235:11 | Unknown literal | taint.cpp:235:11:235:11 | constructor init of field u | TAINT |
| taint.cpp:235:11:235:11 | Unknown literal | taint.cpp:235:11:235:11 | constructor init of field v | TAINT |
@@ -204,26 +211,116 @@
| taint.cpp:235:11:235:11 | constructor init of field u [post-this] | taint.cpp:235:11:235:11 | constructor init of field v [pre-this] | |
| taint.cpp:235:11:235:11 | constructor init of field u [pre-this] | taint.cpp:235:11:235:11 | constructor init of field v [pre-this] | |
| taint.cpp:235:11:235:11 | this | taint.cpp:235:11:235:11 | constructor init of field t [pre-this] | |
-| taint.cpp:235:11:239:2 | [...](...){...} | taint.cpp:240:2:240:2 | b | |
-| taint.cpp:235:11:239:2 | {...} | taint.cpp:235:11:239:2 | [...](...){...} | |
| 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: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 | [...](...){...} | |
| taint.cpp:243:11:243:11 | Unknown literal | taint.cpp:243:11:243:11 | constructor init of field t | TAINT |
| taint.cpp:243:11:243:11 | Unknown literal | taint.cpp:243:11:243:11 | constructor init of field u | TAINT |
| taint.cpp:243:11:243:11 | constructor init of field t [post-this] | taint.cpp:243:11:243:11 | constructor init of field u [pre-this] | |
| taint.cpp:243:11:243:11 | constructor init of field t [pre-this] | taint.cpp:243:11:243:11 | constructor init of field u [pre-this] | |
| taint.cpp:243:11:243:11 | this | taint.cpp:243:11:243:11 | constructor init of field t [pre-this] | |
-| taint.cpp:243:11:246:2 | [...](...){...} | taint.cpp:247:2:247:2 | c | |
-| taint.cpp:243:11:246:2 | {...} | taint.cpp:243:11:246:2 | [...](...){...} | |
| taint.cpp:243:15:243:15 | this | taint.cpp:244:3:244:6 | this | |
| taint.cpp:244:3:244:6 | this | taint.cpp:245:3:245:6 | this | |
| taint.cpp:249:11:252:2 | [...](...){...} | taint.cpp:253:2:253:2 | d | |
| taint.cpp:249:18:249:18 | a | taint.cpp:250:8:250:8 | a | |
| taint.cpp:249:25:249:25 | b | taint.cpp:251:8:251:8 | b | |
| taint.cpp:255:11:259:2 | [...](...){...} | taint.cpp:260:2:260:2 | e | |
+| taint.cpp:255:19:255:19 | a | taint.cpp:255:19:255:19 | a | |
| 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: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 | |
+| taint.cpp:266:12:266:12 | x | taint.cpp:268:9:268:9 | x | |
+| taint.cpp:275:6:275:11 | call to source | taint.cpp:275:2:275:13 | ... = ... | |
+| taint.cpp:275:6:275:11 | call to source | taint.cpp:280:7:280:7 | t | |
+| taint.cpp:275:6:275:11 | call to source | taint.cpp:285:9:285:9 | t | |
+| taint.cpp:275:6:275:11 | call to source | taint.cpp:286:12:286:12 | t | |
+| taint.cpp:275:6:275:11 | call to source | taint.cpp:289:7:289:7 | t | |
+| taint.cpp:276:6:276:6 | 0 | taint.cpp:276:2:276:6 | ... = ... | |
+| taint.cpp:276:6:276:6 | 0 | taint.cpp:281:7:281:7 | x | |
+| taint.cpp:277:6:277:6 | 0 | taint.cpp:277:2:277:6 | ... = ... | |
+| taint.cpp:277:6:277:6 | 0 | taint.cpp:282:7:282:7 | y | |
+| taint.cpp:278:6:278:6 | 0 | taint.cpp:278:2:278:6 | ... = ... | |
+| taint.cpp:278:6:278:6 | 0 | taint.cpp:283:7:283:7 | z | |
+| taint.cpp:278:6:278:6 | 0 | taint.cpp:287:9:287:9 | z | |
+| taint.cpp:285:6:285:7 | call to id | taint.cpp:285:2:285:10 | ... = ... | |
+| taint.cpp:285:6:285:7 | call to id | taint.cpp:290:7:290:7 | x | |
+| taint.cpp:286:6:286:7 | call to id | taint.cpp:286:2:286:14 | ... = ... | |
+| taint.cpp:286:6:286:7 | call to id | taint.cpp:291:7:291:7 | y | |
+| taint.cpp:287:6:287:7 | call to id | taint.cpp:287:2:287:10 | ... = ... | |
+| 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: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: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: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 | |
+| taint.cpp:312:28:312:28 | b | taint.cpp:316:6:316:6 | b | |
+| taint.cpp:316:6:316:6 | b | taint.cpp:316:6:316:10 | ... + ... | TAINT |
+| 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: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: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: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 | ... = ... | |
+| taint.cpp:323:10:323:10 | 1 | taint.cpp:323:6:323:10 | ... + ... | TAINT |
+| taint.cpp:330:6:330:11 | call to source | taint.cpp:330:2:330:13 | ... = ... | |
+| taint.cpp:330:6:330:11 | call to source | taint.cpp:337:7:337:7 | t | |
+| taint.cpp:330:6:330:11 | call to source | taint.cpp:344:15:344:15 | t | |
+| taint.cpp:330:6:330:11 | call to source | taint.cpp:345:15:345:15 | t | |
+| taint.cpp:330:6:330:11 | call to source | taint.cpp:346:16:346:16 | t | |
+| taint.cpp:330:6:330:11 | call to source | taint.cpp:347:16:347:16 | t | |
+| taint.cpp:330:6:330:11 | call to source | taint.cpp:348:17:348:17 | t | |
+| taint.cpp:330:6:330:11 | call to source | taint.cpp:350:7:350:7 | t | |
+| taint.cpp:331:6:331:6 | 0 | taint.cpp:331:2:331:6 | ... = ... | |
+| taint.cpp:331:6:331:6 | 0 | taint.cpp:338:7:338:7 | a | |
+| taint.cpp:331:6:331:6 | 0 | taint.cpp:344:12:344:12 | a | |
+| taint.cpp:331:6:331:6 | 0 | taint.cpp:351:7:351:7 | a | |
+| taint.cpp:332:6:332:6 | 0 | taint.cpp:332:2:332:6 | ... = ... | |
+| taint.cpp:332:6:332:6 | 0 | taint.cpp:339:7:339:7 | b | |
+| taint.cpp:332:6:332:6 | 0 | taint.cpp:345:12:345:12 | b | |
+| taint.cpp:332:6:332:6 | 0 | taint.cpp:352:7:352:7 | b | |
+| taint.cpp:333:6:333:6 | 0 | taint.cpp:333:2:333:6 | ... = ... | |
+| taint.cpp:333:6:333:6 | 0 | taint.cpp:340:7:340:7 | c | |
+| taint.cpp:333:6:333:6 | 0 | taint.cpp:346:13:346:13 | c | |
+| taint.cpp:333:6:333:6 | 0 | taint.cpp:353:7:353:7 | c | |
+| taint.cpp:334:6:334:6 | 0 | taint.cpp:334:2:334:6 | ... = ... | |
+| taint.cpp:334:6:334:6 | 0 | taint.cpp:341:7:341:7 | d | |
+| taint.cpp:334:6:334:6 | 0 | taint.cpp:347:13:347:13 | d | |
+| taint.cpp:334:6:334:6 | 0 | taint.cpp:354:7:354:7 | d | |
+| taint.cpp:335:6:335:6 | 0 | taint.cpp:335:2:335:6 | ... = ... | |
+| taint.cpp:335:6:335:6 | 0 | taint.cpp:342:7:342:7 | e | |
+| taint.cpp:335:6:335:6 | 0 | taint.cpp:348:14:348:14 | e | |
+| taint.cpp:335:6:335:6 | 0 | taint.cpp:355:7:355:7 | e | |
+| taint.cpp:344:12:344:12 | ref arg a | taint.cpp:351:7:351:7 | a | |
+| taint.cpp:344:15:344:15 | ref arg t | taint.cpp:345:15:345:15 | t | |
+| taint.cpp:344:15:344:15 | ref arg t | taint.cpp:346:16:346:16 | t | |
+| taint.cpp:344:15:344:15 | ref arg t | taint.cpp:347:16:347:16 | t | |
+| taint.cpp:344:15:344:15 | ref arg t | taint.cpp:348:17:348:17 | t | |
+| taint.cpp:344:15:344:15 | ref arg t | taint.cpp:350:7:350:7 | t | |
+| taint.cpp:345:12:345:12 | ref arg b | taint.cpp:352:7:352:7 | b | |
+| taint.cpp:346:12:346:13 | ref arg & ... | taint.cpp:353:7:353:7 | c | |
+| taint.cpp:346:13:346:13 | c | taint.cpp:346:12:346:13 | & ... | |
+| taint.cpp:347:12:347:13 | ref arg & ... | taint.cpp:354:7:354:7 | d | |
+| taint.cpp:347:13:347:13 | d | taint.cpp:347:12:347:13 | & ... | |
+| taint.cpp:348:14:348:14 | ref arg e | taint.cpp:355:7:355:7 | e | |
+| taint.cpp:348:17:348:17 | ref arg t | taint.cpp:350:7:350:7 | t | |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
index d29c45cd63e..7176072e759 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
@@ -258,5 +258,99 @@ void test_lambdas()
c = source();
};
e(t, u, w);
- sink(w); // tainted [NOT DETECTED]
+ sink(w); // tainted
+}
+
+// --- taint through return value ---
+
+int id(int x)
+{
+ return x;
+}
+
+void test_return()
+{
+ int x, y, z, t;
+
+ t = source();
+ x = 0;
+ y = 0;
+ z = 0;
+
+ sink(t); // tainted
+ sink(x);
+ sink(y);
+ sink(z);
+
+ x = id(t);
+ y = id(id(t));
+ z = id(z);
+
+ sink(t); // tainted
+ sink(x); // tainted
+ sink(y); // tainted
+ sink(z);
+}
+
+// --- taint through parameters ---
+
+void myAssign1(int &a, int &b)
+{
+ a = b;
+}
+
+void myAssign2(int &a, int b)
+{
+ a = b;
+}
+
+void myAssign3(int *a, int b)
+{
+ *a = b;
+}
+
+void myAssign4(int *a, int b)
+{
+ int c;
+
+ c = b + 1;
+ *a = c;
+}
+
+void myNotAssign(int &a, int &b)
+{
+ a = a + 1;
+ b = b + 1;
+}
+
+void test_outparams()
+{
+ int t, a, b, c, d, e;
+
+ t = source();
+ a = 0;
+ b = 0;
+ c = 0;
+ d = 0;
+ e = 0;
+
+ sink(t); // tainted
+ sink(a);
+ sink(b);
+ sink(c);
+ sink(d);
+ sink(e);
+
+ myAssign1(a, t);
+ myAssign2(b, t);
+ myAssign3(&c, t);
+ myAssign4(&d, t);
+ myNotAssign(e, t);
+
+ sink(t); // tainted
+ sink(a); // tainted
+ sink(b); // tainted
+ sink(c); // tainted [NOT DETECTED]
+ sink(d); // tainted [NOT DETECTED]
+ sink(e);
}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
index 0793bf29e19..c061b238ae1 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected
@@ -28,3 +28,12 @@
| taint.cpp:244:3:244:6 | t | taint.cpp:223:10:223:15 | call to source |
| taint.cpp:250:8:250:8 | a | taint.cpp:223:10:223:15 | call to source |
| taint.cpp:256:8:256:8 | a | taint.cpp:223:10:223:15 | call to source |
+| taint.cpp:261:7:261:7 | w | taint.cpp:258:7:258:12 | call to source |
+| taint.cpp:280:7:280:7 | t | taint.cpp:275:6:275:11 | call to source |
+| taint.cpp:289:7:289:7 | t | taint.cpp:275:6:275:11 | call to source |
+| taint.cpp:290:7:290:7 | x | taint.cpp:275:6:275:11 | call to source |
+| taint.cpp:291:7:291:7 | y | taint.cpp:275:6:275:11 | call to source |
+| taint.cpp:337:7:337:7 | t | taint.cpp:330:6:330:11 | call to source |
+| taint.cpp:350:7:350:7 | t | taint.cpp:330:6:330:11 | call to source |
+| taint.cpp:351:7:351:7 | a | taint.cpp:330:6:330:11 | call to source |
+| taint.cpp:352:7:352:7 | b | taint.cpp:330:6:330:11 | call to source |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
index 45798f7ef06..f0725b21050 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected
@@ -21,3 +21,7 @@
| taint.cpp:244:3:244:6 | taint.cpp:223:10:223:15 | AST only |
| taint.cpp:250:8:250:8 | taint.cpp:223:10:223:15 | AST only |
| taint.cpp:256:8:256:8 | taint.cpp:223:10:223:15 | AST only |
+| taint.cpp:261:7:261:7 | taint.cpp:258:7:258:12 | AST only |
+| taint.cpp:350:7:350:7 | taint.cpp:330:6:330:11 | AST only |
+| taint.cpp:351:7:351:7 | taint.cpp:330:6:330:11 | AST only |
+| taint.cpp:352:7:352:7 | taint.cpp:330:6:330:11 | AST only |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
index de2c7642e44..3336100fd48 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_ir.expected
@@ -1,7 +1,5 @@
| taint.cpp:8:8:8:13 | clean1 | taint.cpp:4:27:4:33 | source1 |
-| taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | ... = ... |
| taint.cpp:16:8:16:14 | source1 | taint.cpp:12:22:12:27 | call to source |
-| taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | ... = ... |
| taint.cpp:17:8:17:16 | ++ ... | taint.cpp:12:22:12:27 | call to source |
| taint.cpp:109:7:109:13 | access to array | taint.cpp:105:12:105:17 | call to source |
| taint.cpp:129:7:129:9 | * ... | taint.cpp:120:11:120:16 | call to source |
@@ -11,3 +9,8 @@
| taint.cpp:167:8:167:13 | call to source | taint.cpp:167:8:167:13 | call to source |
| taint.cpp:168:8:168:14 | tainted | taint.cpp:164:19:164:24 | call to source |
| taint.cpp:210:7:210:7 | x | taint.cpp:207:6:207:11 | call to source |
+| taint.cpp:280:7:280:7 | t | taint.cpp:275:6:275:11 | call to source |
+| taint.cpp:289:7:289:7 | t | taint.cpp:275:6:275:11 | call to source |
+| taint.cpp:290:7:290:7 | x | taint.cpp:275:6:275:11 | call to source |
+| taint.cpp:291:7:291:7 | y | taint.cpp:275:6:275:11 | call to source |
+| taint.cpp:337:7:337:7 | t | taint.cpp:330:6:330:11 | call to source |
diff --git a/cpp/ql/test/library-tests/defuse/definition.expected b/cpp/ql/test/library-tests/defuse/definition.expected
index a744109dc2a..35d91efd916 100644
--- a/cpp/ql/test/library-tests/defuse/definition.expected
+++ b/cpp/ql/test/library-tests/defuse/definition.expected
@@ -11,8 +11,8 @@
| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:38:20:38:25 | ... += ... |
| addressOf.cpp:40:8:40:11 | iref | addressOf.cpp:40:15:40:15 | i |
| addressOf.cpp:40:8:40:11 | iref | addressOf.cpp:42:18:42:22 | & ... |
-| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:13:47:31 | [...](...){...} |
-| addressOf.cpp:49:8:49:9 | f2 | addressOf.cpp:49:13:49:39 | [...](...){...} |
+| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:12:47:31 | [...](...){...} |
+| addressOf.cpp:49:8:49:9 | f2 | addressOf.cpp:49:12:49:39 | [...](...){...} |
| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:56:13:56:28 | {...} |
| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:57:18:57:45 | ... + ... |
| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:58:18:58:18 | a |
diff --git a/cpp/ql/test/library-tests/defuse/definitionUsePair.expected b/cpp/ql/test/library-tests/defuse/definitionUsePair.expected
index f63d34c7c13..7c0e5bd3f8e 100644
--- a/cpp/ql/test/library-tests/defuse/definitionUsePair.expected
+++ b/cpp/ql/test/library-tests/defuse/definitionUsePair.expected
@@ -8,8 +8,8 @@
| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:37:18:37:26 | & ... | addressOf.cpp:38:20:38:20 | i |
| addressOf.cpp:31:23:31:23 | i | addressOf.cpp:38:18:38:30 | ... + ... | addressOf.cpp:40:15:40:15 | i |
| addressOf.cpp:40:8:40:11 | iref | addressOf.cpp:40:15:40:15 | i | addressOf.cpp:42:19:42:22 | iref |
-| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:13:47:31 | [...](...){...} | addressOf.cpp:48:3:48:4 | f1 |
-| addressOf.cpp:49:8:49:9 | f2 | addressOf.cpp:49:13:49:39 | [...](...){...} | addressOf.cpp:50:3:50:4 | f2 |
+| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:12:47:31 | [...](...){...} | addressOf.cpp:48:3:48:4 | f1 |
+| addressOf.cpp:49:8:49:9 | f2 | addressOf.cpp:49:12:49:39 | [...](...){...} | addressOf.cpp:50:3:50:4 | f2 |
| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:56:13:56:28 | {...} | addressOf.cpp:57:19:57:19 | a |
| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:57:18:57:45 | ... + ... | addressOf.cpp:58:18:58:18 | a |
| indirect_use.cpp:20:10:20:10 | p | indirect_use.cpp:20:14:20:15 | ip | indirect_use.cpp:21:17:21:17 | p |
diff --git a/cpp/ql/test/library-tests/defuse/exprDefinition.expected b/cpp/ql/test/library-tests/defuse/exprDefinition.expected
index f5922a12c9d..ec45bd97f2b 100644
--- a/cpp/ql/test/library-tests/defuse/exprDefinition.expected
+++ b/cpp/ql/test/library-tests/defuse/exprDefinition.expected
@@ -1,5 +1,5 @@
-| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:13:47:31 | [...](...){...} | addressOf.cpp:47:13:47:31 | [...](...){...} |
-| addressOf.cpp:49:8:49:9 | f2 | addressOf.cpp:49:13:49:39 | [...](...){...} | addressOf.cpp:49:13:49:39 | [...](...){...} |
+| addressOf.cpp:47:8:47:9 | f1 | addressOf.cpp:47:12:47:31 | [...](...){...} | addressOf.cpp:47:12:47:31 | [...](...){...} |
+| addressOf.cpp:49:8:49:9 | f2 | addressOf.cpp:49:12:49:39 | [...](...){...} | addressOf.cpp:49:12:49:39 | [...](...){...} |
| addressOf.cpp:56:7:56:7 | a | addressOf.cpp:56:13:56:28 | {...} | addressOf.cpp:56:13:56:28 | {...} |
| indirect_use.cpp:20:10:20:10 | p | indirect_use.cpp:20:14:20:15 | ip | indirect_use.cpp:20:14:20:15 | ip |
| indirect_use.cpp:25:10:25:10 | p | indirect_use.cpp:25:14:25:19 | ... + ... | indirect_use.cpp:25:14:25:19 | ... + ... |
diff --git a/cpp/ql/test/library-tests/depends_initializers/InitializerCFG.expected b/cpp/ql/test/library-tests/depends_initializers/InitializerCFG.expected
index da871c2fbda..862cd295942 100644
--- a/cpp/ql/test/library-tests/depends_initializers/InitializerCFG.expected
+++ b/cpp/ql/test/library-tests/depends_initializers/InitializerCFG.expected
@@ -40,7 +40,7 @@
| template_static_instantiated.cpp:23:28:23:34 | initializer for static_int_one | myTemplateFunction |
| template_static_instantiated.cpp:23:28:23:34 | initializer for static_int_one | myTemplateFunction |
| template_static_instantiated.cpp:24:24:24:24 | initializer for static_t_1 | |
-| template_static_instantiated.cpp:24:24:24:24 | initializer for static_t_1 | |
+| template_static_instantiated.cpp:24:24:24:24 | initializer for static_t_1 | myTemplateFunction |
| template_static_instantiated.cpp:25:22:25:24 | initializer for static_t_c | myTemplateFunction |
| template_static_instantiated.cpp:25:24:25:24 | initializer for static_t_c | |
| template_static_instantiated.cpp:26:22:26:24 | initializer for static_t_v | myTemplateFunction |
@@ -72,7 +72,7 @@
| template_static_instantiated.cpp:47:29:47:35 | initializer for static_int_one | myMethod |
| template_static_instantiated.cpp:47:29:47:35 | initializer for static_int_one | myMethod |
| template_static_instantiated.cpp:48:25:48:25 | initializer for static_t_1 | |
-| template_static_instantiated.cpp:48:25:48:25 | initializer for static_t_1 | |
+| template_static_instantiated.cpp:48:25:48:25 | initializer for static_t_1 | myMethod |
| template_static_instantiated.cpp:49:23:49:25 | initializer for static_t_c | myMethod |
| template_static_instantiated.cpp:49:25:49:25 | initializer for static_t_c | |
| template_static_instantiated.cpp:50:23:50:25 | initializer for static_t_v | myMethod |
diff --git a/cpp/ql/test/library-tests/depends_initializers/template_static_instantiated.cpp b/cpp/ql/test/library-tests/depends_initializers/template_static_instantiated.cpp
index ad703eee4af..e0a4b735aee 100644
--- a/cpp/ql/test/library-tests/depends_initializers/template_static_instantiated.cpp
+++ b/cpp/ql/test/library-tests/depends_initializers/template_static_instantiated.cpp
@@ -21,7 +21,7 @@ template void myTemplateFunction()
static int static_int_c = c; // [initializer is not populated]
static int static_int_v = v; // [initializer is not populated]
static int static_int_one = one(); // [initializer is not populated]
- static T static_t_1 = 1; // [initializer is not populated]
+ static T static_t_1 = 1; // [initializer is not populated] [BUG: CPP-450]
static T static_t_c = c; // [initializer is not populated]
static T static_t_v = v; // [initializer is not populated]
static T static_t_one = one(); // [initializer is not populated]
@@ -45,7 +45,7 @@ public:
static int static_int_c = c; // [initializer is not populated]
static int static_int_v = v; // [initializer is not populated]
static int static_int_one = one(); // [initializer is not populated]
- static T static_t_1 = 1; // [initializer is not populated]
+ static T static_t_1 = 1; // [initializer is not populated] [BUG: CPP-450]
static T static_t_c = c; // [initializer is not populated]
static T static_t_v = v; // [initializer is not populated]
static T static_t_one = one(); // [initializer is not populated]
diff --git a/cpp/ql/test/library-tests/exclusions/exclusions.cpp b/cpp/ql/test/library-tests/exclusions/exclusions.cpp
new file mode 100644
index 00000000000..724904df732
--- /dev/null
+++ b/cpp/ql/test/library-tests/exclusions/exclusions.cpp
@@ -0,0 +1,9 @@
+// This is the example from the QLDoc of `isFromMacroDefinition`.
+
+void f(int);
+
+#define M(x) f(x)
+
+void useM(int y) {
+ M(y + 1);
+}
diff --git a/cpp/ql/test/library-tests/exclusions/exclusions.expected b/cpp/ql/test/library-tests/exclusions/exclusions.expected
new file mode 100644
index 00000000000..70de602eb0f
--- /dev/null
+++ b/cpp/ql/test/library-tests/exclusions/exclusions.expected
@@ -0,0 +1 @@
+| exclusions.cpp:8:3:8:10 | call to f |
diff --git a/cpp/ql/test/library-tests/exclusions/exclusions.ql b/cpp/ql/test/library-tests/exclusions/exclusions.ql
new file mode 100644
index 00000000000..94855d34d17
--- /dev/null
+++ b/cpp/ql/test/library-tests/exclusions/exclusions.ql
@@ -0,0 +1,5 @@
+import semmle.code.cpp.commons.Exclusions
+
+from Element e
+where isFromMacroDefinition(e)
+select e
diff --git a/cpp/ql/test/library-tests/exprs/conditional_decl/conditionaldeclexpr.cpp b/cpp/ql/test/library-tests/exprs/conditional_decl/conditionaldeclexpr.cpp
new file mode 100644
index 00000000000..7da7472de8a
--- /dev/null
+++ b/cpp/ql/test/library-tests/exprs/conditional_decl/conditionaldeclexpr.cpp
@@ -0,0 +1,35 @@
+
+void do_something_with(bool b)
+{
+ // ...
+}
+
+void do_something_else_with(int i)
+{
+ // ...
+}
+
+void test_if(int x, int y)
+{
+ bool b = x < y;
+ do_something_with(b);
+
+ if (bool c = x < y) { // ConditionalDeclExpr
+ do_something_with(c);
+ x++;
+ }
+}
+
+void test_while(int x, int y)
+{
+ while (int d = x - y) { // ConditionalDeclExpr
+ do_something_else_with(d);
+ }
+}
+
+void test_for(int x, int y)
+{
+ for (int i = 0; bool c = x < y; x++) { // ConditionalDeclExpr
+ do_something_with(c);
+ }
+}
diff --git a/cpp/ql/test/library-tests/exprs/conditional_decl/conditionaldeclexpr.expected b/cpp/ql/test/library-tests/exprs/conditional_decl/conditionaldeclexpr.expected
new file mode 100644
index 00000000000..db4a3518c6e
--- /dev/null
+++ b/cpp/ql/test/library-tests/exprs/conditional_decl/conditionaldeclexpr.expected
@@ -0,0 +1,3 @@
+| conditionaldeclexpr.cpp:17:7:17:20 | (condition decl) | conditionaldeclexpr.cpp:17:12:17:12 | c | conditionaldeclexpr.cpp:17:16:17:20 | ... < ... |
+| conditionaldeclexpr.cpp:25:10:25:22 | (condition decl) | conditionaldeclexpr.cpp:25:14:25:14 | d | conditionaldeclexpr.cpp:25:18:25:22 | ... - ... |
+| conditionaldeclexpr.cpp:32:19:32:32 | (condition decl) | conditionaldeclexpr.cpp:32:24:32:24 | c | conditionaldeclexpr.cpp:32:28:32:32 | ... < ... |
diff --git a/cpp/ql/test/library-tests/exprs/conditional_decl/conditionaldeclexpr.ql b/cpp/ql/test/library-tests/exprs/conditional_decl/conditionaldeclexpr.ql
new file mode 100644
index 00000000000..4010e925b2c
--- /dev/null
+++ b/cpp/ql/test/library-tests/exprs/conditional_decl/conditionaldeclexpr.ql
@@ -0,0 +1,4 @@
+import cpp
+
+from ConditionDeclExpr cde
+select cde, cde.getVariableAccess(), cde.getInitializingExpr()
diff --git a/cpp/ql/test/library-tests/fun_decl/compile1.cpp b/cpp/ql/test/library-tests/fun_decl/compile1.cpp
new file mode 100644
index 00000000000..9624d7b1c36
--- /dev/null
+++ b/cpp/ql/test/library-tests/fun_decl/compile1.cpp
@@ -0,0 +1,7 @@
+#include "fwd.h"
+
+void func1()
+{
+ classA *a = create_an_a();
+}
+
diff --git a/cpp/ql/test/library-tests/fun_decl/compile2.cpp b/cpp/ql/test/library-tests/fun_decl/compile2.cpp
new file mode 100644
index 00000000000..8fd7a38ea38
--- /dev/null
+++ b/cpp/ql/test/library-tests/fun_decl/compile2.cpp
@@ -0,0 +1,15 @@
+#include "fwd.h"
+
+class classA {
+public:
+ ~classA() { }
+};
+
+classA *create_an_a() {
+ return new classA;
+}
+
+void func2()
+{
+ classA *a = create_an_a();
+}
diff --git a/cpp/ql/test/library-tests/fun_decl/fwd.h b/cpp/ql/test/library-tests/fun_decl/fwd.h
new file mode 100644
index 00000000000..3425b1a3a23
--- /dev/null
+++ b/cpp/ql/test/library-tests/fun_decl/fwd.h
@@ -0,0 +1,3 @@
+class classA;
+classA *create_an_a(); // permits creation of a classA while it is an incomplete type.
+typedef classA classA_typedef;
diff --git a/cpp/ql/test/library-tests/fun_decl/test.expected b/cpp/ql/test/library-tests/fun_decl/test.expected
new file mode 100644
index 00000000000..cd72c33fe67
--- /dev/null
+++ b/cpp/ql/test/library-tests/fun_decl/test.expected
@@ -0,0 +1,8 @@
+| compile1.cpp:3:6:3:10 | func1 |
+| compile2.cpp:3:7:3:7 | operator= |
+| compile2.cpp:5:2:5:8 | ~classA |
+| compile2.cpp:8:9:8:19 | create_an_a |
+| compile2.cpp:12:6:12:10 | func2 |
+| file://:0:0:0:0 | operator new |
+| file://:0:0:0:0 | operator= |
+| file://:0:0:0:0 | operator= |
diff --git a/cpp/ql/test/library-tests/fun_decl/test.ql b/cpp/ql/test/library-tests/fun_decl/test.ql
new file mode 100644
index 00000000000..a1808f9558c
--- /dev/null
+++ b/cpp/ql/test/library-tests/fun_decl/test.ql
@@ -0,0 +1,4 @@
+import cpp
+
+from Function f
+select f
diff --git a/cpp/ql/test/library-tests/functions/functionaccess/FunctionAccess.cpp b/cpp/ql/test/library-tests/functions/functionaccess/FunctionAccess.cpp
new file mode 100644
index 00000000000..7543d2fc201
--- /dev/null
+++ b/cpp/ql/test/library-tests/functions/functionaccess/FunctionAccess.cpp
@@ -0,0 +1,14 @@
+
+int myTarget(int);
+
+int call(int (*target)(int), int val) {
+ return target(val);
+}
+
+void testFunctionAccess() {
+ int (*myFunctionPointer)(int) = &myTarget; // FunctionAccess
+
+ call(myFunctionPointer, 1);
+ call(myTarget, 2); // FunctionAccess
+ (&myTarget)(3); // FunctionAccess
+}
diff --git a/cpp/ql/test/library-tests/functions/functionaccess/FunctionAccess.expected b/cpp/ql/test/library-tests/functions/functionaccess/FunctionAccess.expected
new file mode 100644
index 00000000000..0dbfb6183d2
--- /dev/null
+++ b/cpp/ql/test/library-tests/functions/functionaccess/FunctionAccess.expected
@@ -0,0 +1,3 @@
+| FunctionAccess.cpp:9:36:9:43 | myTarget | FunctionAccess.cpp:2:5:2:12 | myTarget |
+| FunctionAccess.cpp:12:8:12:15 | myTarget | FunctionAccess.cpp:2:5:2:12 | myTarget |
+| FunctionAccess.cpp:13:5:13:12 | myTarget | FunctionAccess.cpp:2:5:2:12 | myTarget |
diff --git a/cpp/ql/test/library-tests/functions/functionaccess/FunctionAccess.ql b/cpp/ql/test/library-tests/functions/functionaccess/FunctionAccess.ql
new file mode 100644
index 00000000000..a22af037e37
--- /dev/null
+++ b/cpp/ql/test/library-tests/functions/functionaccess/FunctionAccess.ql
@@ -0,0 +1,4 @@
+import cpp
+
+from FunctionAccess fa
+select fa, fa.getTarget()
diff --git a/cpp/ql/test/library-tests/functions/routinetype/routinetype.cpp b/cpp/ql/test/library-tests/functions/routinetype/routinetype.cpp
new file mode 100644
index 00000000000..f9813cf5fd6
--- /dev/null
+++ b/cpp/ql/test/library-tests/functions/routinetype/routinetype.cpp
@@ -0,0 +1,4 @@
+
+using myRoutineType = int(int);
+
+myRoutineType *fp = 0;
diff --git a/cpp/ql/test/library-tests/functions/routinetype/types.expected b/cpp/ql/test/library-tests/functions/routinetype/types.expected
new file mode 100644
index 00000000000..d620bea517e
--- /dev/null
+++ b/cpp/ql/test/library-tests/functions/routinetype/types.expected
@@ -0,0 +1 @@
+| routinetype.cpp:2:7:2:19 | myRoutineType | file://:0:0:0:0 | ..()(..) | RoutineType |
diff --git a/cpp/ql/test/library-tests/functions/routinetype/types.ql b/cpp/ql/test/library-tests/functions/routinetype/types.ql
new file mode 100644
index 00000000000..058928cf5b7
--- /dev/null
+++ b/cpp/ql/test/library-tests/functions/routinetype/types.ql
@@ -0,0 +1,5 @@
+import cpp
+
+from TypedefType t, Type u
+where u = t.getBaseType()
+select t, u, concat(u.getAQlClass(), ", ")
diff --git a/cpp/ql/test/library-tests/ir/escape/points_to.expected b/cpp/ql/test/library-tests/ir/escape/points_to.expected
index f40460f4d91..ac518b5dd79 100644
--- a/cpp/ql/test/library-tests/ir/escape/points_to.expected
+++ b/cpp/ql/test/library-tests/ir/escape/points_to.expected
@@ -1,6 +1,16 @@
+| escape.cpp:111:18:111:21 | CopyValue | no_+0:0 | no_+0:0 |
| escape.cpp:115:19:115:28 | PointerAdd[4] | no_+0:0 | no_+0:0 |
+| escape.cpp:115:20:115:23 | CopyValue | no_+0:0 | no_+0:0 |
| escape.cpp:116:19:116:28 | PointerSub[4] | no_+0:0 | no_+0:0 |
+| escape.cpp:116:20:116:23 | CopyValue | no_+0:0 | no_+0:0 |
| escape.cpp:117:19:117:26 | PointerAdd[4] | no_+0:0 | no_+0:0 |
+| escape.cpp:117:23:117:26 | CopyValue | no_+0:0 | no_+0:0 |
+| escape.cpp:118:9:118:12 | CopyValue | no_+0:0 | no_+0:0 |
+| escape.cpp:120:12:120:15 | CopyValue | no_+0:0 | no_+0:0 |
+| escape.cpp:123:14:123:17 | CopyValue | no_+0:0 | no_+0:0 |
+| escape.cpp:124:15:124:18 | CopyValue | no_+0:0 | no_+0:0 |
+| escape.cpp:127:9:127:12 | CopyValue | no_+0:0 | no_+0:0 |
+| escape.cpp:129:12:129:15 | CopyValue | no_+0:0 | no_+0:0 |
| escape.cpp:134:5:134:18 | Convert | no_Array+0:0 | no_Array+0:0 |
| escape.cpp:134:11:134:18 | Convert | no_Array+0:0 | no_Array+0:0 |
| escape.cpp:135:5:135:12 | Convert | no_Array+0:0 | no_Array+0:0 |
@@ -16,32 +26,61 @@
| escape.cpp:140:21:140:32 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 |
| escape.cpp:141:27:141:27 | FieldAddress[x] | no_Point+0:0 | no_Point+0:0 |
| escape.cpp:142:14:142:14 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 |
+| escape.cpp:143:19:143:27 | CopyValue | no_Point+0:0 | no_Point+0:0 |
| escape.cpp:143:31:143:31 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 |
+| escape.cpp:144:6:144:14 | CopyValue | no_Point+0:0 | no_Point+0:0 |
| escape.cpp:144:18:144:18 | FieldAddress[y] | no_Point+4:0 | no_Point+4:0 |
+| escape.cpp:145:20:145:30 | CopyValue | no_Point+8:0 | no_Point+8:0 |
| escape.cpp:145:30:145:30 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 |
+| escape.cpp:146:5:146:18 | CopyValue | no_Point+8:0 | no_Point+8:0 |
+| escape.cpp:146:7:146:17 | CopyValue | no_Point+8:0 | no_Point+8:0 |
| escape.cpp:146:17:146:17 | FieldAddress[z] | no_Point+8:0 | no_Point+8:0 |
-| escape.cpp:149:5:149:14 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 |
-| escape.cpp:149:5:149:14 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 |
+| escape.cpp:149:5:149:14 | ConvertToNonVirtualBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 |
+| escape.cpp:149:5:149:14 | ConvertToNonVirtualBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:149:16:149:16 | FieldAddress[b] | no_Derived+0:0 | no_Derived+0:0 |
-| escape.cpp:150:18:150:27 | ConvertToBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 |
-| escape.cpp:150:18:150:27 | ConvertToBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 |
+| escape.cpp:150:18:150:27 | ConvertToNonVirtualBase[Derived : Intermediate1] | no_Derived+0:0 | no_Derived+0:0 |
+| escape.cpp:150:18:150:27 | ConvertToNonVirtualBase[Intermediate1 : Base] | no_Derived+0:0 | no_Derived+0:0 |
| escape.cpp:150:29:150:29 | FieldAddress[b] | no_Derived+0:0 | no_Derived+0:0 |
-| escape.cpp:151:5:151:14 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 |
+| escape.cpp:151:5:151:14 | ConvertToNonVirtualBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 |
| escape.cpp:151:16:151:17 | FieldAddress[i2] | no_Derived+16:0 | no_Derived+16:0 |
-| escape.cpp:152:19:152:28 | ConvertToBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 |
+| escape.cpp:152:19:152:28 | ConvertToNonVirtualBase[Derived : Intermediate2] | no_Derived+12:0 | no_Derived+12:0 |
| escape.cpp:152:30:152:31 | FieldAddress[i2] | no_Derived+16:0 | no_Derived+16:0 |
+| escape.cpp:155:17:155:30 | CopyValue | no_ssa_addrOf+0:0 | no_ssa_addrOf+0:0 |
| escape.cpp:155:17:155:30 | Store | no_ssa_addrOf+0:0 | no_ssa_addrOf+0:0 |
+| escape.cpp:158:17:158:28 | CopyValue | no_ssa_refTo+0:0 | no_ssa_refTo+0:0 |
| escape.cpp:158:17:158:28 | Store | no_ssa_refTo+0:0 | no_ssa_refTo+0:0 |
| escape.cpp:161:19:161:42 | Convert | no_ssa_refToArrayElement+0:0 | no_ssa_refToArrayElement+0:0 |
+| escape.cpp:161:19:161:45 | CopyValue | no_ssa_refToArrayElement+20:0 | no_ssa_refToArrayElement+20:0 |
| escape.cpp:161:19:161:45 | PointerAdd[4] | no_ssa_refToArrayElement+20:0 | no_ssa_refToArrayElement+20:0 |
| escape.cpp:161:19:161:45 | Store | no_ssa_refToArrayElement+20:0 | no_ssa_refToArrayElement+20:0 |
+| escape.cpp:164:24:164:40 | CopyValue | no_ssa_refToArray+0:0 | no_ssa_refToArray+0:0 |
| escape.cpp:164:24:164:40 | Store | no_ssa_refToArray+0:0 | no_ssa_refToArray+0:0 |
+| escape.cpp:167:19:167:28 | CopyValue | passByPtr+0:0 | passByPtr+0:0 |
+| escape.cpp:170:21:170:29 | CopyValue | passByRef+0:0 | passByRef+0:0 |
+| escape.cpp:173:22:173:38 | CopyValue | no_ssa_passByPtr+0:0 | no_ssa_passByPtr+0:0 |
+| escape.cpp:176:24:176:39 | CopyValue | no_ssa_passByRef+0:0 | no_ssa_passByRef+0:0 |
+| escape.cpp:179:22:179:42 | CopyValue | no_ssa_passByPtr_ret+0:0 | no_ssa_passByPtr_ret+0:0 |
+| escape.cpp:182:24:182:43 | CopyValue | no_ssa_passByRef_ret+0:0 | no_ssa_passByRef_ret+0:0 |
+| escape.cpp:185:30:185:40 | CopyValue | passByPtr2+0:0 | passByPtr2+0:0 |
+| escape.cpp:188:32:188:41 | CopyValue | passByRef2+0:0 | passByRef2+0:0 |
| escape.cpp:191:30:191:42 | Call | none | passByPtr3+0:0 |
+| escape.cpp:191:44:191:54 | CopyValue | passByPtr3+0:0 | passByPtr3+0:0 |
| escape.cpp:194:32:194:46 | Call | none | passByRef3+0:0 |
+| escape.cpp:194:32:194:59 | CopyValue | none | passByRef3+0:0 |
+| escape.cpp:194:48:194:57 | CopyValue | passByRef3+0:0 | passByRef3+0:0 |
+| escape.cpp:199:17:199:34 | CopyValue | no_ssa_passByPtr4+0:0 | no_ssa_passByPtr4+0:0 |
+| escape.cpp:199:37:199:54 | CopyValue | no_ssa_passByPtr5+0:0 | no_ssa_passByPtr5+0:0 |
| escape.cpp:202:5:202:19 | Call | none | passByRef6+0:0 |
+| escape.cpp:202:5:202:32 | CopyValue | none | passByRef6+0:0 |
+| escape.cpp:202:21:202:30 | CopyValue | passByRef6+0:0 | passByRef6+0:0 |
| escape.cpp:205:5:205:19 | Call | none | no_ssa_passByRef7+0:0 |
+| escape.cpp:205:5:205:39 | CopyValue | none | no_ssa_passByRef7+0:0 |
+| escape.cpp:205:21:205:37 | CopyValue | no_ssa_passByRef7+0:0 | no_ssa_passByRef7+0:0 |
| escape.cpp:209:14:209:25 | Call | none | no_ssa_c+0:0 |
+| escape.cpp:217:14:217:16 | CopyValue | c2+0:0 | c2+0:0 |
| escape.cpp:221:8:221:19 | Call | none | c3+0:0 |
| escape.cpp:225:17:225:28 | Call | none | c4+0:0 |
| escape.cpp:247:2:247:27 | Store | condEscape1+0:0 | condEscape1+0:0 |
+| escape.cpp:247:16:247:27 | CopyValue | condEscape1+0:0 | condEscape1+0:0 |
| escape.cpp:249:9:249:34 | Store | condEscape2+0:0 | condEscape2+0:0 |
+| escape.cpp:249:23:249:34 | CopyValue | condEscape2+0:0 | condEscape2+0:0 |
diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected
index efea5c27210..aa56b05163f 100644
--- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected
+++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected
@@ -67,31 +67,7 @@ bad_asts.cpp:
# 5| params:
#-----| 0: [Parameter] p#0
#-----| Type = [RValueReferenceType] S &&
-# 9| [MemberFunction] int Bad::S::MemberFunction(int)
-# 9| params:
-# 9| 0: [Parameter] y
-# 9| Type = [IntType] int
-# 9| body: [Block] { ... }
-# 10| 0: [ReturnStmt] return ...
-# 10| 0: [AddExpr] ... + ...
-# 10| Type = [IntType] int
-# 10| ValueCategory = prvalue
-# 10| 0: [AddExpr] ... + ...
-# 10| Type = [IntType] int
-# 10| ValueCategory = prvalue
-# 10| 0: [Literal] Unknown literal
-# 10| Type = [IntType] int
-# 10| ValueCategory = prvalue
-# 10| 1: [PointerFieldAccess] x
-# 10| Type = [IntType] int
-# 10| ValueCategory = prvalue(load)
-#-----| -1: [ThisExpr] this
-#-----| Type = [PointerType] S *
-#-----| ValueCategory = prvalue(load)
-# 10| 1: [VariableAccess] y
-# 10| Type = [IntType] int
-# 10| ValueCategory = prvalue(load)
-# 9| [TopLevelFunction] int MemberFunction(int)
+# 9| [FunctionTemplateInstantiation,MemberFunction] int Bad::S::MemberFunction(int)
# 9| params:
# 9| 0: [Parameter] y
# 9| Type = [IntType] int
@@ -116,6 +92,31 @@ bad_asts.cpp:
# 10| 1: [VariableAccess] y
# 10| Type = [IntType] int
# 10| ValueCategory = prvalue(load)
+# 9| [MemberFunction,TemplateFunction] int Bad::S::MemberFunction(int)
+# 9| params:
+# 9| 0: [Parameter] y
+# 9| Type = [IntType] int
+# 9| body: [Block] { ... }
+# 10| 0: [ReturnStmt] return ...
+# 10| 0: [AddExpr] ... + ...
+# 10| Type = [IntType] int
+# 10| ValueCategory = prvalue
+# 10| 0: [AddExpr] ... + ...
+# 10| Type = [IntType] int
+# 10| ValueCategory = prvalue
+# 10| 0: [Literal] t
+# 10| Type = [IntType] int
+# 10| Value = [Literal] t
+# 10| ValueCategory = prvalue
+# 10| 1: [PointerFieldAccess] x
+# 10| Type = [IntType] int
+# 10| ValueCategory = prvalue(load)
+#-----| -1: [ThisExpr] this
+#-----| Type = [PointerType] S *
+#-----| ValueCategory = prvalue(load)
+# 10| 1: [VariableAccess] y
+# 10| Type = [IntType] int
+# 10| ValueCategory = prvalue(load)
# 14| [TopLevelFunction] void Bad::CallBadMemberFunction()
# 14| params:
# 14| body: [Block] { ... }
@@ -3672,7 +3673,7 @@ ir.cpp:
# 560| [TopLevelFunction] int EnumSwitch(E)
# 560| params:
# 560| 0: [Parameter] e
-# 560| Type = [TypedefType] E
+# 560| Type = [CTypedefType] E
# 560| body: [Block] { ... }
# 561| 0: [SwitchStmt] switch (...) ...
# 561| 0: [CStyleCast] (int)...
@@ -3680,7 +3681,7 @@ ir.cpp:
# 561| Type = [IntType] int
# 561| ValueCategory = prvalue
# 561| expr: [VariableAccess] e
-# 561| Type = [TypedefType] E
+# 561| Type = [CTypedefType] E
# 561| ValueCategory = prvalue(load)
# 561| 1: [Block] { ... }
# 562| 0: [SwitchCase] case ...:
@@ -3731,7 +3732,7 @@ ir.cpp:
# 572| Type = [ArrayType] char[32]
# 572| init: [Initializer] initializer for a_pad
# 572| expr:
-# 572| Type = [ArrayType] const char[1]
+# 572| Type = [ArrayType] const char[32]
# 572| Value = [StringLiteral] ""
# 572| ValueCategory = lvalue
# 573| 1: [DeclStmt] declaration
@@ -6191,19 +6192,19 @@ ir.cpp:
# 915| [Operator,TopLevelFunction] void* operator new(size_t, float)
# 915| params:
# 915| 0: [Parameter] p#0
-# 915| Type = [Size_t,TypedefType] size_t
+# 915| Type = [CTypedefType,Size_t] size_t
# 915| 1: [Parameter] p#1
# 915| Type = [FloatType] float
# 916| [Operator,TopLevelFunction] void* operator new[](size_t, float)
# 916| params:
# 916| 0: [Parameter] p#0
-# 916| Type = [Size_t,TypedefType] size_t
+# 916| Type = [CTypedefType,Size_t] size_t
# 916| 1: [Parameter] p#1
# 916| Type = [FloatType] float
# 917| [Operator,TopLevelFunction] void* operator new(size_t, std::align_val_t, float)
# 917| params:
# 917| 0: [Parameter] p#0
-# 917| Type = [Size_t,TypedefType] size_t
+# 917| Type = [CTypedefType,Size_t] size_t
# 917| 1: [Parameter] p#1
# 917| Type = [ScopedEnum] align_val_t
# 917| 2: [Parameter] p#2
@@ -6211,7 +6212,7 @@ ir.cpp:
# 918| [Operator,TopLevelFunction] void* operator new[](size_t, std::align_val_t, float)
# 918| params:
# 918| 0: [Parameter] p#0
-# 918| Type = [Size_t,TypedefType] size_t
+# 918| Type = [CTypedefType,Size_t] size_t
# 918| 1: [Parameter] p#1
# 918| Type = [ScopedEnum] align_val_t
# 918| 2: [Parameter] p#2
@@ -6255,23 +6256,23 @@ ir.cpp:
# 926| [MemberFunction] void* SizedDealloc::operator new(size_t)
# 926| params:
# 926| 0: [Parameter] p#0
-# 926| Type = [Size_t,TypedefType] size_t
+# 926| Type = [CTypedefType,Size_t] size_t
# 927| [MemberFunction] void* SizedDealloc::operator new[](size_t)
# 927| params:
# 927| 0: [Parameter] p#0
-# 927| Type = [Size_t,TypedefType] size_t
+# 927| Type = [CTypedefType,Size_t] size_t
# 928| [MemberFunction] void SizedDealloc::operator delete(void*, size_t)
# 928| params:
# 928| 0: [Parameter] p#0
# 928| Type = [VoidPointerType] void *
# 928| 1: [Parameter] p#1
-# 928| Type = [Size_t,TypedefType] size_t
+# 928| Type = [CTypedefType,Size_t] size_t
# 929| [MemberFunction] void SizedDealloc::operator delete[](void*, size_t)
# 929| params:
# 929| 0: [Parameter] p#0
# 929| Type = [VoidPointerType] void *
# 929| 1: [Parameter] p#1
-# 929| Type = [Size_t,TypedefType] size_t
+# 929| Type = [CTypedefType,Size_t] size_t
# 932| [CopyAssignmentOperator] Overaligned& Overaligned::operator=(Overaligned const&)
# 932| params:
#-----| 0: [Parameter] p#0
@@ -6893,9 +6894,6 @@ ir.cpp:
# 1029| params:
#-----| 0: [Parameter] p#0
#-----| Type = [RValueReferenceType] lambda [] type at line 1029, col. 12 &&
-# 1029| initializations:
-# 1029| body: [Block] { ... }
-# 1029| 0: [ReturnStmt] return ...
# 1029| [Constructor] void (lambda [] type at line 1029, col. 12)::(constructor)()
# 1029| params:
# 1029| [MemberFunction] void (lambda [] type at line 1029, col. 12)::_FUN()
@@ -6995,24 +6993,18 @@ ir.cpp:
# 1036| 0: [VariableDeclarationEntry] definition of lambda_val
# 1036| Type = [Closure,LocalClass] decltype([...](...){...})
# 1036| init: [Initializer] initializer for lambda_val
-# 1036| expr: [ConstructorCall] call to (constructor)
-# 1036| Type = [VoidType] void
+# 1036| expr: [LambdaExpression] [...](...){...}
+# 1036| Type = [Closure,LocalClass] decltype([...](...){...})
# 1036| ValueCategory = prvalue
-# 1036| 0: [ReferenceToExpr] (reference to)
-# 1036| Type = [LValueReferenceType] lambda [] type at line 1036, col. 21 &
+# 1036| 0: [ClassAggregateLiteral] {...}
+# 1036| Type = [Closure,LocalClass] decltype([...](...){...})
# 1036| ValueCategory = prvalue
-# 1036| expr: [LambdaExpression] [...](...){...}
-# 1036| Type = [Closure,LocalClass] decltype([...](...){...})
-# 1036| ValueCategory = xvalue
-# 1036| 0: [ClassAggregateLiteral] {...}
-# 1036| Type = [Closure,LocalClass] decltype([...](...){...})
-# 1036| ValueCategory = prvalue
-#-----| .s: [ConstructorCall] call to String
-#-----| Type = [VoidType] void
-#-----| ValueCategory = prvalue
-#-----| .x: [VariableAccess] x
-#-----| Type = [IntType] int
-#-----| ValueCategory = prvalue(load)
+#-----| .s: [ConstructorCall] call to String
+#-----| Type = [VoidType] void
+#-----| ValueCategory = prvalue
+#-----| .x: [VariableAccess] x
+#-----| Type = [IntType] int
+#-----| ValueCategory = prvalue(load)
# 1037| 5: [ExprStmt] ExprStmt
# 1037| 0: [FunctionCall] call to operator()
# 1037| Type = [PlainCharType] char
@@ -7076,21 +7068,15 @@ ir.cpp:
# 1040| 0: [VariableDeclarationEntry] definition of lambda_val_explicit
# 1040| Type = [Closure,LocalClass] decltype([...](...){...})
# 1040| init: [Initializer] initializer for lambda_val_explicit
-# 1040| expr: [ConstructorCall] call to (constructor)
-# 1040| Type = [VoidType] void
+# 1040| expr: [LambdaExpression] [...](...){...}
+# 1040| Type = [Closure,LocalClass] decltype([...](...){...})
# 1040| ValueCategory = prvalue
-# 1040| 0: [ReferenceToExpr] (reference to)
-# 1040| Type = [LValueReferenceType] lambda [] type at line 1040, col. 30 &
+# 1040| 0: [ClassAggregateLiteral] {...}
+# 1040| Type = [Closure,LocalClass] decltype([...](...){...})
# 1040| ValueCategory = prvalue
-# 1040| expr: [LambdaExpression] [...](...){...}
-# 1040| Type = [Closure,LocalClass] decltype([...](...){...})
-# 1040| ValueCategory = xvalue
-# 1040| 0: [ClassAggregateLiteral] {...}
-# 1040| Type = [Closure,LocalClass] decltype([...](...){...})
-# 1040| ValueCategory = prvalue
-#-----| .s: [ConstructorCall] call to String
-#-----| Type = [VoidType] void
-#-----| ValueCategory = prvalue
+#-----| .s: [ConstructorCall] call to String
+#-----| Type = [VoidType] void
+#-----| ValueCategory = prvalue
# 1041| 9: [ExprStmt] ExprStmt
# 1041| 0: [FunctionCall] call to operator()
# 1041| Type = [PlainCharType] char
@@ -7238,9 +7224,6 @@ ir.cpp:
# 1032| params:
#-----| 0: [Parameter] p#0
#-----| Type = [RValueReferenceType] lambda [] type at line 1032, col. 23 &&
-# 1032| initializations:
-# 1032| body: [Block] { ... }
-# 1032| 0: [ReturnStmt] return ...
# 1032| [Constructor] void (void Lambda(int, String const&))::(lambda [] type at line 1032, col. 23)::(constructor)()
# 1032| params:
# 1032| [MemberFunction] char (void Lambda(int, String const&))::(lambda [] type at line 1032, col. 23)::_FUN(float)
@@ -7276,21 +7259,6 @@ ir.cpp:
# 1034| params:
#-----| 0: [Parameter] p#0
#-----| Type = [RValueReferenceType] lambda [] type at line 1034, col. 21 &&
-# 1034| initializations:
-# 1034| 0: [ConstructorFieldInit] constructor init of field s
-# 1034| Type = [LValueReferenceType] const String &
-# 1034| ValueCategory = prvalue
-# 1034| 0: [Literal] Unknown literal
-# 1034| Type = [LValueReferenceType] const String &
-# 1034| ValueCategory = prvalue
-# 1034| 1: [ConstructorFieldInit] constructor init of field x
-# 1034| Type = [LValueReferenceType] int &
-# 1034| ValueCategory = prvalue
-# 1034| 0: [Literal] Unknown literal
-# 1034| Type = [LValueReferenceType] int &
-# 1034| ValueCategory = prvalue
-# 1034| body: [Block] { ... }
-# 1034| 0: [ReturnStmt] return ...
# 1034| [Constructor] void (void Lambda(int, String const&))::(lambda [] type at line 1034, col. 21)::(constructor)()
# 1034| params:
# 1034| [ConstMemberFunction] char (void Lambda(int, String const&))::(lambda [] type at line 1034, col. 21)::operator()(float) const
@@ -7335,21 +7303,6 @@ ir.cpp:
# 1036| params:
#-----| 0: [Parameter] p#0
#-----| Type = [RValueReferenceType] lambda [] type at line 1036, col. 21 &&
-# 1036| initializations:
-# 1036| 0: [ConstructorFieldInit] constructor init of field s
-# 1036| Type = [SpecifiedType] const String
-# 1036| ValueCategory = prvalue
-# 1036| 0: [ConstructorCall] call to String
-# 1036| Type = [VoidType] void
-# 1036| ValueCategory = prvalue
-# 1036| 1: [ConstructorFieldInit] constructor init of field x
-# 1036| Type = [IntType] int
-# 1036| ValueCategory = prvalue
-# 1036| 0: [Literal] Unknown literal
-# 1036| Type = [IntType] int
-# 1036| ValueCategory = prvalue
-# 1036| body: [Block] { ... }
-# 1036| 0: [ReturnStmt] return ...
# 1036| [Constructor] void (void Lambda(int, String const&))::(lambda [] type at line 1036, col. 21)::(constructor)()
# 1036| params:
# 1036| [Destructor] void (void Lambda(int, String const&))::(lambda [] type at line 1036, col. 21)::~()
@@ -7402,15 +7355,6 @@ ir.cpp:
# 1038| params:
#-----| 0: [Parameter] p#0
#-----| Type = [RValueReferenceType] lambda [] type at line 1038, col. 30 &&
-# 1038| initializations:
-# 1038| 0: [ConstructorFieldInit] constructor init of field s
-# 1038| Type = [LValueReferenceType] const String &
-# 1038| ValueCategory = prvalue
-# 1038| 0: [Literal] Unknown literal
-# 1038| Type = [LValueReferenceType] const String &
-# 1038| ValueCategory = prvalue
-# 1038| body: [Block] { ... }
-# 1038| 0: [ReturnStmt] return ...
# 1038| [Constructor] void (void Lambda(int, String const&))::(lambda [] type at line 1038, col. 30)::(constructor)()
# 1038| params:
# 1038| [ConstMemberFunction] char (void Lambda(int, String const&))::(lambda [] type at line 1038, col. 30)::operator()(float) const
@@ -7450,15 +7394,6 @@ ir.cpp:
# 1040| params:
#-----| 0: [Parameter] p#0
#-----| Type = [RValueReferenceType] lambda [] type at line 1040, col. 30 &&
-# 1040| initializations:
-# 1040| 0: [ConstructorFieldInit] constructor init of field s
-# 1040| Type = [SpecifiedType] const String
-# 1040| ValueCategory = prvalue
-# 1040| 0: [ConstructorCall] call to String
-# 1040| Type = [VoidType] void
-# 1040| ValueCategory = prvalue
-# 1040| body: [Block] { ... }
-# 1040| 0: [ReturnStmt] return ...
# 1040| [Constructor] void (void Lambda(int, String const&))::(lambda [] type at line 1040, col. 30)::(constructor)()
# 1040| params:
# 1040| [Destructor] void (void Lambda(int, String const&))::(lambda [] type at line 1040, col. 30)::~()
@@ -7509,21 +7444,6 @@ ir.cpp:
# 1042| params:
#-----| 0: [Parameter] p#0
#-----| Type = [RValueReferenceType] lambda [] type at line 1042, col. 32 &&
-# 1042| initializations:
-# 1042| 0: [ConstructorFieldInit] constructor init of field s
-# 1042| Type = [LValueReferenceType] const String &
-# 1042| ValueCategory = prvalue
-# 1042| 0: [Literal] Unknown literal
-# 1042| Type = [LValueReferenceType] const String &
-# 1042| ValueCategory = prvalue
-# 1042| 1: [ConstructorFieldInit] constructor init of field x
-# 1042| Type = [IntType] int
-# 1042| ValueCategory = prvalue
-# 1042| 0: [Literal] Unknown literal
-# 1042| Type = [IntType] int
-# 1042| ValueCategory = prvalue
-# 1042| body: [Block] { ... }
-# 1042| 0: [ReturnStmt] return ...
# 1042| [Constructor] void (void Lambda(int, String const&))::(lambda [] type at line 1042, col. 32)::(constructor)()
# 1042| params:
# 1042| [ConstMemberFunction] char (void Lambda(int, String const&))::(lambda [] type at line 1042, col. 32)::operator()(float) const
@@ -7565,33 +7485,6 @@ ir.cpp:
# 1045| params:
#-----| 0: [Parameter] p#0
#-----| Type = [RValueReferenceType] lambda [] type at line 1045, col. 23 &&
-# 1045| initializations:
-# 1045| 0: [ConstructorFieldInit] constructor init of field s
-# 1045| Type = [LValueReferenceType] const String &
-# 1045| ValueCategory = prvalue
-# 1045| 0: [Literal] Unknown literal
-# 1045| Type = [LValueReferenceType] const String &
-# 1045| ValueCategory = prvalue
-# 1045| 1: [ConstructorFieldInit] constructor init of field x
-# 1045| Type = [IntType] int
-# 1045| ValueCategory = prvalue
-# 1045| 0: [Literal] Unknown literal
-# 1045| Type = [IntType] int
-# 1045| ValueCategory = prvalue
-# 1045| 2: [ConstructorFieldInit] constructor init of field i
-# 1045| Type = [IntType] int
-# 1045| ValueCategory = prvalue
-# 1045| 0: [Literal] Unknown literal
-# 1045| Type = [IntType] int
-# 1045| ValueCategory = prvalue
-# 1045| 3: [ConstructorFieldInit] constructor init of field j
-# 1045| Type = [LValueReferenceType] int &
-# 1045| ValueCategory = prvalue
-# 1045| 0: [Literal] Unknown literal
-# 1045| Type = [LValueReferenceType] int &
-# 1045| ValueCategory = prvalue
-# 1045| body: [Block] { ... }
-# 1045| 0: [ReturnStmt] return ...
# 1045| [Constructor] void (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 23)::(constructor)()
# 1045| params:
# 1045| [ConstMemberFunction] char (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 23)::operator()(float) const
@@ -7795,16 +7688,16 @@ ir.cpp:
# 1101| 0: [VariableAccess] x
# 1101| Type = [IntType] int
# 1101| ValueCategory = prvalue(load)
-# 1104| [TopLevelFunction] void AsmStmtWithOutputs(unsigned int&, unsigned int&, unsigned int&, unsigned int&)
+# 1104| [TopLevelFunction] void AsmStmtWithOutputs(unsigned int&, unsigned int, unsigned int&, unsigned int)
# 1104| params:
# 1104| 0: [Parameter] a
# 1104| Type = [LValueReferenceType] unsigned int &
# 1104| 1: [Parameter] b
-# 1104| Type = [LValueReferenceType] unsigned int &
+# 1104| Type = [IntType] unsigned int
# 1104| 2: [Parameter] c
# 1104| Type = [LValueReferenceType] unsigned int &
# 1104| 3: [Parameter] d
-# 1104| Type = [LValueReferenceType] unsigned int &
+# 1104| Type = [IntType] unsigned int
# 1105| body: [Block] { ... }
# 1106| 0: [AsmStmt] asm statement
# 1109| 0: [ReferenceDereferenceExpr] (reference dereference)
@@ -7813,24 +7706,18 @@ ir.cpp:
# 1109| expr: [VariableAccess] a
# 1109| Type = [LValueReferenceType] unsigned int &
# 1109| ValueCategory = prvalue(load)
-# 1109| 1: [ReferenceDereferenceExpr] (reference dereference)
+# 1109| 1: [VariableAccess] b
# 1109| Type = [IntType] unsigned int
# 1109| ValueCategory = lvalue
-# 1109| expr: [VariableAccess] b
-# 1109| Type = [LValueReferenceType] unsigned int &
-# 1109| ValueCategory = prvalue(load)
# 1109| 2: [ReferenceDereferenceExpr] (reference dereference)
# 1109| Type = [IntType] unsigned int
-# 1109| ValueCategory = lvalue
+# 1109| ValueCategory = prvalue(load)
# 1109| expr: [VariableAccess] c
# 1109| Type = [LValueReferenceType] unsigned int &
# 1109| ValueCategory = prvalue(load)
-# 1109| 3: [ReferenceDereferenceExpr] (reference dereference)
+# 1109| 3: [VariableAccess] d
# 1109| Type = [IntType] unsigned int
-# 1109| ValueCategory = lvalue
-# 1109| expr: [VariableAccess] d
-# 1109| Type = [LValueReferenceType] unsigned int &
-# 1109| ValueCategory = prvalue(load)
+# 1109| ValueCategory = prvalue(load)
# 1111| 1: [ReturnStmt] return ...
# 1113| [TopLevelFunction] void ExternDeclarations()
# 1113| params:
@@ -7855,7 +7742,7 @@ ir.cpp:
# 1118| Type = [IntType] int
# 1119| 4: [DeclStmt] declaration
# 1119| 0: [TypeDeclarationEntry] declaration of d
-# 1119| Type = [LocalTypedefType] d
+# 1119| Type = [CTypedefType,LocalTypedefType] d
# 1120| 5: [ReturnStmt] return ...
# 1117| [TopLevelFunction] int f(float)
# 1117| params:
@@ -8015,7 +7902,7 @@ ir.cpp:
# 1154| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
# 1154| init: [Initializer] initializer for vi4
# 1154| expr: [VectorAggregateLiteral] {...}
-# 1154| Type = [GNUVectorType] __attribute((vector_size(16))) int
+# 1154| Type = [GNUVectorType] __attribute((vector_size(16UL))) int
# 1154| ValueCategory = prvalue
# 1154| 0: [Literal] 0
# 1154| Type = [IntType] int
@@ -8107,7 +7994,7 @@ ir.cpp:
# 1158| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
# 1158| ValueCategory = lvalue
# 1158| 1: [AddExpr] ... + ...
-# 1158| Type = [GNUVectorType] __attribute((vector_size(16))) int
+# 1158| Type = [GNUVectorType] __attribute((vector_size(16UL))) int
# 1158| ValueCategory = prvalue
# 1158| 0: [VariableAccess] vi4
# 1158| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
@@ -8116,6 +8003,59 @@ ir.cpp:
# 1158| Type = [SpecifiedType] __attribute((vector_size(16UL))) int
# 1158| ValueCategory = prvalue(load)
# 1159| 5: [ReturnStmt] return ...
+# 1161| [TopLevelFunction] void* memcpy(void*, void*, int)
+# 1161| params:
+# 1161| 0: [Parameter] dst
+# 1161| Type = [VoidPointerType] void *
+# 1161| 1: [Parameter] src
+# 1161| Type = [VoidPointerType] void *
+# 1161| 2: [Parameter] size
+# 1161| Type = [IntType] int
+# 1163| [TopLevelFunction] int ModeledCallTarget(int)
+# 1163| params:
+# 1163| 0: [Parameter] x
+# 1163| Type = [IntType] int
+# 1163| body: [Block] { ... }
+# 1164| 0: [DeclStmt] declaration
+# 1164| 0: [VariableDeclarationEntry] definition of y
+# 1164| Type = [IntType] int
+# 1165| 1: [ExprStmt] ExprStmt
+# 1165| 0: [FunctionCall] call to memcpy
+# 1165| Type = [VoidPointerType] void *
+# 1165| ValueCategory = prvalue
+# 1165| 0: [CStyleCast] (void *)...
+# 1165| Conversion = [PointerConversion] pointer conversion
+# 1165| Type = [VoidPointerType] void *
+# 1165| ValueCategory = prvalue
+# 1165| expr: [AddressOfExpr] & ...
+# 1165| Type = [IntPointerType] int *
+# 1165| ValueCategory = prvalue
+# 1165| 0: [VariableAccess] y
+# 1165| Type = [IntType] int
+# 1165| ValueCategory = lvalue
+# 1165| 1: [CStyleCast] (void *)...
+# 1165| Conversion = [PointerConversion] pointer conversion
+# 1165| Type = [VoidPointerType] void *
+# 1165| ValueCategory = prvalue
+# 1165| expr: [AddressOfExpr] & ...
+# 1165| Type = [IntPointerType] int *
+# 1165| ValueCategory = prvalue
+# 1165| 0: [VariableAccess] x
+# 1165| Type = [IntType] int
+# 1165| ValueCategory = lvalue
+# 1165| 2: [CStyleCast] (int)...
+# 1165| Conversion = [IntegralConversion] integral conversion
+# 1165| Type = [IntType] int
+# 1165| Value = [CStyleCast] 4
+# 1165| ValueCategory = prvalue
+# 1165| expr: [SizeofTypeOperator] sizeof(int)
+# 1165| Type = [LongType] unsigned long
+# 1165| Value = [SizeofTypeOperator] 4
+# 1165| ValueCategory = prvalue
+# 1166| 2: [ReturnStmt] return ...
+# 1166| 0: [VariableAccess] y
+# 1166| Type = [IntType] int
+# 1166| ValueCategory = prvalue(load)
perf-regression.cpp:
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
# 4| params:
diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected
index ae680785ce6..e5e666c020b 100644
--- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected
+++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected
@@ -1,8 +1,14 @@
missingOperand
+| ir.cpp:809:7:809:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
+| ir.cpp:810:7:810:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
+| ir.cpp:823:7:823:13 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
+| ir.cpp:824:7:824:26 | IndirectMayWriteSideEffect: call to Base | Instruction 'IndirectMayWriteSideEffect' is missing an expected operand with tag 'Address' in function '$@'. | ir.cpp:799:6:799:25 | IR: HierarchyConversions | void HierarchyConversions() |
unexpectedOperand
duplicateOperand
missingPhiOperand
missingOperandType
+duplicateChiOperand
+sideEffectWithoutPrimary
instructionWithoutSuccessor
ambiguousSuccessors
unexplainedLoop
@@ -13,3 +19,8 @@ containsLoopOfForwardEdges
lostReachability
backEdgeCountMismatch
useNotDominatedByDefinition
+missingCanonicalLanguageType
+multipleCanonicalLanguageTypes
+missingIRType
+multipleIRTypes
+missingCppType
diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp
index 672c8d1ab81..7ebd2e22aee 100644
--- a/cpp/ql/test/library-tests/ir/ir/ir.cpp
+++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp
@@ -1101,12 +1101,12 @@ int AsmStmt(int x) {
return x;
}
-static void AsmStmtWithOutputs(unsigned int& a, unsigned int& b, unsigned int& c, unsigned int& d)
+static void AsmStmtWithOutputs(unsigned int& a, unsigned int b, unsigned int& c, unsigned int d)
{
__asm__ __volatile__
(
"cpuid\n\t"
- : "+a" (a), "+b" (b), "+c" (c), "+d" (d)
+ : "+a" (a), "+b" (b) : "c" (c), "d" (d)
);
}
@@ -1158,4 +1158,12 @@ void VectorTypes(int i) {
vi4 = vi4 + vi4_shuffle;
}
+void *memcpy(void *dst, void *src, int size);
+
+int ModeledCallTarget(int x) {
+ int y;
+ memcpy(&y, &x, sizeof(int));
+ return y;
+}
+
// semmle-extractor-options: -std=c++17 --clang
diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
index 20f15e4f772..5a70e632d49 100644
--- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
+++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
@@ -1,4 +1,28 @@
bad_asts.cpp:
+# 9| int Bad::S::MemberFunction(int)
+# 9| Block 0
+# 9| v0_0(void) = EnterFunction :
+# 9| mu0_1(unknown) = AliasedDefinition :
+# 9| mu0_2(unknown) = UnmodeledDefinition :
+# 9| r0_3(glval) = InitializeThis :
+# 9| r0_4(glval) = VariableAddress[y] :
+# 9| mu0_5(int) = InitializeParameter[y] : &:r0_4
+# 10| r0_6(glval) = VariableAddress[#return] :
+# 10| r0_7(int) = Constant[6] :
+#-----| r0_8(S *) = CopyValue : r0_3
+# 10| r0_9(glval) = FieldAddress[x] : r0_8
+# 10| r0_10(int) = Load : &:r0_9, ~mu0_2
+# 10| r0_11(int) = Add : r0_7, r0_10
+# 10| r0_12(glval) = VariableAddress[y] :
+# 10| r0_13(int) = Load : &:r0_12, ~mu0_2
+# 10| r0_14(int) = Add : r0_11, r0_13
+# 10| mu0_15(int) = Store : &:r0_6, r0_14
+# 9| r0_16(glval) = VariableAddress[#return] :
+# 9| v0_17(void) = ReturnValue : &:r0_16, ~mu0_2
+# 9| v0_18(void) = UnmodeledUse : mu*
+# 9| v0_19(void) = AliasedUse : ~mu0_2
+# 9| v0_20(void) = ExitFunction :
+
# 14| void Bad::CallBadMemberFunction()
# 14| Block 0
# 14| v0_0(void) = EnterFunction :
@@ -14,10 +38,13 @@ bad_asts.cpp:
# 16| r0_10(int) = Constant[1] :
# 16| r0_11(int) = Call : func:r0_9, this:r0_8, 0:r0_10
# 16| mu0_12(unknown) = ^CallSideEffect : ~mu0_2
-# 17| v0_13(void) = NoOp :
-# 14| v0_14(void) = ReturnVoid :
-# 14| v0_15(void) = UnmodeledUse : mu*
-# 14| v0_16(void) = ExitFunction :
+# 16| v0_13(void) = ^BufferReadSideEffect[-1] : &:r0_8, ~mu0_2
+# 16| mu0_14(S) = ^IndirectMayWriteSideEffect[-1] : &:r0_8
+# 17| v0_15(void) = NoOp :
+# 14| v0_16(void) = ReturnVoid :
+# 14| v0_17(void) = UnmodeledUse : mu*
+# 14| v0_18(void) = AliasedUse : ~mu0_2
+# 14| v0_19(void) = ExitFunction :
# 22| void Bad::Point::Point()
# 22| Block 0
@@ -28,7 +55,8 @@ bad_asts.cpp:
# 23| v0_4(void) = NoOp :
# 22| v0_5(void) = ReturnVoid :
# 22| v0_6(void) = UnmodeledUse : mu*
-# 22| v0_7(void) = ExitFunction :
+# 22| v0_7(void) = AliasedUse : ~mu0_2
+# 22| v0_8(void) = ExitFunction :
# 26| void Bad::CallCopyConstructor(Bad::Point const&)
# 26| Block 0
@@ -40,13 +68,15 @@ bad_asts.cpp:
# 27| r0_5(glval) = VariableAddress[b] :
# 27| r0_6(glval) = VariableAddress[a] :
# 27| r0_7(Point &) = Load : &:r0_6, ~mu0_2
-# 27| r0_8(glval) = Convert : r0_7
-# 27| r0_9(Point) = Load : &:r0_8, ~mu0_2
-# 27| mu0_10(Point) = Store : &:r0_5, r0_9
-# 28| v0_11(void) = NoOp :
-# 26| v0_12(void) = ReturnVoid :
-# 26| v0_13(void) = UnmodeledUse : mu*
-# 26| v0_14(void) = ExitFunction :
+# 27| r0_8(glval) = CopyValue : r0_7
+# 27| r0_9(glval) = Convert : r0_8
+# 27| r0_10(Point) = Load : &:r0_9, ~mu0_2
+# 27| mu0_11(Point) = Store : &:r0_5, r0_10
+# 28| v0_12(void) = NoOp :
+# 26| v0_13(void) = ReturnVoid :
+# 26| v0_14(void) = UnmodeledUse : mu*
+# 26| v0_15(void) = AliasedUse : ~mu0_2
+# 26| v0_16(void) = ExitFunction :
# 30| void Bad::errorExpr()
# 30| Block 0
@@ -66,7 +96,8 @@ bad_asts.cpp:
# 34| v0_13(void) = NoOp :
# 30| v0_14(void) = ReturnVoid :
# 30| v0_15(void) = UnmodeledUse : mu*
-# 30| v0_16(void) = ExitFunction :
+# 30| v0_16(void) = AliasedUse : ~mu0_2
+# 30| v0_17(void) = ExitFunction :
clang.cpp:
# 5| int* globalIntAddress()
@@ -76,11 +107,13 @@ clang.cpp:
# 5| mu0_2(unknown) = UnmodeledDefinition :
# 6| r0_3(glval) = VariableAddress[#return] :
# 6| r0_4(glval) = VariableAddress[globalInt] :
-# 6| mu0_5(int *) = Store : &:r0_3, r0_4
-# 5| r0_6(glval) = VariableAddress[#return] :
-# 5| v0_7(void) = ReturnValue : &:r0_6, ~mu0_2
-# 5| v0_8(void) = UnmodeledUse : mu*
-# 5| v0_9(void) = ExitFunction :
+# 6| r0_5(int *) = CopyValue : r0_4
+# 6| mu0_6(int *) = Store : &:r0_3, r0_5
+# 5| r0_7(glval) = VariableAddress[#return] :
+# 5| v0_8(void) = ReturnValue : &:r0_7, ~mu0_2
+# 5| v0_9(void) = UnmodeledUse : mu*
+# 5| v0_10(void) = AliasedUse : ~mu0_2
+# 5| v0_11(void) = ExitFunction :
ir.cpp:
# 1| void Constants()
@@ -175,7 +208,8 @@ ir.cpp:
# 41| v0_87(void) = NoOp :
# 1| v0_88(void) = ReturnVoid :
# 1| v0_89(void) = UnmodeledUse : mu*
-# 1| v0_90(void) = ExitFunction :
+# 1| v0_90(void) = AliasedUse : ~mu0_2
+# 1| v0_91(void) = ExitFunction :
# 43| void Foo()
# 43| Block 0
@@ -208,7 +242,8 @@ ir.cpp:
# 48| v0_26(void) = NoOp :
# 43| v0_27(void) = ReturnVoid :
# 43| v0_28(void) = UnmodeledUse : mu*
-# 43| v0_29(void) = ExitFunction :
+# 43| v0_29(void) = AliasedUse : ~mu0_2
+# 43| v0_30(void) = ExitFunction :
# 50| void IntegerOps(int, int)
# 50| Block 0
@@ -381,7 +416,8 @@ ir.cpp:
# 85| v0_166(void) = NoOp :
# 50| v0_167(void) = ReturnVoid :
# 50| v0_168(void) = UnmodeledUse : mu*
-# 50| v0_169(void) = ExitFunction :
+# 50| v0_169(void) = AliasedUse : ~mu0_2
+# 50| v0_170(void) = ExitFunction :
# 87| void IntegerCompare(int, int)
# 87| Block 0
@@ -439,7 +475,8 @@ ir.cpp:
# 96| v0_51(void) = NoOp :
# 87| v0_52(void) = ReturnVoid :
# 87| v0_53(void) = UnmodeledUse : mu*
-# 87| v0_54(void) = ExitFunction :
+# 87| v0_54(void) = AliasedUse : ~mu0_2
+# 87| v0_55(void) = ExitFunction :
# 98| void IntegerCrement(int)
# 98| Block 0
@@ -481,7 +518,8 @@ ir.cpp:
# 105| v0_35(void) = NoOp :
# 98| v0_36(void) = ReturnVoid :
# 98| v0_37(void) = UnmodeledUse : mu*
-# 98| v0_38(void) = ExitFunction :
+# 98| v0_38(void) = AliasedUse : ~mu0_2
+# 98| v0_39(void) = ExitFunction :
# 107| void IntegerCrement_LValue(int)
# 107| Block 0
@@ -497,19 +535,24 @@ ir.cpp:
# 110| r0_9(int) = Constant[1] :
# 110| r0_10(int) = Add : r0_8, r0_9
# 110| mu0_11(int) = Store : &:r0_7, r0_10
-# 110| r0_12(glval) = VariableAddress[p] :
-# 110| mu0_13(int *) = Store : &:r0_12, r0_7
-# 111| r0_14(glval) = VariableAddress[x] :
-# 111| r0_15(int) = Load : &:r0_14, ~mu0_2
-# 111| r0_16(int) = Constant[1] :
-# 111| r0_17(int) = Sub : r0_15, r0_16
-# 111| mu0_18(int) = Store : &:r0_14, r0_17
-# 111| r0_19(glval) = VariableAddress[p] :
-# 111| mu0_20(int *) = Store : &:r0_19, r0_14
-# 112| v0_21(void) = NoOp :
-# 107| v0_22(void) = ReturnVoid :
-# 107| v0_23(void) = UnmodeledUse : mu*
-# 107| v0_24(void) = ExitFunction :
+# 110| r0_12(glval) = CopyValue : r0_7
+# 110| r0_13(int *) = CopyValue : r0_12
+# 110| r0_14(glval) = VariableAddress[p] :
+# 110| mu0_15(int *) = Store : &:r0_14, r0_13
+# 111| r0_16(glval) = VariableAddress[x] :
+# 111| r0_17(int) = Load : &:r0_16, ~mu0_2
+# 111| r0_18(int) = Constant[1] :
+# 111| r0_19(int) = Sub : r0_17, r0_18
+# 111| mu0_20(int) = Store : &:r0_16, r0_19
+# 111| r0_21(glval) = CopyValue : r0_16
+# 111| r0_22(int *) = CopyValue : r0_21
+# 111| r0_23(glval) = VariableAddress[p] :
+# 111| mu0_24(int *) = Store : &:r0_23, r0_22
+# 112| v0_25(void) = NoOp :
+# 107| v0_26(void) = ReturnVoid :
+# 107| v0_27(void) = UnmodeledUse : mu*
+# 107| v0_28(void) = AliasedUse : ~mu0_2
+# 107| v0_29(void) = ExitFunction :
# 114| void FloatOps(double, double)
# 114| Block 0
@@ -591,7 +634,8 @@ ir.cpp:
# 131| v0_75(void) = NoOp :
# 114| v0_76(void) = ReturnVoid :
# 114| v0_77(void) = UnmodeledUse : mu*
-# 114| v0_78(void) = ExitFunction :
+# 114| v0_78(void) = AliasedUse : ~mu0_2
+# 114| v0_79(void) = ExitFunction :
# 133| void FloatCompare(double, double)
# 133| Block 0
@@ -649,7 +693,8 @@ ir.cpp:
# 142| v0_51(void) = NoOp :
# 133| v0_52(void) = ReturnVoid :
# 133| v0_53(void) = UnmodeledUse : mu*
-# 133| v0_54(void) = ExitFunction :
+# 133| v0_54(void) = AliasedUse : ~mu0_2
+# 133| v0_55(void) = ExitFunction :
# 144| void FloatCrement(float)
# 144| Block 0
@@ -691,7 +736,8 @@ ir.cpp:
# 151| v0_35(void) = NoOp :
# 144| v0_36(void) = ReturnVoid :
# 144| v0_37(void) = UnmodeledUse : mu*
-# 144| v0_38(void) = ExitFunction :
+# 144| v0_38(void) = AliasedUse : ~mu0_2
+# 144| v0_39(void) = ExitFunction :
# 153| void PointerOps(int*, int)
# 153| Block 0
@@ -767,7 +813,8 @@ ir.cpp:
# 169| v0_69(void) = NoOp :
# 153| v0_70(void) = ReturnVoid :
# 153| v0_71(void) = UnmodeledUse : mu*
-# 153| v0_72(void) = ExitFunction :
+# 153| v0_72(void) = AliasedUse : ~mu0_2
+# 153| v0_73(void) = ExitFunction :
# 171| void ArrayAccess(int*, int)
# 171| Block 0
@@ -849,7 +896,8 @@ ir.cpp:
# 185| v0_75(void) = NoOp :
# 171| v0_76(void) = ReturnVoid :
# 171| v0_77(void) = UnmodeledUse : mu*
-# 171| v0_78(void) = ExitFunction :
+# 171| v0_78(void) = AliasedUse : ~mu0_2
+# 171| v0_79(void) = ExitFunction :
# 187| void StringLiteral(int)
# 187| Block 0
@@ -882,7 +930,8 @@ ir.cpp:
# 191| v0_26(void) = NoOp :
# 187| v0_27(void) = ReturnVoid :
# 187| v0_28(void) = UnmodeledUse : mu*
-# 187| v0_29(void) = ExitFunction :
+# 187| v0_29(void) = AliasedUse : ~mu0_2
+# 187| v0_30(void) = ExitFunction :
# 193| void PointerCompare(int*, int*)
# 193| Block 0
@@ -940,7 +989,8 @@ ir.cpp:
# 202| v0_51(void) = NoOp :
# 193| v0_52(void) = ReturnVoid :
# 193| v0_53(void) = UnmodeledUse : mu*
-# 193| v0_54(void) = ExitFunction :
+# 193| v0_54(void) = AliasedUse : ~mu0_2
+# 193| v0_55(void) = ExitFunction :
# 204| void PointerCrement(int*)
# 204| Block 0
@@ -982,7 +1032,8 @@ ir.cpp:
# 211| v0_35(void) = NoOp :
# 204| v0_36(void) = ReturnVoid :
# 204| v0_37(void) = UnmodeledUse : mu*
-# 204| v0_38(void) = ExitFunction :
+# 204| v0_38(void) = AliasedUse : ~mu0_2
+# 204| v0_39(void) = ExitFunction :
# 213| void CompoundAssignment()
# 213| Block 0
@@ -1026,7 +1077,8 @@ ir.cpp:
# 228| v0_37(void) = NoOp :
# 213| v0_38(void) = ReturnVoid :
# 213| v0_39(void) = UnmodeledUse : mu*
-# 213| v0_40(void) = ExitFunction :
+# 213| v0_40(void) = AliasedUse : ~mu0_2
+# 213| v0_41(void) = ExitFunction :
# 230| void UninitializedVariables()
# 230| Block 0
@@ -1042,7 +1094,8 @@ ir.cpp:
# 233| v0_9(void) = NoOp :
# 230| v0_10(void) = ReturnVoid :
# 230| v0_11(void) = UnmodeledUse : mu*
-# 230| v0_12(void) = ExitFunction :
+# 230| v0_12(void) = AliasedUse : ~mu0_2
+# 230| v0_13(void) = ExitFunction :
# 235| int Parameters(int, int)
# 235| Block 0
@@ -1063,7 +1116,8 @@ ir.cpp:
# 235| r0_14(glval) = VariableAddress[#return] :
# 235| v0_15(void) = ReturnValue : &:r0_14, ~mu0_2
# 235| v0_16(void) = UnmodeledUse : mu*
-# 235| v0_17(void) = ExitFunction :
+# 235| v0_17(void) = AliasedUse : ~mu0_2
+# 235| v0_18(void) = ExitFunction :
# 239| void IfStatements(bool, int, int)
# 239| Block 0
@@ -1121,7 +1175,8 @@ ir.cpp:
# 251| v6_0(void) = NoOp :
# 239| v6_1(void) = ReturnVoid :
# 239| v6_2(void) = UnmodeledUse : mu*
-# 239| v6_3(void) = ExitFunction :
+# 239| v6_3(void) = AliasedUse : ~mu0_2
+# 239| v6_4(void) = ExitFunction :
# 240| Block 7
# 240| v7_0(void) = NoOp :
@@ -1148,7 +1203,8 @@ ir.cpp:
# 257| v2_0(void) = NoOp :
# 253| v2_1(void) = ReturnVoid :
# 253| v2_2(void) = UnmodeledUse : mu*
-# 253| v2_3(void) = ExitFunction :
+# 253| v2_3(void) = AliasedUse : ~mu0_2
+# 253| v2_4(void) = ExitFunction :
# 254| Block 3
# 254| r3_0(glval) = VariableAddress[n] :
@@ -1186,7 +1242,8 @@ ir.cpp:
# 263| v2_0(void) = NoOp :
# 259| v2_1(void) = ReturnVoid :
# 259| v2_2(void) = UnmodeledUse : mu*
-# 259| v2_3(void) = ExitFunction :
+# 259| v2_3(void) = AliasedUse : ~mu0_2
+# 259| v2_4(void) = ExitFunction :
# 265| void For_Empty()
# 265| Block 0
@@ -1200,7 +1257,8 @@ ir.cpp:
# 265| Block 1
# 265| v1_0(void) = ReturnVoid :
# 265| v1_1(void) = UnmodeledUse : mu*
-# 265| v1_2(void) = ExitFunction :
+# 265| v1_2(void) = AliasedUse : ~mu0_2
+# 265| v1_3(void) = ExitFunction :
# 268| Block 2
# 268| v2_0(void) = NoOp :
@@ -1219,7 +1277,8 @@ ir.cpp:
# 272| Block 1
# 272| v1_0(void) = ReturnVoid :
# 272| v1_1(void) = UnmodeledUse : mu*
-# 272| v1_2(void) = ExitFunction :
+# 272| v1_2(void) = AliasedUse : ~mu0_2
+# 272| v1_3(void) = ExitFunction :
# 274| Block 2
# 274| v2_0(void) = NoOp :
@@ -1252,7 +1311,8 @@ ir.cpp:
# 283| v3_0(void) = NoOp :
# 278| v3_1(void) = ReturnVoid :
# 278| v3_2(void) = UnmodeledUse : mu*
-# 278| v3_3(void) = ExitFunction :
+# 278| v3_3(void) = AliasedUse : ~mu0_2
+# 278| v3_4(void) = ExitFunction :
# 285| void For_Update()
# 285| Block 0
@@ -1267,7 +1327,8 @@ ir.cpp:
# 285| Block 1
# 285| v1_0(void) = ReturnVoid :
# 285| v1_1(void) = UnmodeledUse : mu*
-# 285| v1_2(void) = ExitFunction :
+# 285| v1_2(void) = AliasedUse : ~mu0_2
+# 285| v1_3(void) = ExitFunction :
# 288| Block 2
# 288| v2_0(void) = NoOp :
@@ -1305,7 +1366,8 @@ ir.cpp:
# 296| v3_0(void) = NoOp :
# 292| v3_1(void) = ReturnVoid :
# 292| v3_2(void) = UnmodeledUse : mu*
-# 292| v3_3(void) = ExitFunction :
+# 292| v3_3(void) = AliasedUse : ~mu0_2
+# 292| v3_4(void) = ExitFunction :
# 298| void For_InitUpdate()
# 298| Block 0
@@ -1320,7 +1382,8 @@ ir.cpp:
# 298| Block 1
# 298| v1_0(void) = ReturnVoid :
# 298| v1_1(void) = UnmodeledUse : mu*
-# 298| v1_2(void) = ExitFunction :
+# 298| v1_2(void) = AliasedUse : ~mu0_2
+# 298| v1_3(void) = ExitFunction :
# 300| Block 2
# 300| v2_0(void) = NoOp :
@@ -1363,7 +1426,8 @@ ir.cpp:
# 309| v3_0(void) = NoOp :
# 304| v3_1(void) = ReturnVoid :
# 304| v3_2(void) = UnmodeledUse : mu*
-# 304| v3_3(void) = ExitFunction :
+# 304| v3_3(void) = AliasedUse : ~mu0_2
+# 304| v3_4(void) = ExitFunction :
# 311| void For_InitConditionUpdate()
# 311| Block 0
@@ -1397,7 +1461,8 @@ ir.cpp:
# 315| v3_0(void) = NoOp :
# 311| v3_1(void) = ReturnVoid :
# 311| v3_2(void) = UnmodeledUse : mu*
-# 311| v3_3(void) = ExitFunction :
+# 311| v3_3(void) = AliasedUse : ~mu0_2
+# 311| v3_4(void) = ExitFunction :
# 317| void For_Break()
# 317| Block 0
@@ -1444,7 +1509,8 @@ ir.cpp:
# 323| v5_1(void) = NoOp :
# 317| v5_2(void) = ReturnVoid :
# 317| v5_3(void) = UnmodeledUse : mu*
-# 317| v5_4(void) = ExitFunction :
+# 317| v5_4(void) = AliasedUse : ~mu0_2
+# 317| v5_5(void) = ExitFunction :
# 325| void For_Continue_Update()
# 325| Block 0
@@ -1491,7 +1557,8 @@ ir.cpp:
# 331| v5_0(void) = NoOp :
# 325| v5_1(void) = ReturnVoid :
# 325| v5_2(void) = UnmodeledUse : mu*
-# 325| v5_3(void) = ExitFunction :
+# 325| v5_3(void) = AliasedUse : ~mu0_2
+# 325| v5_4(void) = ExitFunction :
# 333| void For_Continue_NoUpdate()
# 333| Block 0
@@ -1533,7 +1600,8 @@ ir.cpp:
# 339| v5_0(void) = NoOp :
# 333| v5_1(void) = ReturnVoid :
# 333| v5_2(void) = UnmodeledUse : mu*
-# 333| v5_3(void) = ExitFunction :
+# 333| v5_3(void) = AliasedUse : ~mu0_2
+# 333| v5_4(void) = ExitFunction :
# 341| int Dereference(int*)
# 341| Block 0
@@ -1545,16 +1613,18 @@ ir.cpp:
# 342| r0_5(int) = Constant[1] :
# 342| r0_6(glval) = VariableAddress[p] :
# 342| r0_7(int *) = Load : &:r0_6, ~mu0_2
-# 342| mu0_8(int) = Store : &:r0_7, r0_5
-# 343| r0_9(glval) = VariableAddress[#return] :
-# 343| r0_10(glval) = VariableAddress[p] :
-# 343| r0_11(int *) = Load : &:r0_10, ~mu0_2
-# 343| r0_12(int) = Load : &:r0_11, ~mu0_2
-# 343| mu0_13(int) = Store : &:r0_9, r0_12
-# 341| r0_14(glval) = VariableAddress[#return] :
-# 341| v0_15(void) = ReturnValue : &:r0_14, ~mu0_2
-# 341| v0_16(void) = UnmodeledUse : mu*
-# 341| v0_17(void) = ExitFunction :
+# 342| r0_8(glval) = CopyValue : r0_7
+# 342| mu0_9(int) = Store : &:r0_8, r0_5
+# 343| r0_10(glval) = VariableAddress[#return] :
+# 343| r0_11(glval) = VariableAddress[p] :
+# 343| r0_12(int *) = Load : &:r0_11, ~mu0_2
+# 343| r0_13(int) = Load : &:r0_12, ~mu0_2
+# 343| mu0_14(int) = Store : &:r0_10, r0_13
+# 341| r0_15(glval) = VariableAddress[#return] :
+# 341| v0_16(void) = ReturnValue : &:r0_15, ~mu0_2
+# 341| v0_17(void) = UnmodeledUse : mu*
+# 341| v0_18(void) = AliasedUse : ~mu0_2
+# 341| v0_19(void) = ExitFunction :
# 348| int* AddressOf()
# 348| Block 0
@@ -1563,11 +1633,13 @@ ir.cpp:
# 348| mu0_2(unknown) = UnmodeledDefinition :
# 349| r0_3(glval) = VariableAddress[#return] :
# 349| r0_4(glval) = VariableAddress[g] :
-# 349| mu0_5(int *) = Store : &:r0_3, r0_4
-# 348| r0_6(glval) = VariableAddress[#return] :
-# 348| v0_7(void) = ReturnValue : &:r0_6, ~mu0_2
-# 348| v0_8(void) = UnmodeledUse : mu*
-# 348| v0_9(void) = ExitFunction :
+# 349| r0_5(int *) = CopyValue : r0_4
+# 349| mu0_6(int *) = Store : &:r0_3, r0_5
+# 348| r0_7(glval) = VariableAddress[#return] :
+# 348| v0_8(void) = ReturnValue : &:r0_7, ~mu0_2
+# 348| v0_9(void) = UnmodeledUse : mu*
+# 348| v0_10(void) = AliasedUse : ~mu0_2
+# 348| v0_11(void) = ExitFunction :
# 352| void Break(int)
# 352| Block 0
@@ -1604,7 +1676,8 @@ ir.cpp:
# 358| v4_1(void) = NoOp :
# 352| v4_2(void) = ReturnVoid :
# 352| v4_3(void) = UnmodeledUse : mu*
-# 352| v4_4(void) = ExitFunction :
+# 352| v4_4(void) = AliasedUse : ~mu0_2
+# 352| v4_5(void) = ExitFunction :
# 353| Block 5
# 353| r5_0(glval) = VariableAddress[n] :
@@ -1659,7 +1732,8 @@ ir.cpp:
# 367| v5_0(void) = NoOp :
# 360| v5_1(void) = ReturnVoid :
# 360| v5_2(void) = UnmodeledUse : mu*
-# 360| v5_3(void) = ExitFunction :
+# 360| v5_3(void) = AliasedUse : ~mu0_2
+# 360| v5_4(void) = ExitFunction :
# 372| void Call()
# 372| Block 0
@@ -1672,7 +1746,8 @@ ir.cpp:
# 374| v0_6(void) = NoOp :
# 372| v0_7(void) = ReturnVoid :
# 372| v0_8(void) = UnmodeledUse : mu*
-# 372| v0_9(void) = ExitFunction :
+# 372| v0_9(void) = AliasedUse : ~mu0_2
+# 372| v0_10(void) = ExitFunction :
# 376| int CallAdd(int, int)
# 376| Block 0
@@ -1695,7 +1770,8 @@ ir.cpp:
# 376| r0_16(glval) = VariableAddress[#return] :
# 376| v0_17(void) = ReturnValue : &:r0_16, ~mu0_2
# 376| v0_18(void) = UnmodeledUse : mu*
-# 376| v0_19(void) = ExitFunction :
+# 376| v0_19(void) = AliasedUse : ~mu0_2
+# 376| v0_20(void) = ExitFunction :
# 380| int Comma(int, int)
# 380| Block 0
@@ -1717,11 +1793,13 @@ ir.cpp:
# 381| r0_15(int) = Load : &:r0_14, ~mu0_2
# 381| r0_16(int) = Call : func:r0_11, 0:r0_13, 1:r0_15
# 381| mu0_17(unknown) = ^CallSideEffect : ~mu0_2
-# 381| mu0_18(int) = Store : &:r0_7, r0_16
-# 380| r0_19(glval) = VariableAddress[#return] :
-# 380| v0_20(void) = ReturnValue : &:r0_19, ~mu0_2
-# 380| v0_21(void) = UnmodeledUse : mu*
-# 380| v0_22(void) = ExitFunction :
+# 381| r0_18(int) = CopyValue : r0_16
+# 381| mu0_19(int) = Store : &:r0_7, r0_18
+# 380| r0_20(glval) = VariableAddress[#return] :
+# 380| v0_21(void) = ReturnValue : &:r0_20, ~mu0_2
+# 380| v0_22(void) = UnmodeledUse : mu*
+# 380| v0_23(void) = AliasedUse : ~mu0_2
+# 380| v0_24(void) = ExitFunction :
# 384| void Switch(int)
# 384| Block 0
@@ -1802,7 +1880,8 @@ ir.cpp:
# 410| v9_1(void) = NoOp :
# 384| v9_2(void) = ReturnVoid :
# 384| v9_3(void) = UnmodeledUse : mu*
-# 384| v9_4(void) = ExitFunction :
+# 384| v9_4(void) = AliasedUse : ~mu0_2
+# 384| v9_5(void) = ExitFunction :
# 422| Point ReturnStruct(Point)
# 422| Block 0
@@ -1818,7 +1897,8 @@ ir.cpp:
# 422| r0_9(glval) = VariableAddress[#return] :
# 422| v0_10(void) = ReturnValue : &:r0_9, ~mu0_2
# 422| v0_11(void) = UnmodeledUse : mu*
-# 422| v0_12(void) = ExitFunction :
+# 422| v0_12(void) = AliasedUse : ~mu0_2
+# 422| v0_13(void) = ExitFunction :
# 426| void FieldAccess()
# 426| Block 0
@@ -1840,11 +1920,13 @@ ir.cpp:
# 430| r0_15(glval) = VariableAddress[p] :
# 430| r0_16(glval) = VariableAddress[pt] :
# 430| r0_17(glval) = FieldAddress[y] : r0_16
-# 430| mu0_18(int *) = Store : &:r0_15, r0_17
-# 431| v0_19(void) = NoOp :
-# 426| v0_20(void) = ReturnVoid :
-# 426| v0_21(void) = UnmodeledUse : mu*
-# 426| v0_22(void) = ExitFunction :
+# 430| r0_18(int *) = CopyValue : r0_17
+# 430| mu0_19(int *) = Store : &:r0_15, r0_18
+# 431| v0_20(void) = NoOp :
+# 426| v0_21(void) = ReturnVoid :
+# 426| v0_22(void) = UnmodeledUse : mu*
+# 426| v0_23(void) = AliasedUse : ~mu0_2
+# 426| v0_24(void) = ExitFunction :
# 433| void LogicalOr(bool, bool)
# 433| Block 0
@@ -1906,7 +1988,8 @@ ir.cpp:
# 445| v7_0(void) = NoOp :
# 433| v7_1(void) = ReturnVoid :
# 433| v7_2(void) = UnmodeledUse : mu*
-# 433| v7_3(void) = ExitFunction :
+# 433| v7_3(void) = AliasedUse : ~mu0_2
+# 433| v7_4(void) = ExitFunction :
# 447| void LogicalAnd(bool, bool)
# 447| Block 0
@@ -1968,7 +2051,8 @@ ir.cpp:
# 459| v7_0(void) = NoOp :
# 447| v7_1(void) = ReturnVoid :
# 447| v7_2(void) = UnmodeledUse : mu*
-# 447| v7_3(void) = ExitFunction :
+# 447| v7_3(void) = AliasedUse : ~mu0_2
+# 447| v7_4(void) = ExitFunction :
# 461| void LogicalNot(bool, bool)
# 461| Block 0
@@ -2023,7 +2107,8 @@ ir.cpp:
# 473| v6_0(void) = NoOp :
# 461| v6_1(void) = ReturnVoid :
# 461| v6_2(void) = UnmodeledUse : mu*
-# 461| v6_3(void) = ExitFunction :
+# 461| v6_3(void) = AliasedUse : ~mu0_2
+# 461| v6_4(void) = ExitFunction :
# 475| void ConditionValues(bool, bool)
# 475| Block 0
@@ -2094,7 +2179,8 @@ ir.cpp:
# 480| v7_5(void) = NoOp :
# 475| v7_6(void) = ReturnVoid :
# 475| v7_7(void) = UnmodeledUse : mu*
-# 475| v7_8(void) = ExitFunction :
+# 475| v7_8(void) = AliasedUse : ~mu0_2
+# 475| v7_9(void) = ExitFunction :
# 479| Block 8
# 479| r8_0(glval) = VariableAddress[#temp479:11] :
@@ -2171,7 +2257,8 @@ ir.cpp:
# 484| v3_3(void) = NoOp :
# 482| v3_4(void) = ReturnVoid :
# 482| v3_5(void) = UnmodeledUse : mu*
-# 482| v3_6(void) = ExitFunction :
+# 482| v3_6(void) = AliasedUse : ~mu0_2
+# 482| v3_7(void) = ExitFunction :
# 486| void Conditional_LValue(bool)
# 486| Block 0
@@ -2192,24 +2279,25 @@ ir.cpp:
#-----| True -> Block 2
# 489| Block 1
-# 489| r1_0(glval) = VariableAddress[#temp489:6] :
-# 489| r1_1(glval) = Load : &:r1_0, ~mu0_2
-# 489| mu1_2(int) = Store : &:r1_1, r0_9
-# 490| v1_3(void) = NoOp :
-# 486| v1_4(void) = ReturnVoid :
-# 486| v1_5(void) = UnmodeledUse : mu*
-# 486| v1_6(void) = ExitFunction :
+# 489| r1_0(glval) = VariableAddress[#temp489:6] :
+# 489| r1_1(glval) = Load : &:r1_0, ~mu0_2
+# 489| mu1_2(int) = Store : &:r1_1, r0_9
+# 490| v1_3(void) = NoOp :
+# 486| v1_4(void) = ReturnVoid :
+# 486| v1_5(void) = UnmodeledUse : mu*
+# 486| v1_6(void) = AliasedUse : ~mu0_2
+# 486| v1_7(void) = ExitFunction :
# 489| Block 2
-# 489| r2_0(glval) = VariableAddress[x] :
-# 489| r2_1(glval) = VariableAddress[#temp489:6] :
-# 489| mu2_2(int) = Store : &:r2_1, r2_0
+# 489| r2_0(glval) = VariableAddress[x] :
+# 489| r2_1(glval) = VariableAddress[#temp489:6] :
+# 489| mu2_2(glval) = Store : &:r2_1, r2_0
#-----| Goto -> Block 1
# 489| Block 3
-# 489| r3_0(glval) = VariableAddress[y] :
-# 489| r3_1(glval) = VariableAddress[#temp489:6] :
-# 489| mu3_2(int) = Store : &:r3_1, r3_0
+# 489| r3_0(glval) = VariableAddress[y] :
+# 489| r3_1(glval) = VariableAddress[#temp489:6] :
+# 489| mu3_2(glval) = Store : &:r3_1, r3_0
#-----| Goto -> Block 1
# 492| void Conditional_Void(bool)
@@ -2229,7 +2317,8 @@ ir.cpp:
# 494| v1_0(void) = NoOp :
# 492| v1_1(void) = ReturnVoid :
# 492| v1_2(void) = UnmodeledUse : mu*
-# 492| v1_3(void) = ExitFunction :
+# 492| v1_3(void) = AliasedUse : ~mu0_2
+# 492| v1_4(void) = ExitFunction :
# 493| Block 2
# 493| r2_0(glval) = FunctionAddress[VoidFunc] :
@@ -2263,7 +2352,8 @@ ir.cpp:
# 501| v0_15(void) = NoOp :
# 496| v0_16(void) = ReturnVoid :
# 496| v0_17(void) = UnmodeledUse : mu*
-# 496| v0_18(void) = ExitFunction :
+# 496| v0_18(void) = AliasedUse : ~mu0_2
+# 496| v0_19(void) = ExitFunction :
# 503| void InitList(int, float)
# 503| Block 0
@@ -2311,7 +2401,8 @@ ir.cpp:
# 510| v0_41(void) = NoOp :
# 503| v0_42(void) = ReturnVoid :
# 503| v0_43(void) = UnmodeledUse : mu*
-# 503| v0_44(void) = ExitFunction :
+# 503| v0_44(void) = AliasedUse : ~mu0_2
+# 503| v0_45(void) = ExitFunction :
# 512| void NestedInitList(int, float)
# 512| Block 0
@@ -2388,7 +2479,8 @@ ir.cpp:
# 517| v0_70(void) = NoOp :
# 512| v0_71(void) = ReturnVoid :
# 512| v0_72(void) = UnmodeledUse : mu*
-# 512| v0_73(void) = ExitFunction :
+# 512| v0_73(void) = AliasedUse : ~mu0_2
+# 512| v0_74(void) = ExitFunction :
# 519| void ArrayInit(int, float)
# 519| Block 0
@@ -2436,7 +2528,8 @@ ir.cpp:
# 523| v0_41(void) = NoOp :
# 519| v0_42(void) = ReturnVoid :
# 519| v0_43(void) = UnmodeledUse : mu*
-# 519| v0_44(void) = ExitFunction :
+# 519| v0_44(void) = AliasedUse : ~mu0_2
+# 519| v0_45(void) = ExitFunction :
# 530| void UnionInit(int, float)
# 530| Block 0
@@ -2457,7 +2550,8 @@ ir.cpp:
# 533| v0_14(void) = NoOp :
# 530| v0_15(void) = ReturnVoid :
# 530| v0_16(void) = UnmodeledUse : mu*
-# 530| v0_17(void) = ExitFunction :
+# 530| v0_17(void) = AliasedUse : ~mu0_2
+# 530| v0_18(void) = ExitFunction :
# 535| void EarlyReturn(int, int)
# 535| Block 0
@@ -2480,7 +2574,8 @@ ir.cpp:
# 535| Block 1
# 535| v1_0(void) = ReturnVoid :
# 535| v1_1(void) = UnmodeledUse : mu*
-# 535| v1_2(void) = ExitFunction :
+# 535| v1_2(void) = AliasedUse : ~mu0_2
+# 535| v1_3(void) = ExitFunction :
# 537| Block 2
# 537| v2_0(void) = NoOp :
@@ -2516,7 +2611,8 @@ ir.cpp:
# 543| r1_0(glval) = VariableAddress[#return] :
# 543| v1_1(void) = ReturnValue : &:r1_0, ~mu0_2
# 543| v1_2(void) = UnmodeledUse : mu*
-# 543| v1_3(void) = ExitFunction :
+# 543| v1_3(void) = AliasedUse : ~mu0_2
+# 543| v1_4(void) = ExitFunction :
# 545| Block 2
# 545| r2_0(glval) = VariableAddress[#return] :
@@ -2552,7 +2648,8 @@ ir.cpp:
# 551| r0_12(glval) = VariableAddress[#return] :
# 551| v0_13(void) = ReturnValue : &:r0_12, ~mu0_2
# 551| v0_14(void) = UnmodeledUse : mu*
-# 551| v0_15(void) = ExitFunction :
+# 551| v0_15(void) = AliasedUse : ~mu0_2
+# 551| v0_16(void) = ExitFunction :
# 560| int EnumSwitch(E)
# 560| Block 0
@@ -2573,7 +2670,8 @@ ir.cpp:
# 560| r1_0(glval) = VariableAddress[#return] :
# 560| v1_1(void) = ReturnValue : &:r1_0, ~mu0_2
# 560| v1_2(void) = UnmodeledUse : mu*
-# 560| v1_3(void) = ExitFunction :
+# 560| v1_3(void) = AliasedUse : ~mu0_2
+# 560| v1_4(void) = ExitFunction :
# 564| Block 2
# 564| v2_0(void) = NoOp :
@@ -2602,63 +2700,60 @@ ir.cpp:
# 571| mu0_1(unknown) = AliasedDefinition :
# 571| mu0_2(unknown) = UnmodeledDefinition :
# 572| r0_3(glval) = VariableAddress[a_pad] :
-# 572| r0_4(glval) = StringConstant[""] :
-# 572| r0_5(char[1]) = Load : &:r0_4, ~mu0_2
-# 572| mu0_6(char[1]) = Store : &:r0_3, r0_5
-# 572| r0_7(unknown[31]) = Constant[0] :
-# 572| r0_8(int) = Constant[1] :
-# 572| r0_9(glval) = PointerAdd[1] : r0_3, r0_8
-# 572| mu0_10(unknown[31]) = Store : &:r0_9, r0_7
-# 573| r0_11(glval) = VariableAddress[a_nopad] :
-# 573| r0_12(glval) = StringConstant["foo"] :
-# 573| r0_13(char[4]) = Load : &:r0_12, ~mu0_2
-# 573| mu0_14(char[4]) = Store : &:r0_11, r0_13
-# 574| r0_15(glval) = VariableAddress[a_infer] :
-# 574| r0_16(glval) = StringConstant["blah"] :
-# 574| r0_17(char[5]) = Load : &:r0_16, ~mu0_2
-# 574| mu0_18(char[5]) = Store : &:r0_15, r0_17
-# 575| r0_19(glval) = VariableAddress[b] :
-# 575| mu0_20(char[2]) = Uninitialized[b] : &:r0_19
-# 576| r0_21(glval) = VariableAddress[c] :
-# 576| mu0_22(char[2]) = Uninitialized[c] : &:r0_21
-# 576| r0_23(int) = Constant[0] :
-# 576| r0_24(glval) = PointerAdd[1] : r0_21, r0_23
-# 576| r0_25(unknown[2]) = Constant[0] :
-# 576| mu0_26(unknown[2]) = Store : &:r0_24, r0_25
-# 577| r0_27(glval) = VariableAddress[d] :
-# 577| mu0_28(char[2]) = Uninitialized[d] : &:r0_27
-# 577| r0_29(int) = Constant[0] :
-# 577| r0_30(glval) = PointerAdd[1] : r0_27, r0_29
+# 572| r0_4(glval) = StringConstant[""] :
+# 572| r0_5(char[32]) = Load : &:r0_4, ~mu0_2
+# 572| mu0_6(char[32]) = Store : &:r0_3, r0_5
+# 573| r0_7(glval) = VariableAddress[a_nopad] :
+# 573| r0_8(glval) = StringConstant["foo"] :
+# 573| r0_9(char[4]) = Load : &:r0_8, ~mu0_2
+# 573| mu0_10(char[4]) = Store : &:r0_7, r0_9
+# 574| r0_11(glval) = VariableAddress[a_infer] :
+# 574| r0_12(glval) = StringConstant["blah"] :
+# 574| r0_13(char[5]) = Load : &:r0_12, ~mu0_2
+# 574| mu0_14(char[5]) = Store : &:r0_11, r0_13
+# 575| r0_15(glval) = VariableAddress[b] :
+# 575| mu0_16(char[2]) = Uninitialized[b] : &:r0_15
+# 576| r0_17(glval) = VariableAddress[c] :
+# 576| mu0_18(char[2]) = Uninitialized[c] : &:r0_17
+# 576| r0_19(int) = Constant[0] :
+# 576| r0_20(glval) = PointerAdd[1] : r0_17, r0_19
+# 576| r0_21(unknown[2]) = Constant[0] :
+# 576| mu0_22(unknown[2]) = Store : &:r0_20, r0_21
+# 577| r0_23(glval) = VariableAddress[d] :
+# 577| mu0_24(char[2]) = Uninitialized[d] : &:r0_23
+# 577| r0_25(int) = Constant[0] :
+# 577| r0_26(glval) = PointerAdd[1] : r0_23, r0_25
+# 577| r0_27(char) = Constant[0] :
+# 577| mu0_28(char) = Store : &:r0_26, r0_27
+# 577| r0_29(int) = Constant[1] :
+# 577| r0_30(glval) = PointerAdd[1] : r0_23, r0_29
# 577| r0_31(char) = Constant[0] :
# 577| mu0_32(char) = Store : &:r0_30, r0_31
-# 577| r0_33(int) = Constant[1] :
-# 577| r0_34(glval) = PointerAdd[1] : r0_27, r0_33
-# 577| r0_35(char) = Constant[0] :
-# 577| mu0_36(char) = Store : &:r0_34, r0_35
-# 578| r0_37(glval) = VariableAddress[e] :
-# 578| mu0_38(char[2]) = Uninitialized[e] : &:r0_37
-# 578| r0_39(int) = Constant[0] :
-# 578| r0_40(glval) = PointerAdd[1] : r0_37, r0_39
-# 578| r0_41(char) = Constant[0] :
+# 578| r0_33(glval) = VariableAddress[e] :
+# 578| mu0_34(char[2]) = Uninitialized[e] : &:r0_33
+# 578| r0_35(int) = Constant[0] :
+# 578| r0_36(glval) = PointerAdd[1] : r0_33, r0_35
+# 578| r0_37(char) = Constant[0] :
+# 578| mu0_38(char) = Store : &:r0_36, r0_37
+# 578| r0_39(int) = Constant[1] :
+# 578| r0_40(glval) = PointerAdd[1] : r0_33, r0_39
+# 578| r0_41(char) = Constant[1] :
# 578| mu0_42(char) = Store : &:r0_40, r0_41
-# 578| r0_43(int) = Constant[1] :
-# 578| r0_44(glval) = PointerAdd[1] : r0_37, r0_43
-# 578| r0_45(char) = Constant[1] :
-# 578| mu0_46(char) = Store : &:r0_44, r0_45
-# 579| r0_47(glval) = VariableAddress[f] :
-# 579| mu0_48(char[3]) = Uninitialized[f] : &:r0_47
-# 579| r0_49(int) = Constant[0] :
-# 579| r0_50(glval) = PointerAdd[1] : r0_47, r0_49
-# 579| r0_51(char) = Constant[0] :
-# 579| mu0_52(char) = Store : &:r0_50, r0_51
-# 579| r0_53(int) = Constant[1] :
-# 579| r0_54(glval) = PointerAdd[1] : r0_47, r0_53
-# 579| r0_55(unknown[2]) = Constant[0] :
-# 579| mu0_56(unknown[2]) = Store : &:r0_54, r0_55
-# 580| v0_57(void) = NoOp :
-# 571| v0_58(void) = ReturnVoid :
-# 571| v0_59(void) = UnmodeledUse : mu*
-# 571| v0_60(void) = ExitFunction :
+# 579| r0_43(glval) = VariableAddress[f] :
+# 579| mu0_44(char[3]) = Uninitialized[f] : &:r0_43
+# 579| r0_45(int) = Constant[0] :
+# 579| r0_46(glval) = PointerAdd[1] : r0_43, r0_45
+# 579| r0_47(char) = Constant[0] :
+# 579| mu0_48(char) = Store : &:r0_46, r0_47
+# 579| r0_49(int) = Constant[1] :
+# 579| r0_50(glval) = PointerAdd[1] : r0_43, r0_49
+# 579| r0_51(unknown[2]) = Constant[0] :
+# 579| mu0_52(unknown[2]) = Store : &:r0_50, r0_51
+# 580| v0_53(void) = NoOp :
+# 571| v0_54(void) = ReturnVoid :
+# 571| v0_55(void) = UnmodeledUse : mu*
+# 571| v0_56(void) = AliasedUse : ~mu0_2
+# 571| v0_57(void) = ExitFunction :
# 584| void VarArgs()
# 584| Block 0
@@ -2673,10 +2768,15 @@ ir.cpp:
# 585| r0_8(char *) = Convert : r0_7
# 585| v0_9(void) = Call : func:r0_3, 0:r0_5, 1:r0_6, 2:r0_8
# 585| mu0_10(unknown) = ^CallSideEffect : ~mu0_2
-# 586| v0_11(void) = NoOp :
-# 584| v0_12(void) = ReturnVoid :
-# 584| v0_13(void) = UnmodeledUse : mu*
-# 584| v0_14(void) = ExitFunction :
+# 585| v0_11(void) = ^BufferReadSideEffect[0] : &:r0_5, ~mu0_2
+# 585| v0_12(void) = ^BufferReadSideEffect[2] : &:r0_8, ~mu0_2
+# 585| mu0_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_5
+# 585| mu0_14(unknown) = ^BufferMayWriteSideEffect[2] : &:r0_8
+# 586| v0_15(void) = NoOp :
+# 584| v0_16(void) = ReturnVoid :
+# 584| v0_17(void) = UnmodeledUse : mu*
+# 584| v0_18(void) = AliasedUse : ~mu0_2
+# 584| v0_19(void) = ExitFunction :
# 590| void SetFuncPtr()
# 590| Block 0
@@ -2684,85 +2784,111 @@ ir.cpp:
# 590| mu0_1(unknown) = AliasedDefinition :
# 590| mu0_2(unknown) = UnmodeledDefinition :
# 591| r0_3(glval<..(*)(..)>) = VariableAddress[pfn] :
-# 591| r0_4(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] :
+# 591| r0_4(..(*)(..)) = FunctionAddress[FuncPtrTarget] :
# 591| mu0_5(..(*)(..)) = Store : &:r0_3, r0_4
# 592| r0_6(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] :
-# 592| r0_7(glval<..(*)(..)>) = VariableAddress[pfn] :
-# 592| mu0_8(..(*)(..)) = Store : &:r0_7, r0_6
-# 593| r0_9(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] :
-# 593| r0_10(glval<..(*)(..)>) = VariableAddress[pfn] :
-# 593| mu0_11(..(*)(..)) = Store : &:r0_10, r0_9
-# 594| r0_12(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] :
-# 594| r0_13(glval<..(*)(..)>) = VariableAddress[pfn] :
-# 594| mu0_14(..(*)(..)) = Store : &:r0_13, r0_12
-# 595| v0_15(void) = NoOp :
-# 590| v0_16(void) = ReturnVoid :
-# 590| v0_17(void) = UnmodeledUse : mu*
-# 590| v0_18(void) = ExitFunction :
+# 592| r0_7(..(*)(..)) = CopyValue : r0_6
+# 592| r0_8(glval<..(*)(..)>) = VariableAddress[pfn] :
+# 592| mu0_9(..(*)(..)) = Store : &:r0_8, r0_7
+# 593| r0_10(..(*)(..)) = FunctionAddress[FuncPtrTarget] :
+# 593| r0_11(..(*)(..)) = CopyValue : r0_10
+# 593| r0_12(glval<..(*)(..)>) = VariableAddress[pfn] :
+# 593| mu0_13(..(*)(..)) = Store : &:r0_12, r0_11
+# 594| r0_14(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] :
+# 594| r0_15(..(*)(..)) = CopyValue : r0_14
+# 594| r0_16(..(*)(..)) = CopyValue : r0_15
+# 594| r0_17(..(*)(..)) = CopyValue : r0_16
+# 594| r0_18(..(*)(..)) = CopyValue : r0_17
+# 594| r0_19(glval<..(*)(..)>) = VariableAddress[pfn] :
+# 594| mu0_20(..(*)(..)) = Store : &:r0_19, r0_18
+# 595| v0_21(void) = NoOp :
+# 590| v0_22(void) = ReturnVoid :
+# 590| v0_23(void) = UnmodeledUse : mu*
+# 590| v0_24(void) = AliasedUse : ~mu0_2
+# 590| v0_25(void) = ExitFunction :
# 615| void DeclareObject()
# 615| Block 0
-# 615| v0_0(void) = EnterFunction :
-# 615| mu0_1(unknown) = AliasedDefinition :
-# 615| mu0_2(unknown) = UnmodeledDefinition :
-# 616| r0_3(glval) = VariableAddress[s1] :
-# 616| r0_4(glval) = FunctionAddress[String] :
-# 616| v0_5(void) = Call : func:r0_4, this:r0_3
-# 616| mu0_6(unknown) = ^CallSideEffect : ~mu0_2
-# 617| r0_7(glval) = VariableAddress[s2] :
-# 617| r0_8(glval) = FunctionAddress[String] :
-# 617| r0_9(glval) = StringConstant["hello"] :
-# 617| r0_10(char *) = Convert : r0_9
-# 617| v0_11(void) = Call : func:r0_8, this:r0_7, 0:r0_10
-# 617| mu0_12(unknown) = ^CallSideEffect : ~mu0_2
-# 618| r0_13(glval) = VariableAddress[s3] :
-# 618| r0_14(glval) = FunctionAddress[ReturnObject] :
-# 618| r0_15(String) = Call : func:r0_14
-# 618| mu0_16(unknown) = ^CallSideEffect : ~mu0_2
-# 618| mu0_17(String) = Store : &:r0_13, r0_15
-# 619| r0_18(glval) = VariableAddress[s4] :
-# 619| r0_19(glval) = FunctionAddress[String] :
-# 619| r0_20(glval) = StringConstant["test"] :
-# 619| r0_21(char *) = Convert : r0_20
-# 619| v0_22(void) = Call : func:r0_19, this:r0_18, 0:r0_21
-# 619| mu0_23(unknown) = ^CallSideEffect : ~mu0_2
-# 620| v0_24(void) = NoOp :
-# 615| v0_25(void) = ReturnVoid :
-# 615| v0_26(void) = UnmodeledUse : mu*
-# 615| v0_27(void) = ExitFunction :
+# 615| v0_0(void) = EnterFunction :
+# 615| mu0_1(unknown) = AliasedDefinition :
+# 615| mu0_2(unknown) = UnmodeledDefinition :
+# 616| r0_3(glval) = VariableAddress[s1] :
+# 616| mu0_4(String) = Uninitialized[s1] : &:r0_3
+# 616| r0_5(glval) = FunctionAddress[String] :
+# 616| v0_6(void) = Call : func:r0_5, this:r0_3
+# 616| mu0_7(unknown) = ^CallSideEffect : ~mu0_2
+# 616| mu0_8(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_3
+# 617| r0_9(glval) = VariableAddress[s2] :
+# 617| mu0_10(String) = Uninitialized[s2] : &:r0_9
+# 617| r0_11(glval) = FunctionAddress[String] :
+# 617| r0_12(glval) = StringConstant["hello"] :
+# 617| r0_13(char *) = Convert : r0_12
+# 617| v0_14(void) = Call : func:r0_11, this:r0_9, 0:r0_13
+# 617| mu0_15(unknown) = ^CallSideEffect : ~mu0_2
+# 617| mu0_16(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_9
+# 617| v0_17(void) = ^BufferReadSideEffect[0] : &:r0_13, ~mu0_2
+# 617| mu0_18(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_13
+# 618| r0_19(glval) = VariableAddress[s3] :
+# 618| r0_20(glval) = FunctionAddress[ReturnObject] :
+# 618| r0_21(String) = Call : func:r0_20
+# 618| mu0_22(unknown) = ^CallSideEffect : ~mu0_2
+# 618| mu0_23(String) = Store : &:r0_19, r0_21
+# 619| r0_24(glval) = VariableAddress[s4] :
+# 619| mu0_25(String) = Uninitialized[s4] : &:r0_24
+# 619| r0_26(glval) = FunctionAddress[String] :
+# 619| r0_27(glval) = StringConstant["test"] :
+# 619| r0_28(char *) = Convert : r0_27
+# 619| v0_29(void) = Call : func:r0_26, this:r0_24, 0:r0_28
+# 619| mu0_30(unknown) = ^CallSideEffect : ~mu0_2
+# 619| mu0_31(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_24
+# 619| v0_32(void) = ^BufferReadSideEffect[0] : &:r0_28, ~mu0_2
+# 619| mu0_33(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_28
+# 620| v0_34(void) = NoOp :
+# 615| v0_35(void) = ReturnVoid :
+# 615| v0_36(void) = UnmodeledUse : mu*
+# 615| v0_37(void) = AliasedUse : ~mu0_2
+# 615| v0_38(void) = ExitFunction :
# 622| void CallMethods(String&, String*, String)
# 622| Block 0
-# 622| v0_0(void) = EnterFunction :
-# 622| mu0_1(unknown) = AliasedDefinition :
-# 622| mu0_2(unknown) = UnmodeledDefinition :
-# 622| r0_3(glval) = VariableAddress[r] :
-# 622| mu0_4(String &) = InitializeParameter[r] : &:r0_3
-# 622| r0_5(glval) = VariableAddress[p] :
-# 622| mu0_6(String *) = InitializeParameter[p] : &:r0_5
-# 622| r0_7(glval) = VariableAddress[s] :
-# 622| mu0_8(String) = InitializeParameter[s] : &:r0_7
-# 623| r0_9(glval) = VariableAddress[r] :
-# 623| r0_10(String &) = Load : &:r0_9, ~mu0_2
-# 623| r0_11(glval) = Convert : r0_10
-# 623| r0_12(glval) = FunctionAddress[c_str] :
-# 623| r0_13(char *) = Call : func:r0_12, this:r0_11
-# 623| mu0_14(unknown) = ^CallSideEffect : ~mu0_2
-# 624| r0_15(glval) = VariableAddress[p] :
-# 624| r0_16(String *) = Load : &:r0_15, ~mu0_2
-# 624| r0_17(String *) = Convert : r0_16
-# 624| r0_18(glval) = FunctionAddress[c_str] :
-# 624| r0_19(char *) = Call : func:r0_18, this:r0_17
-# 624| mu0_20(unknown) = ^CallSideEffect : ~mu0_2
-# 625| r0_21(glval) = VariableAddress[s] :
-# 625| r0_22(glval) = Convert : r0_21
-# 625| r0_23(glval) = FunctionAddress[c_str] :
-# 625| r0_24(char *) = Call : func:r0_23, this:r0_22
-# 625| mu0_25(unknown) = ^CallSideEffect : ~mu0_2
-# 626| v0_26(void) = NoOp :
-# 622| v0_27(void) = ReturnVoid :
-# 622| v0_28(void) = UnmodeledUse : mu*
-# 622| v0_29(void) = ExitFunction :
+# 622| v0_0(void) = EnterFunction :
+# 622| mu0_1(unknown) = AliasedDefinition :
+# 622| mu0_2(unknown) = UnmodeledDefinition :
+# 622| r0_3(glval) = VariableAddress[r] :
+# 622| mu0_4(String &) = InitializeParameter[r] : &:r0_3
+# 622| r0_5(glval) = VariableAddress[p] :
+# 622| mu0_6(String *) = InitializeParameter[p] : &:r0_5
+# 622| r0_7(glval) = VariableAddress[s] :
+# 622| mu0_8(String) = InitializeParameter[s] : &:r0_7
+# 623| r0_9(glval) = VariableAddress[r] :
+# 623| r0_10(String &) = Load : &:r0_9, ~mu0_2
+# 623| r0_11(glval) = CopyValue : r0_10
+# 623| r0_12(glval) = Convert : r0_11
+# 623| r0_13(glval) = FunctionAddress[c_str] :
+# 623| r0_14(char *) = Call : func:r0_13, this:r0_12
+# 623| mu0_15(unknown) = ^CallSideEffect : ~mu0_2
+# 623| v0_16(void) = ^BufferReadSideEffect[-1] : &:r0_12, ~mu0_2
+# 623| mu0_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_12
+# 624| r0_18(glval) = VariableAddress[p] :
+# 624| r0_19(String *) = Load : &:r0_18, ~mu0_2
+# 624| r0_20(String *) = Convert : r0_19
+# 624| r0_21(glval) = FunctionAddress[c_str] :
+# 624| r0_22(char *) = Call : func:r0_21, this:r0_20
+# 624| mu0_23(unknown) = ^CallSideEffect : ~mu0_2
+# 624| v0_24(void) = ^BufferReadSideEffect[-1] : &:r0_20, ~mu0_2
+# 624| mu0_25(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_20
+# 625| r0_26(glval) = VariableAddress[s] :
+# 625| r0_27(glval) = Convert : r0_26
+# 625| r0_28(glval) = FunctionAddress[c_str] :
+# 625| r0_29(char *) = Call : func:r0_28, this:r0_27
+# 625| mu0_30(unknown) = ^CallSideEffect : ~mu0_2
+# 625| v0_31(void) = ^BufferReadSideEffect[-1] : &:r0_27, ~mu0_2
+# 625| mu0_32(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_27
+# 626| v0_33(void) = NoOp :
+# 622| v0_34(void) = ReturnVoid :
+# 622| v0_35(void) = UnmodeledUse : mu*
+# 622| v0_36(void) = AliasedUse : ~mu0_2
+# 622| v0_37(void) = ExitFunction :
# 630| int C::StaticMemberFunction(int)
# 630| Block 0
@@ -2778,7 +2904,8 @@ ir.cpp:
# 630| r0_9(glval) = VariableAddress[#return] :
# 630| v0_10(void) = ReturnValue : &:r0_9, ~mu0_2
# 630| v0_11(void) = UnmodeledUse : mu*
-# 630| v0_12(void) = ExitFunction :
+# 630| v0_12(void) = AliasedUse : ~mu0_2
+# 630| v0_13(void) = ExitFunction :
# 634| int C::InstanceMemberFunction(int)
# 634| Block 0
@@ -2795,7 +2922,8 @@ ir.cpp:
# 634| r0_10(glval) = VariableAddress[#return] :
# 634| v0_11(void) = ReturnValue : &:r0_10, ~mu0_2
# 634| v0_12(void) = UnmodeledUse : mu*
-# 634| v0_13(void) = ExitFunction :
+# 634| v0_13(void) = AliasedUse : ~mu0_2
+# 634| v0_14(void) = ExitFunction :
# 638| int C::VirtualMemberFunction(int)
# 638| Block 0
@@ -2812,7 +2940,8 @@ ir.cpp:
# 638| r0_10(glval) = VariableAddress[#return] :
# 638| v0_11(void) = ReturnValue : &:r0_10, ~mu0_2
# 638| v0_12(void) = UnmodeledUse : mu*
-# 638| v0_13(void) = ExitFunction :
+# 638| v0_13(void) = AliasedUse : ~mu0_2
+# 638| v0_14(void) = ExitFunction :
# 642| void C::FieldAccess()
# 642| Block 0
@@ -2826,33 +2955,36 @@ ir.cpp:
# 643| mu0_7(int) = Store : &:r0_6, r0_4
# 644| r0_8(int) = Constant[1] :
# 644| r0_9(C *) = CopyValue : r0_3
-# 644| r0_10(glval) = FieldAddress[m_a] : r0_9
-# 644| mu0_11(int) = Store : &:r0_10, r0_8
-# 645| r0_12(int) = Constant[2] :
-#-----| r0_13(C *) = CopyValue : r0_3
-# 645| r0_14(glval) = FieldAddress[m_a] : r0_13
-# 645| mu0_15(int) = Store : &:r0_14, r0_12
-# 646| r0_16(glval) = VariableAddress[x] :
-# 646| mu0_17(int) = Uninitialized[x] : &:r0_16
-# 647| r0_18(C *) = CopyValue : r0_3
-# 647| r0_19(glval) = FieldAddress[m_a] : r0_18
-# 647| r0_20(int) = Load : &:r0_19, ~mu0_2
-# 647| r0_21(glval) = VariableAddress[x] :
-# 647| mu0_22(int) = Store : &:r0_21, r0_20
-# 648| r0_23(C *) = CopyValue : r0_3
-# 648| r0_24(glval) = FieldAddress[m_a] : r0_23
-# 648| r0_25(int) = Load : &:r0_24, ~mu0_2
-# 648| r0_26(glval) = VariableAddress[x] :
-# 648| mu0_27(int) = Store : &:r0_26, r0_25
-#-----| r0_28(C *) = CopyValue : r0_3
-# 649| r0_29(glval) = FieldAddress[m_a] : r0_28
-# 649| r0_30(int) = Load : &:r0_29, ~mu0_2
-# 649| r0_31(glval) = VariableAddress[x] :
-# 649| mu0_32(int) = Store : &:r0_31, r0_30
-# 650| v0_33(void) = NoOp :
-# 642| v0_34(void) = ReturnVoid :
-# 642| v0_35(void) = UnmodeledUse : mu*
-# 642| v0_36(void) = ExitFunction :
+# 644| r0_10(glval) = CopyValue : r0_9
+# 644| r0_11(glval) = FieldAddress[m_a] : r0_10
+# 644| mu0_12(int) = Store : &:r0_11, r0_8
+# 645| r0_13(int) = Constant[2] :
+#-----| r0_14(C *) = CopyValue : r0_3
+# 645| r0_15(glval) = FieldAddress[m_a] : r0_14
+# 645| mu0_16(int) = Store : &:r0_15, r0_13
+# 646| r0_17(glval) = VariableAddress[x] :
+# 646| mu0_18(int) = Uninitialized[x] : &:r0_17
+# 647| r0_19(C *) = CopyValue : r0_3
+# 647| r0_20(glval) = FieldAddress[m_a] : r0_19
+# 647| r0_21(int) = Load : &:r0_20, ~mu0_2
+# 647| r0_22(glval) = VariableAddress[x] :
+# 647| mu0_23(int) = Store : &:r0_22, r0_21
+# 648| r0_24(C *) = CopyValue : r0_3
+# 648| r0_25(glval) = CopyValue : r0_24
+# 648| r0_26(glval) = FieldAddress[m_a] : r0_25
+# 648| r0_27(int) = Load : &:r0_26, ~mu0_2
+# 648| r0_28(glval) = VariableAddress[x] :
+# 648| mu0_29(int) = Store : &:r0_28, r0_27
+#-----| r0_30(C *) = CopyValue : r0_3
+# 649| r0_31(glval